tinyhmacmd5 0.0.7 → 0.1.1

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.
package/README.md CHANGED
@@ -2,10 +2,14 @@
2
2
 
3
3
  A tiny JavaScript HMAC-MD5 implementation.
4
4
 
5
- The minified browser bundle ([`browser.min.js`](browser.min.js)) is only 965 bytes.
5
+ The minified browser bundle ([`browser.min.js`](browser.min.js)) is only 1057 bytes.
6
6
 
7
7
  Preview: https://bddjr.github.io/tinyhmacmd5/
8
8
 
9
+ > [!WARNING]
10
+ > MD5 is cryptographically broken and unsafe for security-sensitive applications.
11
+ > Do not rely on it for password hashing, digital signatures, or certificate verification.
12
+
9
13
  ## Setup
10
14
 
11
15
  ### npm
@@ -30,6 +34,10 @@ See https://www.jsdelivr.com/package/npm/tinyhmacmd5
30
34
 
31
35
  ## Example
32
36
 
37
+ > [!WARNING]
38
+ > Please make sure on your own that the types of input parameters conform to the definitions in [`main.d.ts`](main.d.ts).
39
+ > If you pass wrong types, it may not throw an error and may produce incorrect results.
40
+
33
41
  HMAC-MD5:
34
42
 
35
43
  ```js
package/browser.min.js CHANGED
@@ -1,2 +1,2 @@
1
1
  /** @license https://bddjr.github.io/tinyhmacmd5/lic */
2
- {let r=1732584193,e=271733878,t=~r,n=~e,o=[],f=(f,a,h=0,i,l,u,d,c,g=[r,n,t,e],s=[...g],y=r=>g[3&++c])=>{for(f[a+64>>>9<<4|14]=0|a,f[a>>>5]|=128<<a%32;h<f.length;h+=16){for(c=i=0;i<64;c-=6)g[3&c]=0|((l=0|(l=y(),u=y(),d=y(),((a=i>>4<<2)?a>4?a>8?u^(l|~d):l^u^d:l&d|u&~d:l&u|~l&d)+(0|f[h+(i*(29521>>a)+(1296>>a)&15)])+(u="',16%).4$+07&*/5".charCodeAt(a+i%4)-32,o[63-i++]??=0|2**32*Math.abs(Math.sin(i)))+y()))<<u|l>>>32-u)+y();for(c=4;c;)s[--c]=g[c]=0|s[c]+g[c]}return g},a=(r,e,t=("string"==typeof r?r=(new TextEncoder).encode(r):r).length,n=[],o=8*t)=>{for(;t;)n[e+(--t>>>2)]|=r[t]<<t%4*8;return[n,o]};var md5=(r,e,t)=>{var n=16,o=null!=e,[h,i]=a(r,o*n),l=new Uint8Array(n);if(o){let[r,t]=a(e,0),o=e=>{for(;n;)h[--n]=16843009*e^r[n];n=16};t>512&&(r=f(r,t)),o(54),(h=f(h,512+i)).unshift(...Array(16)),o(92),i=640}for(h=f(h,i);n;)l[--n]=h[n>>2]>>>n%4*8;return t?l:l.reduce((r,e)=>r+(e>>4&&"")+e.toString(16),"")}}
2
+ {let r=1732584193,e=271733878,n=~r,t=~e,o=2**32,f=Math,a=f.floor,l=r=>16*a((r+8)/64)+16,i=[],u=(u,d,h=0,c=l(d),g,s,w,y,A=[r,t,n,e],v=[...A])=>{for(u[c-1]=0|8*d/o,u[c-2]=0|8*d,u[a(d/4)]|=128<<d%4*8;h<u.length;h+=16){for(y=c=0;c<64;)A[3&y]=0|((g=0|(g=A[3&++y],s=A[3&++y],w=A[3&++y],((d=c>>4<<2)?d>4?d>8?s^(g|~w):g^s^w:g&w|s&~w:g&s|~g&w)+(0|u[h+(c*(29521>>d)+(1296>>d)&15)])+(s=31&"',16%).4$+07&*/5".charCodeAt(d+c%4),i[63-c++]??=0|o*f.abs(f.sin(c)))+A[y+1&3]))<<s|g>>>32-s)+A[y+2&3];for(y=4;y;)v[--y]=A[y]=0|v[y]+A[y]}return A},d=(r,e,n=("string"==typeof r?r=(new TextEncoder).encode(r):r).length,t=new Int32Array(l(4*e+n)),o=0)=>{for(;o<n;)t[e++]=r[o++]|r[o++]<<8|r[o++]<<16|r[o++]<<24;return[t,n]};var md5=(r,e,n)=>{var t=16,o=null!=e,[f,a]=d(r,o*t),l=new Uint8Array(t);if(o){let[r,n]=d(e,0),o=[];for(n>64&&(r=u(r,n));t;)f[--t]=909522486^r[t],o[t]=1549556828^r[t];t=16,(f=u(f,64+a)).unshift(...o),a=80}for(f=u(f,a);t;)l[--t]=f[t>>2]>>>t%4*8;return n?l:l.reduce((r,e)=>r+(e>>4&&"")+e.toString(16),"")}}
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
@@ -7,14 +7,23 @@ let A = 1732584193
7
7
  , C = ~A
8
8
  , B = ~D
9
9
 
10
+ let $2_32 = 2 ** 32
11
+
12
+ let $Math = Math
13
+
14
+ let floor = $Math.floor
15
+
16
+ /** @param {number} byteLen */
17
+ let toBinlLen = (byteLen) => floor((byteLen + 8) / 64) * 16 + 16
18
+
10
19
  /** @type {number[]} MD5 constants cached in memory */
11
20
  let K = []
12
21
 
13
22
  /**
14
23
  * Calculate the MD5 of an array of little-endian words, and a bit length.
15
24
  *
16
- * @param {number[]} x Array of little-endian words
17
- * @param {number} l Bit length
25
+ * @param {Int32Array | number[]} x Array of little-endian words
26
+ * @param {number} l Byte length
18
27
  *
19
28
  * @param {number} [j]
20
29
  * @param {number} [t0]
@@ -29,31 +38,29 @@ let binlMD5 = (
29
38
  l,
30
39
  // var:
31
40
  i = 0,
32
- j,
41
+ j = toBinlLen(l),
33
42
  t0,
34
43
  t1,
35
44
  t2,
36
45
  oi,
37
46
  output = [A, B, C, D],
38
47
  oldOutput = [...output],
39
- o = (
40
- /** @param {*} [_] */
41
- _ => output[++oi & 3]
42
- ),
43
48
  ) => {
49
+ // console.time("binlMD5")
44
50
 
45
51
  // append padding
46
- x[l + 64 >>> 9 << 4 | 14] = 0 | l;
47
- x[l >>> 5] |= 0x80 << l % 32;
52
+ x[j - 1] = 0 | l * 8 / $2_32;
53
+ x[j - 2] = 0 | l * 8;
54
+ x[floor(l / 4)] |= 0x80 << l % 4 * 8;
48
55
 
49
56
  for (; i < x.length; i += 16) {
50
- for (oi = j = 0; j < 64; oi -= 6) {
57
+ for (oi = j = 0; j < 64;) {
51
58
  output[oi & 3] = (
52
59
  t0 = 0 | (
53
60
  (
54
- t0 = o(),
55
- t1 = o(),
56
- t2 = o(),
61
+ t0 = output[++oi & 3],
62
+ t1 = output[++oi & 3],
63
+ t2 = output[++oi & 3],
57
64
  (l = j >> 4 << 2)
58
65
  ? l > 4
59
66
  ? l > 8
@@ -66,60 +73,67 @@ let binlMD5 = (
66
73
  0 | x[i + (j * (0x7351 >> l) + (0x0510 >> l) & 15)]
67
74
  ) +
68
75
  (
69
- t1 = "',16%).4$+07&*/5".charCodeAt(l + j % 4) - 32,
70
- K[63 - j++] ??= 0 | 2 ** 32 * Math.abs(Math.sin(j))
76
+ t1 = "',16%).4$+07&*/5".charCodeAt(l + j % 4) & 31,
77
+ K[63 - j++] ??= 0 | $2_32 * $Math.abs($Math.sin(j))
71
78
  ) +
72
- o()
79
+ output[oi + 1 & 3]
73
80
  ),
74
- 0 | (t0 << t1 | t0 >>> 32 - t1) + o()
81
+ 0 | (t0 << t1 | t0 >>> 32 - t1) + output[oi + 2 & 3]
75
82
  )
76
83
  }
77
84
  for (oi = 4; oi;) {
78
85
  oldOutput[--oi] = output[oi] = 0 | oldOutput[oi] + output[oi]
79
86
  }
80
87
  }
88
+ // console.timeEnd("binlMD5")
81
89
  return output
82
90
  }
83
91
 
84
92
  /**
85
93
  * Convert bytes to an array of little-endian words
86
94
  *
87
- * @param {string | Uint8Array} input
88
- * @param {number} padLen
95
+ * @param {*} input
96
+ * @param {number} j pad int32 length
89
97
  *
90
- * @param {number[]} [output]
91
- *
92
- * @returns {[number[], number]}
98
+ * @returns {[Int32Array<ArrayBuffer> | number[], number]}
93
99
  */
94
100
  let inputToBinl = (
95
101
  input,
96
- padLen,
102
+ j,
97
103
  // var:
98
- i = (
104
+ byteLen = (
99
105
  typeof input == 'string'
100
106
  ? input = new TextEncoder().encode(input)
101
107
  : input
102
108
  ).length,
103
- output = [],
104
- bitLen = 8 * i,
109
+ // Using Array would fail to handle large files, so Int32Array must be used.
110
+ output = new Int32Array(toBinlLen(j * 4 + byteLen)),
111
+ i = 0,
105
112
  ) => {
106
- for (; i;) {
107
- output[padLen + (--i >>> 2)] |= /**@type {Uint8Array}*/(input)[i] << i % 4 * 8
113
+ // console.time("inputToBinl")
114
+ for (; i < byteLen;) {
115
+ output[j++] = (
116
+ input[i++] |
117
+ input[i++] << 8 |
118
+ input[i++] << 16 |
119
+ input[i++] << 24
120
+ )
108
121
  }
109
- return [output, bitLen]
122
+ // console.timeEnd("inputToBinl")
123
+ return [output, byteLen]
110
124
  }
111
125
 
112
126
  /**
113
127
  * @overload
114
- * @param {string | Uint8Array} data
115
- * @param {string | Uint8Array | null} [key]
128
+ * @param {string | Uint8Array | Uint8ClampedArray} data
129
+ * @param {string | Uint8Array | Uint8ClampedArray | null} [key]
116
130
  * @param {false} [raw]
117
131
  * @returns {string}
118
132
  */
119
133
  /**
120
134
  * @overload
121
- * @param {string | Uint8Array} data
122
- * @param {string | Uint8Array | null | undefined} key
135
+ * @param {string | Uint8Array | Uint8ClampedArray} data
136
+ * @param {string | Uint8Array | Uint8ClampedArray | null | undefined} key
123
137
  * @param {true} raw
124
138
  * @returns {Uint8Array<ArrayBuffer>}
125
139
  */
@@ -129,37 +143,35 @@ let inputToBinl = (
129
143
  * By default, returns the hash as a lowercase hexadecimal string.
130
144
  * If `raw` is true, returns a Uint8Array.
131
145
  *
132
- * @param {string | Uint8Array} data The input data to hash. Strings are UTF‑8 encoded.
133
- * @param {string | Uint8Array | null} [key] Optional HMAC key. When given, HMAC‑MD5 is calculated instead of plain MD5.
146
+ * @param {string | Uint8Array | Uint8ClampedArray} data The input data to hash. Strings are UTF‑8 encoded.
147
+ * @param {string | Uint8Array | Uint8ClampedArray | null} [key] Optional HMAC key. When given, HMAC‑MD5 is calculated instead of plain MD5.
134
148
  * @param {boolean} [raw] If true, the hash is returned as raw bytes (Uint8Array); otherwise, as a hex string.
135
149
  * @returns {string | Uint8Array<ArrayBuffer>} The MD5 (or HMAC‑MD5) digest, either as a hex string or a Uint8Array.
136
150
  */
137
151
  var md5 = (data, key, raw) => {
138
152
  var i = 16
139
153
  , hasKey = key != null
140
- , [bdata, bitLen] = inputToBinl(data, /**@type {*}*/(hasKey) * i)
154
+ , [bdata, dataByteLen] = inputToBinl(data, /**@type {*}*/(hasKey) * i)
141
155
  , out = new Uint8Array(i)
142
156
 
143
157
  if (hasKey) {
144
158
  // HMAC
145
- let [bkey, keyBitlen] = inputToBinl(key, 0)
146
- , pad = (/**@type {number}*/ x) => {
147
- for (; i;) {
148
- bdata[--i] = 0x01010101 * x ^ bkey[i]
149
- }
150
- i = 16
151
- }
152
- if (keyBitlen > 512) {
153
- bkey = binlMD5(bkey, keyBitlen)
159
+ let [bkey, keyByteLen] = inputToBinl(key, 0)
160
+ let opad = []
161
+ if (keyByteLen > 64) {
162
+ bkey = binlMD5(bkey, keyByteLen)
163
+ }
164
+ for (; i;) {
165
+ bdata[--i] = 0x36363636 ^ bkey[i] // ipad
166
+ opad[i] = 0x5c5c5c5c ^ bkey[i]
154
167
  }
155
- pad(0x36)
156
- bdata = binlMD5(bdata, 512 + bitLen)
157
- bdata.unshift(...Array(16))
158
- pad(0x5c)
159
- bitLen = 640
168
+ i = 16
169
+ bdata = binlMD5(bdata, 64 + dataByteLen)
170
+ bdata.unshift(...opad)
171
+ dataByteLen = 80
160
172
  }
161
173
 
162
- bdata = binlMD5(bdata, bitLen)
174
+ bdata = binlMD5(bdata, dataByteLen)
163
175
 
164
176
  // binl to bytes
165
177
  for (; i;) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "tinyhmacmd5",
3
- "version": "0.0.7",
4
- "description": "A tiny JavaScript HMAC-MD5 implementation. The minified browser bundle is only 965 bytes.",
3
+ "version": "0.1.1",
4
+ "description": "A tiny JavaScript HMAC-MD5 implementation. The minified browser bundle is only 1057 bytes.",
5
5
  "author": "bddjr",
6
6
  "license": "MIT",
7
7
  "type": "module",