feat: Replace aCAPTCHA with official ALTCHA (altcha.org) Proof-of-Work web component widget
This commit is contained in:
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"editor.formatOnSave": true
|
||||
}
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
## 4.12.0 (November 19, 2024)
|
||||
|
||||
- **BREAKING CHANGE**: crc32c() and createCRC32C() were removed. You can now do the same thing with crc32() and createCRC32() through setting the polynomial parameter to 0x82F63B78
|
||||
- Added CRC-64 hash function
|
||||
- Exported IDataType, IHasher types from index.ts
|
||||
- Update benchmarks
|
||||
- Update dependencies, including Clang
|
||||
- Migrate from ESLint to Biome and fix linting errors
|
||||
|
||||
## 4.11.0 (November 13, 2023)
|
||||
|
||||
- Added `secret` parameter to Argon2 (by [@malobre](https://github.com/malobre))
|
||||
|
||||
## 4.10.0 (October 15, 2023)
|
||||
|
||||
- Add sideEffects: false to package.json
|
||||
- Update dependencies, including Clang
|
||||
|
||||
## 4.9.0 (July 22, 2021)
|
||||
|
||||
- Add CRC32C
|
||||
- Update dependencies, including Clang
|
||||
|
||||
## 4.8.0 (June 27, 2021)
|
||||
|
||||
- Optimized Adler-32 for WASM
|
||||
- Add xxhash3() and xxhash128() hash functions
|
||||
- Slightly faster xxhash32() and xxhash64()
|
||||
|
||||
## 4.7.0 (June 5, 2021)
|
||||
|
||||
- Add Adler-32 algorithm
|
||||
- New feature: saving and loading the internal state of the hash (segmented hashing and rewinding)
|
||||
- Faster builds using GNU Make
|
||||
- Fix alignment of the main buffer (might result in better performance on some platforms)
|
||||
- Updated dependencies
|
||||
- _(Special thanks to Nicholas Sherlock for his contributions)_
|
||||
|
||||
## 4.6.0 (March 26, 2021)
|
||||
|
||||
- Smaller CRC32 binary (now it computes the lookup table at the first run)
|
||||
- Less RAM required for the WASM instances: it only allocates 128kB of RAM instead of 256kB
|
||||
- Update dependencies
|
||||
|
||||
## 4.5.0 (March 6, 2021)
|
||||
|
||||
- Add BLAKE3 algorithm
|
||||
- Performance improvements at BLAKE2
|
||||
- Update dependencies
|
||||
- Update benchmarks
|
||||
|
||||
## 4.4.1 (November 22, 2020)
|
||||
|
||||
- Add JSDoc annotations
|
||||
|
||||
## 4.4.0 (November 8, 2020)
|
||||
|
||||
- Add Whirlpool and SM3 algorithms
|
||||
- Fix block size at blake algorithms
|
||||
- Switch compiler from Emscripten to Clang
|
||||
- Fix cached seed invalidation at xxhash64
|
||||
- Added wasm binary hashes to build log
|
||||
- Create minified bundles from each algorithm
|
||||
|
||||
## 4.3.0 (October 10, 2020)
|
||||
|
||||
- Add blake2s algorithm
|
||||
- Add bundle sizes to README
|
||||
|
||||
## 4.2.2 (September 19, 2020)
|
||||
|
||||
- Add bcrypt algorithm
|
||||
- Add argon2Validate() function
|
||||
|
||||
## 4.1.0 (September 5, 2020)
|
||||
|
||||
- Add scrypt algorithm
|
||||
- Improve type signiture of key derivation functions. Now the return type depends on the parameters
|
||||
- Update benchmarks, add relative performance
|
||||
- Add section to README about string encoding pitfalls
|
||||
|
||||
## 4.0.1 (August 29, 2020)
|
||||
|
||||
- ~~**BREAKING CHANGE**: All APIs were converted to **synchronous** functions, so you don't need to use `await` or `.then()` anymore.~~ It turned out that Chrome does not allow synchronous WebAssembly instantiation of binaries larger than 4KB. So this change was reverted.
|
||||
- **BREAKING CHANGE**: `argon2()` function was renamed to `argon2i()`, `argon2d()` and `argon2id()`.
|
||||
- **BREAKING CHANGE**: `pbkdf2()` function requires now an object containing all parameters
|
||||
- Update dependencies
|
||||
|
||||
## 3.7.2 (August 15, 2020)
|
||||
|
||||
- Add more badges to README
|
||||
- Update dependencies
|
||||
- Increase code coverage
|
||||
- Consistent coding style in C files
|
||||
|
||||
## 3.7.1 (August 2, 2020)
|
||||
|
||||
- Allow returning Uint8Array from digest()
|
||||
- Allow operation chaining in createXXXX() response
|
||||
- Throw an error when an IHasher instance is reused without init() call.
|
||||
- Add "binary" and "encoded" output types to argon2
|
||||
- Update benchmark results
|
||||
- Remove all dependencies on Buffer object in standalone mode
|
||||
|
||||
## 3.7.0 (August 1, 2020)
|
||||
|
||||
- Added Argon2
|
||||
- Support hash lengths shorter than 16 bytes in Blake2b
|
||||
|
||||
## 3.6.0 (July 25, 2020)
|
||||
|
||||
- **Breaking change**: Drop support for String() objects (normal string literals are still supported of course)
|
||||
- Node.js buffer polyfills are not needed anymore
|
||||
|
||||
## 3.5.0 (July 19, 2020)
|
||||
|
||||
- Added BLAKE2b algorithm
|
||||
- Fixed a bug, where sha3() and keccak() gave incorrect results when the hash length was changed
|
||||
|
||||
## 3.4.1 (June 27, 2020)
|
||||
|
||||
- Add PBKDF2 example and benchmark results
|
||||
|
||||
## 3.4.0 (June 27, 2020)
|
||||
|
||||
- Add PBKDF2 algorithm
|
||||
|
||||
## 3.3.0 (June 22, 2020)
|
||||
|
||||
- Add RIPEMD-160 algorithm
|
||||
|
||||
## 3.2.1 (June 5, 2020)
|
||||
|
||||
- Fix buffer.copy() error in browsers
|
||||
|
||||
## 3.2.0 (June 5, 2020)
|
||||
|
||||
- Added HMAC
|
||||
- Better examples in readme
|
||||
- Updated dependencies
|
||||
|
||||
## 3.1.2 (May 27, 2020)
|
||||
|
||||
- Fixed example code in readme
|
||||
- Better description and keywords
|
||||
|
||||
## 3.1.1 (May 25, 2020)
|
||||
|
||||
- ~3x faster when hashing small chunks of data (one WASM function call instead of three)
|
||||
- Updated benchmark results in readme
|
||||
|
||||
## 3.0.1 (May 24, 2020)
|
||||
|
||||
- Updated benchmark results in readme
|
||||
- Use new web-based tool for benchmarking
|
||||
|
||||
## 3.0.0 (May 24, 2020)
|
||||
|
||||
- Improved performance in smaller input buffers
|
||||
|
||||
## 2.6.0 (April 18, 2020)
|
||||
|
||||
- Allow concurrent hash calculations with multiple states
|
||||
- Fix WebAssembly detection
|
||||
- Cache WebAssembly modules
|
||||
- Fix crash when starting many parallel hash calculations
|
||||
- Allocate less memory for WASM modules
|
||||
|
||||
## 2.5.1 (April 13, 2020)
|
||||
|
||||
- Add React.js demo app
|
||||
|
||||
## 2.5.0 (April 13, 2020)
|
||||
|
||||
- Add xxhash32 and xxhash64
|
||||
- Add Keccak
|
||||
- Bugfixes
|
||||
|
||||
## 2.1.0 (April 12, 2020)
|
||||
|
||||
- Use faster SHA256 and SHA512 implementations
|
||||
- Allocate less memory in WASM modules
|
||||
- Clean up C source codes
|
||||
|
||||
## 2.0.0 (April 11, 2020)
|
||||
|
||||
- Initial release
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Dani Biró
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
Embedded C implementations might use other, similarly permissive licenses.
|
||||
Check the beginning of the files from the /src directory.
|
||||
|
||||
Special thank you to the authors of original C algorithms:
|
||||
- Alexander Peslyak <solar@openwall.com>
|
||||
- Aleksey Kravchenko <rhash.admin@gmail.com>
|
||||
- Colin Percival
|
||||
- Stephan Brumme <create@stephan-brumme.com>
|
||||
- Steve Reid <steve@edmweb.com>
|
||||
- Samuel Neves <sneves@dei.uc.pt>
|
||||
- Solar Designer <solar@openwall.com>
|
||||
- Project Nayuki
|
||||
- ARM Limited
|
||||
- Yanbo Li dreamfly281@gmail.com, goldboar@163.comYanbo Li
|
||||
- Mark Adler
|
||||
- Yann Collet
|
||||
+543
@@ -0,0 +1,543 @@
|
||||
# hash-wasm
|
||||
|
||||
[](http://npmjs.org/package/hash-wasm)
|
||||
[](https://bundlephobia.com/result?p=hash-wasm)
|
||||
[](https://codecov.io/gh/Daninet/hash-wasm)
|
||||
[](https://github.com/Daninet/hash-wasm/actions)
|
||||
[](https://www.jsdelivr.com/package/npm/hash-wasm)
|
||||
|
||||
Hash-WASM is a ⚡lightning fast⚡ hash function library for browsers and Node.js.
|
||||
It is using hand-tuned WebAssembly binaries to calculate the hash faster than other libraries.
|
||||
|
||||
# Supported algorithms
|
||||
|
||||
| Name | Bundle size (gzipped) |
|
||||
| ---------------------------------------------- | --------------------- |
|
||||
| Adler-32 | 3 kB |
|
||||
| Argon2: Argon2d, Argon2i, Argon2id (v1.3) | 11 kB |
|
||||
| bcrypt | 11 kB |
|
||||
| BLAKE2b | 6 kB |
|
||||
| BLAKE2s | 5 kB |
|
||||
| BLAKE3 | 9 kB |
|
||||
| CRC32 | 3 kB |
|
||||
| CRC64 | 4 kB |
|
||||
| HMAC | - |
|
||||
| MD4 | 4 kB |
|
||||
| MD5 | 4 kB |
|
||||
| PBKDF2 | - |
|
||||
| RIPEMD-160 | 5 kB |
|
||||
| scrypt | 10 kB |
|
||||
| SHA-1 | 5 kB |
|
||||
| SHA-2: SHA-224, SHA-256 | 7 kB |
|
||||
| SHA-2: SHA-384, SHA-512 | 8 kB |
|
||||
| SHA-3: SHA3-224, SHA3-256, SHA3-384, SHA3-512 | 4 kB |
|
||||
| Keccak-224, Keccak-256, Keccak-384, Keccak-512 | 4 kB |
|
||||
| SM3 | 4 kB |
|
||||
| Whirlpool | 6 kB |
|
||||
| xxHash32 | 3 kB |
|
||||
| xxHash64 | 4 kB |
|
||||
| xxHash3 | 7 kB |
|
||||
| xxHash128 | 8 kB |
|
||||
|
||||
# Features
|
||||
|
||||
- A lot faster than other JS / WASM implementations (see [benchmarks](#benchmark) below)
|
||||
- It's lightweight. See the table above
|
||||
- Compiled from heavily optimized algorithms written in C
|
||||
- Supports all modern browsers, Node.js and Deno
|
||||
- Supports large data streams
|
||||
- Supports UTF-8 strings and typed arrays
|
||||
- Supports chunked input streams
|
||||
- Modular architecture (the algorithms are compiled into individual WASM binaries)
|
||||
- WASM modules are bundled as base64 strings (no problems with linking)
|
||||
- Supports tree shaking (Webpack only bundles the hash algorithms you use)
|
||||
- Works without Webpack or other bundlers
|
||||
- Includes TypeScript type definitions
|
||||
- Works in Web Workers
|
||||
- Zero dependencies
|
||||
- Supports concurrent hash calculations with multiple states
|
||||
- Supports saving and loading the internal state of the hash (segmented hashing and rewinding)
|
||||
- [Unit tests](https://github.com/Daninet/hash-wasm/tree/master/test) for all algorithms
|
||||
- 100% open source & transparent [build process](https://github.com/Daninet/hash-wasm/actions)
|
||||
- Easy to use, Promise-based API
|
||||
|
||||
# Installation
|
||||
|
||||
```
|
||||
npm i hash-wasm
|
||||
```
|
||||
|
||||
It can also be used directly from HTML (via [jsDelivr](https://www.jsdelivr.com/package/npm/hash-wasm)):
|
||||
|
||||
```html
|
||||
<!-- load all algortihms into the global `hashwasm` variable -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/hash-wasm@4"></script>
|
||||
|
||||
<!-- load individual algortihms into the global `hashwasm` variable -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/hash-wasm@4/dist/md5.umd.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/hash-wasm@4/dist/hmac.umd.min.js"></script>
|
||||
```
|
||||
|
||||
# Examples
|
||||
|
||||
### Demo apps
|
||||
|
||||
[Hash calculator](https://3w4be.csb.app/) - [source code](https://codesandbox.io/s/hash-wasm-3w4be?file=/src/App.tsx)
|
||||
|
||||
[MD5 file hasher using HTML5 File API](https://stackoverflow.com/a/63287199/6251760)
|
||||
|
||||
### Usage with the shorthand form
|
||||
|
||||
It is the easiest and the fastest way to calculate hashes. Use it when the input buffer is already in the memory.
|
||||
|
||||
```javascript
|
||||
import { md5, sha1, sha512, sha3 } from "hash-wasm";
|
||||
|
||||
async function run() {
|
||||
console.log("MD5:", await md5("demo"));
|
||||
|
||||
const int8Buffer = new Uint8Array([0, 1, 2, 3]);
|
||||
console.log("SHA1:", await sha1(int8Buffer));
|
||||
console.log("SHA512:", await sha512(int8Buffer));
|
||||
|
||||
const int32Buffer = new Uint32Array([1056, 641]);
|
||||
console.log("SHA3-256:", await sha3(int32Buffer, 256));
|
||||
}
|
||||
|
||||
run();
|
||||
```
|
||||
|
||||
_\* See [String encoding pitfalls](#string-encoding-pitfalls)_
|
||||
|
||||
_\*\* See [API reference](#api)_
|
||||
|
||||
### Advanced usage with streaming input
|
||||
|
||||
createXXXX() functions create new WASM instances with separate states, which can be used to calculate multiple hashes paralelly. They are slower compared to shorthand functions like md5(), which reuse the same WASM instance and state to do multiple calculations. For this reason, the shorthand form is always preferred when the data is already in the memory.
|
||||
|
||||
For the best performance, avoid calling createXXXX() functions in loops. When calculating multiple hashes sequentially, the init() function can be used to reset the internal state between runs. It is faster than creating new instances with createXXXX().
|
||||
|
||||
```javascript
|
||||
import { createSHA1 } from "hash-wasm";
|
||||
|
||||
async function run() {
|
||||
const sha1 = await createSHA1();
|
||||
sha1.init();
|
||||
|
||||
while (hasMoreData()) {
|
||||
const chunk = readChunk();
|
||||
sha1.update(chunk);
|
||||
}
|
||||
|
||||
const hash = sha1.digest("binary"); // returns Uint8Array
|
||||
console.log("SHA1:", hash);
|
||||
}
|
||||
|
||||
run();
|
||||
```
|
||||
|
||||
_\* See [String encoding pitfalls](#string-encoding-pitfalls)_
|
||||
|
||||
_\*\* See [API reference](#api)_
|
||||
|
||||
### Hashing passwords with Argon2
|
||||
|
||||
The recommended process for choosing the parameters can be found here: https://tools.ietf.org/html/draft-irtf-cfrg-argon2-04#section-4
|
||||
|
||||
```javascript
|
||||
import { argon2id, argon2Verify } from "hash-wasm";
|
||||
|
||||
async function run() {
|
||||
const salt = new Uint8Array(16);
|
||||
window.crypto.getRandomValues(salt);
|
||||
|
||||
const key = await argon2id({
|
||||
password: "pass",
|
||||
salt, // salt is a buffer containing random bytes
|
||||
parallelism: 1,
|
||||
iterations: 256,
|
||||
memorySize: 512, // use 512KB memory
|
||||
hashLength: 32, // output size = 32 bytes
|
||||
outputType: "encoded", // return standard encoded string containing parameters needed to verify the key
|
||||
});
|
||||
|
||||
console.log("Derived key:", key);
|
||||
|
||||
const isValid = await argon2Verify({
|
||||
password: "pass",
|
||||
hash: key,
|
||||
});
|
||||
|
||||
console.log(isValid ? "Valid password" : "Invalid password");
|
||||
}
|
||||
|
||||
run();
|
||||
```
|
||||
|
||||
_\* See [String encoding pitfalls](#string-encoding-pitfalls)_
|
||||
|
||||
_\*\* See [API reference](#api)_
|
||||
|
||||
### Hashing passwords with bcrypt
|
||||
|
||||
```javascript
|
||||
import { bcrypt, bcryptVerify } from "hash-wasm";
|
||||
|
||||
async function run() {
|
||||
const salt = new Uint8Array(16);
|
||||
window.crypto.getRandomValues(salt);
|
||||
|
||||
const key = await bcrypt({
|
||||
password: "pass",
|
||||
salt, // salt is a buffer containing 16 random bytes
|
||||
costFactor: 11,
|
||||
outputType: "encoded", // return standard encoded string containing parameters needed to verify the key
|
||||
});
|
||||
|
||||
console.log("Derived key:", key);
|
||||
|
||||
const isValid = await bcryptVerify({
|
||||
password: "pass",
|
||||
hash: key,
|
||||
});
|
||||
|
||||
console.log(isValid ? "Valid password" : "Invalid password");
|
||||
}
|
||||
|
||||
run();
|
||||
```
|
||||
|
||||
_\* See [String encoding pitfalls](#string-encoding-pitfalls)_
|
||||
|
||||
_\*\* See [API reference](#api)_
|
||||
|
||||
### Calculating HMAC
|
||||
|
||||
All supported hash functions can be used to calculate HMAC. For the best performance, avoid calling createXXXX() in loops (see `Advanced usage with streaming input` section above)
|
||||
|
||||
```javascript
|
||||
import { createHMAC, createSHA3 } from "hash-wasm";
|
||||
|
||||
async function run() {
|
||||
const hashFunc = createSHA3(224); // SHA3-224
|
||||
const hmac = await createHMAC(hashFunc, "key");
|
||||
|
||||
const fruits = ["apple", "raspberry", "watermelon"];
|
||||
console.log("Input:", fruits);
|
||||
|
||||
const codes = fruits.map((data) => {
|
||||
hmac.init();
|
||||
hmac.update(data);
|
||||
return hmac.digest();
|
||||
});
|
||||
|
||||
console.log("HMAC:", codes);
|
||||
}
|
||||
|
||||
run();
|
||||
```
|
||||
|
||||
_\* See [String encoding pitfalls](#string-encoding-pitfalls)_
|
||||
|
||||
_\*\* See [API reference](#api)_
|
||||
|
||||
### Calculating PBKDF2
|
||||
|
||||
All supported hash functions can be used to calculate PBKDF2. For the best performance, avoid calling createXXXX() in loops (see `Advanced usage with streaming input` section above)
|
||||
|
||||
```javascript
|
||||
import { pbkdf2, createSHA1 } from "hash-wasm";
|
||||
|
||||
async function run() {
|
||||
const salt = new Uint8Array(16);
|
||||
window.crypto.getRandomValues(salt);
|
||||
|
||||
const key = await pbkdf2({
|
||||
password: "password",
|
||||
salt,
|
||||
iterations: 1000,
|
||||
hashLength: 32,
|
||||
hashFunction: createSHA1(),
|
||||
outputType: "hex",
|
||||
});
|
||||
|
||||
console.log("Derived key:", key);
|
||||
}
|
||||
|
||||
run();
|
||||
```
|
||||
|
||||
_\* See [String encoding pitfalls](#string-encoding-pitfalls)_
|
||||
|
||||
_\*\* See [API reference](#api)_
|
||||
|
||||
### String encoding pitfalls
|
||||
|
||||
You should be aware that there may be multiple UTF-8 representations of a given string:
|
||||
|
||||
```js
|
||||
"\u00fc"; // encodes the ü character
|
||||
"u\u0308"; // also encodes the ü character
|
||||
|
||||
"\u00fc" === "u\u0308"; // false
|
||||
"ü" === "ü"; // false
|
||||
```
|
||||
|
||||
All algorithms defined in this library depend on the binary representation of the input string. Thus, it's highly recommended to normalize your strings before passing it to hash-wasm. You can use the `normalize()` built-in String function to archive this:
|
||||
|
||||
```js
|
||||
"\u00fc".normalize() === "u\u0308".normalize(); // true
|
||||
|
||||
const te = new TextEncoder();
|
||||
te.encode("u\u0308"); // Uint8Array(3) [117, 204, 136]
|
||||
te.encode("\u00fc"); // Uint8Array(2) [195, 188]
|
||||
|
||||
te.encode("u\u0308".normalize("NFKC")); // Uint8Array(2) [195, 188]
|
||||
te.encode("\u00fc".normalize("NFKC")); // Uint8Array(2) [195, 188]
|
||||
```
|
||||
|
||||
You can read more about this issue here: https://en.wikipedia.org/wiki/Unicode_equivalence
|
||||
|
||||
### Resumable hashing
|
||||
|
||||
You can save the current internal state of the hash using the `.save()` function. This state may be written to disk or stored elsewhere in memory.
|
||||
You can then use the `.load(state)` function to reload that state into a new instance of the hash, or back into the same instance.
|
||||
|
||||
This allows you to span the work of hashing a file across multiple processes (e.g. in environments with limited execution times like AWS Lambda, where large jobs need to be split across multiple invocations), or rewind the hash to an earlier point in the stream. For example, the first process could:
|
||||
|
||||
```js
|
||||
// first process starts hashing
|
||||
const md5 = await createMD5();
|
||||
md5.init();
|
||||
md5.update("Hello, ");
|
||||
const state = md5.save(); // save this state
|
||||
|
||||
// second process resumes hashing from the stored state
|
||||
const md5 = await createMD5();
|
||||
md5.load(state);
|
||||
md5.update("world!");
|
||||
console.log(md5.digest()); // Prints 6cd3556deb0da54bca060b4c39479839 = md5("Hello, world!")
|
||||
```
|
||||
|
||||
_Note that both the saving and loading processes must be running compatible versions of the hash function (i.e. the hash function hasn't changed between the versions of hash-wasm used in the saving and loading processes). If the saved state is incompatible, `load()` will throw an exception._
|
||||
|
||||
_The saved state can contain information about the input, including plaintext input bytes, so from a security perspective it must be treated with the same care as the input data itself._
|
||||
|
||||
<br/>
|
||||
|
||||
# Browser support
|
||||
|
||||
<br/>
|
||||
|
||||
| Chrome | Safari | Firefox | Edge | IE | Node.js | Deno |
|
||||
| ------ | ------ | ------- | ---- | ------------- | ------- | ---- |
|
||||
| 57+ | 11+ | 53+ | 16+ | Not supported | 8+ | 1+ |
|
||||
|
||||
<br/>
|
||||
|
||||
# Benchmark
|
||||
|
||||
You can make your own measurements here: [link](https://daninet.github.io/hash-wasm-benchmark/)
|
||||
|
||||
Two scenarios were measured:
|
||||
|
||||
- throughput with the short form (input size = 32 bytes)
|
||||
- throughput with the short form (input size = 1MB)
|
||||
|
||||
Results:
|
||||
|
||||
| MD5 | throughput (32 bytes) | throughput (1MB) |
|
||||
| --------------------------- | --------------------- | ---------------- |
|
||||
| **hash-wasm 4.10.0** | **110.52 MB/s** | **850.31 MB/s** |
|
||||
| spark-md5 3.0.2 (from npm) | 38.87 MB/s | 171.73 MB/s |
|
||||
| md5-wasm 2.0.0 (from npm) | 37.36 MB/s | 131.77 MB/s |
|
||||
| crypto-js 4.1.1 (from npm) | 9.30 MB/s | 46.71 MB/s |
|
||||
| node-forge 1.3.1 (from npm) | 18.23 MB/s | 28.94 MB/s |
|
||||
| md5 2.3.0 (from npm) | 14.50 MB/s | 21.65 MB/s |
|
||||
|
||||
#
|
||||
|
||||
| SHA1 | throughput (32 bytes) | throughput (1MB) |
|
||||
| --------------------------- | --------------------- | ---------------- |
|
||||
| **hash-wasm 4.10.0** | **83.80 MB/s** | **798.19 MB/s** |
|
||||
| jsSHA 3.3.1 (from npm) | 34.93 MB/s | 78.12 MB/s |
|
||||
| crypto-js 4.1.1 (from npm) | 9.50 MB/s | 69.02 MB/s |
|
||||
| node-forge 1.3.1 (from npm) | 17.02 MB/s | 32.00 MB/s |
|
||||
| sha1 1.1.1 (from npm) | 14.68 MB/s | 24.24 MB/s |
|
||||
|
||||
#
|
||||
|
||||
| SHA256 | throughput (32 bytes) | throughput (1MB) |
|
||||
| ----------------------------- | --------------------- | ---------------- |
|
||||
| **hash-wasm 4.10.0** | **63.99 MB/s** | **426.16 MB/s** |
|
||||
| sha256-wasm 2.2.2 (from npm) | 20.37 MB/s | 308.39 MB/s |
|
||||
| noble-hashes 1.3.2 (from npm) | 24.73 MB/s | 110.02 MB/s |
|
||||
| crypto-js 4.1.1 (from npm) | 8.99 MB/s | 65.17 MB/s |
|
||||
| jsSHA 3.3.1 (from npm) | 25.64 MB/s | 57.98 MB/s |
|
||||
| node-forge 1.3.1 (from npm) | 13.93 MB/s | 28.19 MB/s |
|
||||
|
||||
#
|
||||
|
||||
| SHA3-512 | throughput (32 bytes) | throughput (1MB) |
|
||||
| ----------------------------- | --------------------- | ---------------- |
|
||||
| **hash-wasm 4.10.0** | **38.06 MB/s** | **234.40 MB/s** |
|
||||
| sha3-wasm 1.0.0 (from npm) | 15.44 MB/s | 101.51 MB/s |
|
||||
| noble-hashes 1.3.2 (from npm) | 5.74 MB/s | 14.19 MB/s |
|
||||
| sha3 2.1.4 (from npm) | 3.80 MB/s | 10.73 MB/s |
|
||||
| jsSHA 3.2.0 (from npm) | 2.08 MB/s | 3.82 MB/s |
|
||||
|
||||
#
|
||||
|
||||
| XXHash64 | throughput (32 bytes) | throughput (1MB) |
|
||||
| ---------------------------- | --------------------- | ------------------ |
|
||||
| **hash-wasm 4.10.0** | **101.66 MB/s** | **15 989 MB/s** |
|
||||
| xxhash-wasm 1.0.2 (from npm) | 47.58 MB/s | 15 929 MB/s |
|
||||
| xxhashjs 0.2.2 (from npm) | 0.92 MB/s | 42.26 MB/s |
|
||||
|
||||
#
|
||||
|
||||
| PBKDF2-SHA512 - 1000 iterations | operations per second (16 bytes) |
|
||||
| ------------------------------- | -------------------------------- |
|
||||
| **hash-wasm 4.10.0** | **588 ops** |
|
||||
| noble-hashes 1.3.2 (from npm) | 395 ops |
|
||||
| pbkdf2 3.1.2 (from npm) | 83 ops |
|
||||
| crypto-js 4.1.1 (from npm) | 29 ops |
|
||||
|
||||
#
|
||||
|
||||
| Argon2id (m=512, t=8, p=1) | operations per second (16 bytes) |
|
||||
| -------------------------------- | -------------------------------- |
|
||||
| **hash-wasm 4.10.0** | **438 ops** |
|
||||
| argon2-browser 1.18.0 (from npm) | 213 ops |
|
||||
| argon2-wasm-pro 1.1.0 (from npm) | 203 ops |
|
||||
| argon2-wasm 0.9.0 (from npm) | 195 ops |
|
||||
|
||||
<br/>
|
||||
|
||||
\* These measurements were made with `Chrome v131` on a Ryzen 9 7900X desktop CPU.
|
||||
|
||||
# API
|
||||
|
||||
```ts
|
||||
type IDataType = string | Buffer | Uint8Array | Uint16Array | Uint32Array;
|
||||
|
||||
// all functions return hash in hex format
|
||||
adler32(data: IDataType): Promise<string>
|
||||
blake2b(data: IDataType, bits?: number, key?: IDataType): Promise<string> // default is 512 bits
|
||||
blake2s(data: IDataType, bits?: number, key?: IDataType): Promise<string> // default is 256 bits
|
||||
blake3(data: IDataType, bits?: number, key?: IDataType): Promise<string> // default is 256 bits
|
||||
crc32(data: IDataType, polynomial?: number): Promise<string> // default polynomial is 0xedb88320, for CRC32C use 0x82f63b78
|
||||
crc64(data: IDataType, polynomial?: string): Promise<string> // default polynomial is 'c96c5795d7870f42' (ECMA)
|
||||
keccak(data: IDataType, bits?: 224 | 256 | 384 | 512): Promise<string> // default is 512 bits
|
||||
md4(data: IDataType): Promise<string>
|
||||
md5(data: IDataType): Promise<string>
|
||||
ripemd160(data: IDataType): Promise<string>
|
||||
sha1(data: IDataType): Promise<string>
|
||||
sha224(data: IDataType): Promise<string>
|
||||
sha256(data: IDataType): Promise<string>
|
||||
sha3(data: IDataType, bits?: 224 | 256 | 384 | 512): Promise<string> // default is 512 bits
|
||||
sha384(data: IDataType): Promise<string>
|
||||
sha512(data: IDataType): Promise<string>
|
||||
sm3(data: IDataType): Promise<string>
|
||||
whirlpool(data: IDataType): Promise<string>
|
||||
xxhash32(data: IDataType, seed?: number): Promise<string>
|
||||
xxhash64(data: IDataType, seedLow?: number, seedHigh?: number): Promise<string>
|
||||
xxhash3(data: IDataType, seedLow?: number, seedHigh?: number): Promise<string>
|
||||
xxhash128(data: IDataType, seedLow?: number, seedHigh?: number): Promise<string>
|
||||
|
||||
interface IHasher {
|
||||
init: () => IHasher;
|
||||
update: (data: IDataType) => IHasher;
|
||||
digest: (outputType: 'hex' | 'binary') => string | Uint8Array; // by default returns hex string
|
||||
save: () => Uint8Array; // returns the internal state for later resumption
|
||||
load: (state: Uint8Array) => IHasher; // loads a previously saved internal state
|
||||
blockSize: number; // in bytes
|
||||
digestSize: number; // in bytes
|
||||
}
|
||||
|
||||
createAdler32(): Promise<IHasher>
|
||||
createBLAKE2b(bits?: number, key?: IDataType): Promise<IHasher> // default is 512 bits
|
||||
createBLAKE2s(bits?: number, key?: IDataType): Promise<IHasher> // default is 256 bits
|
||||
createBLAKE3(bits?: number, key?: IDataType): Promise<IHasher> // default is 256 bits
|
||||
createCRC32(polynomial?: number): Promise<IHasher> // default polynomial is 0xedb88320, for CRC32C use 0x82f63b78
|
||||
createCRC64(polynomial?: number): Promise<IHasher> // default polynomial is 'c96c5795d7870f42' (ECMA)
|
||||
createKeccak(bits?: 224 | 256 | 384 | 512): Promise<IHasher> // default is 512 bits
|
||||
createMD4(): Promise<IHasher>
|
||||
createMD5(): Promise<IHasher>
|
||||
createRIPEMD160(): Promise<IHasher>
|
||||
createSHA1(): Promise<IHasher>
|
||||
createSHA224(): Promise<IHasher>
|
||||
createSHA256(): Promise<IHasher>
|
||||
createSHA3(bits?: 224 | 256 | 384 | 512): Promise<IHasher> // default is 512 bits
|
||||
createSHA384(): Promise<IHasher>
|
||||
createSHA512(): Promise<IHasher>
|
||||
createSM3(): Promise<IHasher>
|
||||
createWhirlpool(): Promise<IHasher>
|
||||
createXXHash32(seed: number): Promise<IHasher>
|
||||
createXXHash64(seedLow: number, seedHigh: number): Promise<IHasher>
|
||||
createXXHash3(seedLow: number, seedHigh: number): Promise<IHasher>
|
||||
createXXHash128(seedLow: number, seedHigh: number): Promise<IHasher>
|
||||
|
||||
createHMAC(hashFunction: Promise<IHasher>, key: IDataType): Promise<IHasher>
|
||||
|
||||
pbkdf2({
|
||||
password: IDataType, // password (or message) to be hashed
|
||||
salt: IDataType, // salt (usually containing random bytes)
|
||||
iterations: number, // number of iterations to perform
|
||||
hashLength: number, // output size in bytes
|
||||
hashFunction: Promise<IHasher>, // the return value of a function like createSHA1()
|
||||
outputType?: 'hex' | 'binary', // by default returns hex string
|
||||
}): Promise<string | Uint8Array>
|
||||
|
||||
scrypt({
|
||||
password: IDataType, // password (or message) to be hashed
|
||||
salt: IDataType, // salt (usually containing random bytes)
|
||||
costFactor: number, // CPU/memory cost - must be a power of 2 (e.g. 1024)
|
||||
blockSize: number, // block size parameter (8 is commonly used)
|
||||
parallelism: number, // degree of parallelism
|
||||
hashLength: number, // output size in bytes
|
||||
outputType?: 'hex' | 'binary', // by default returns hex string
|
||||
}): Promise<string | Uint8Array>
|
||||
|
||||
interface IArgon2Options {
|
||||
password: IDataType; // password (or message) to be hashed
|
||||
salt: IDataType; // salt (usually containing random bytes)
|
||||
secret?: IDataType; // secret for keyed hashing
|
||||
iterations: number; // number of iterations to perform
|
||||
parallelism: number; // degree of parallelism
|
||||
memorySize: number; // amount of memory to be used in kibibytes (1024 bytes)
|
||||
hashLength: number; // output size in bytes
|
||||
outputType?: 'hex' | 'binary' | 'encoded'; // by default returns hex string
|
||||
}
|
||||
|
||||
argon2i(options: IArgon2Options): Promise<string | Uint8Array>
|
||||
argon2d(options: IArgon2Options): Promise<string | Uint8Array>
|
||||
argon2id(options: IArgon2Options): Promise<string | Uint8Array>
|
||||
|
||||
argon2Verify({
|
||||
password: IDataType, // password
|
||||
secret?: IDataType, // secret used on hash creation
|
||||
hash: string, // encoded hash
|
||||
}): Promise<boolean>
|
||||
|
||||
bcrypt({
|
||||
password: IDataType, // password
|
||||
salt: IDataType, // salt (16 bytes long - usually containing random bytes)
|
||||
costFactor: number, // number of iterations to perform (4 - 31)
|
||||
outputType?: 'hex' | 'binary' | 'encoded', // by default returns encoded string
|
||||
}): Promise<string | Uint8Array>
|
||||
|
||||
bcryptVerify({
|
||||
password: IDataType, // password
|
||||
hash: string, // encoded hash
|
||||
}): Promise<boolean>
|
||||
|
||||
```
|
||||
|
||||
# Future plans
|
||||
|
||||
- Add more well-known algorithms
|
||||
- Write a polyfill which keeps bundle sizes low and enables running binaries containing newer WASM instructions
|
||||
- Use WebAssembly Bulk Memory Operations
|
||||
- Use WebAssembly SIMD instructions (expecting a 10-20% performance increase)
|
||||
- Enable multithreading where it's possible (like at Argon2)
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
|
||||
"vcs": {
|
||||
"enabled": false,
|
||||
"clientKind": "git",
|
||||
"useIgnoreFile": false
|
||||
},
|
||||
"files": {
|
||||
"ignoreUnknown": false,
|
||||
"ignore": []
|
||||
},
|
||||
"formatter": {
|
||||
"enabled": true,
|
||||
"indentStyle": "tab"
|
||||
},
|
||||
"organizeImports": {
|
||||
"enabled": true
|
||||
},
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
"rules": {
|
||||
"recommended": true
|
||||
}
|
||||
},
|
||||
"javascript": {
|
||||
"formatter": {
|
||||
"quoteStyle": "double"
|
||||
}
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"include": ["test/**"],
|
||||
"linter": {
|
||||
"rules": {
|
||||
"suspicious": {
|
||||
"noExplicitAny": "off"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
+7
File diff suppressed because one or more lines are too long
+7
File diff suppressed because one or more lines are too long
+7
File diff suppressed because one or more lines are too long
+7
File diff suppressed because one or more lines are too long
+7
File diff suppressed because one or more lines are too long
+7
File diff suppressed because one or more lines are too long
+7
File diff suppressed because one or more lines are too long
+7
File diff suppressed because one or more lines are too long
+7
@@ -0,0 +1,7 @@
|
||||
/*!
|
||||
* hash-wasm (https://www.npmjs.com/package/hash-wasm)
|
||||
* (c) Dani Biro
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).hashwasm=e.hashwasm||{})}(this,(function(e){"use strict";var t;const n="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:global,r=null!==(t=n.Buffer)&&void 0!==t?t:null,i=n.TextEncoder?new n.TextEncoder:null,f=null!==r?e=>{if("string"==typeof e){const t=r.from(e,"utf8");return new Uint8Array(t.buffer,t.byteOffset,t.length)}if(r.isBuffer(e))return new Uint8Array(e.buffer,e.byteOffset,e.length);if(ArrayBuffer.isView(e))return new Uint8Array(e.buffer,e.byteOffset,e.byteLength);throw new Error("Invalid data type!")}:e=>{if("string"==typeof e)return i.encode(e);if(ArrayBuffer.isView(e))return new Uint8Array(e.buffer,e.byteOffset,e.byteLength);throw new Error("Invalid data type!")},o="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",s=new Uint8Array(256);for(let e=0;e<o.length;e++)s[o.charCodeAt(e)]=e;function a(e,t){e.init();const{blockSize:n}=e,r=function(e,t){const{blockSize:n}=e,r=f(t);if(r.length>n){e.update(r);const t=e.digest("binary");return e.init(),t}return new Uint8Array(r.buffer,r.byteOffset,r.length)}(e,t),i=new Uint8Array(n);i.set(r);const o=new Uint8Array(n);for(let e=0;e<n;e++){const t=i[e];o[e]=92^t,i[e]=54^t}e.update(i);const s={init:()=>(e.init(),e.update(i),s),update:t=>(e.update(t),s),digest:t=>{const n=e.digest("binary");return e.init(),e.update(o),e.update(n),e.digest(t)},save:()=>{throw new Error("save() not supported")},load:()=>{throw new Error("load() not supported")},blockSize:e.blockSize,digestSize:e.digestSize};return s}e.createHMAC=function(e,t){if(!e||!e.then)throw new Error('Invalid hash function is provided! Usage: createHMAC(createMD5(), "key").');return e.then((e=>a(e,t)))}}));
|
||||
+2671
File diff suppressed because one or more lines are too long
+7
File diff suppressed because one or more lines are too long
+2731
File diff suppressed because one or more lines are too long
+7
File diff suppressed because one or more lines are too long
+7
File diff suppressed because one or more lines are too long
+60
@@ -0,0 +1,60 @@
|
||||
import { type IDataType, type IEmbeddedWasm } from "./util";
|
||||
export declare const MAX_HEAP: number;
|
||||
type ThenArg<T> = T extends Promise<infer U> ? U : T extends (...args: any[]) => Promise<infer V> ? V : T;
|
||||
export type IHasher = {
|
||||
/**
|
||||
* Initializes hash state to default value
|
||||
*/
|
||||
init: () => IHasher;
|
||||
/**
|
||||
* Updates the hash content with the given data
|
||||
*/
|
||||
update: (data: IDataType) => IHasher;
|
||||
/**
|
||||
* Calculates the hash of all of the data passed to be hashed with hash.update().
|
||||
* Defaults to hexadecimal string
|
||||
* @param outputType If outputType is "binary", it returns Uint8Array. Otherwise it
|
||||
* returns hexadecimal string
|
||||
*/
|
||||
digest: {
|
||||
(outputType: "binary"): Uint8Array;
|
||||
(outputType?: "hex"): string;
|
||||
};
|
||||
/**
|
||||
* Save the current internal state of the hasher for later resumption with load().
|
||||
* Cannot be called before .init() or after .digest()
|
||||
*
|
||||
* Note that this state can include arbitrary information about the value being hashed (e.g.
|
||||
* could include N plaintext bytes from the value), so needs to be treated as being as
|
||||
* sensitive as the input value itself.
|
||||
*/
|
||||
save: () => Uint8Array;
|
||||
/**
|
||||
* Resume a state that was created by save(). If this state was not created by a
|
||||
* compatible build of hash-wasm, an exception will be thrown.
|
||||
*/
|
||||
load: (state: Uint8Array) => IHasher;
|
||||
/**
|
||||
* Block size in bytes
|
||||
*/
|
||||
blockSize: number;
|
||||
/**
|
||||
* Digest size in bytes
|
||||
*/
|
||||
digestSize: number;
|
||||
};
|
||||
export declare function WASMInterface(binary: IEmbeddedWasm, hashLength: number): Promise<{
|
||||
getMemory: () => Uint8Array;
|
||||
writeMemory: (data: Uint8Array, offset?: number) => void;
|
||||
getExports: () => any;
|
||||
setMemorySize: (totalSize: number) => void;
|
||||
init: (bits?: number) => void;
|
||||
update: (data: IDataType) => void;
|
||||
digest: (outputType: "hex" | "binary", padding?: number) => Uint8Array | string;
|
||||
save: () => Uint8Array;
|
||||
load: (state: Uint8Array) => void;
|
||||
calculate: (data: IDataType, initParam?: any, digestParam?: any) => string;
|
||||
hashLength: number;
|
||||
}>;
|
||||
export type IWASMInterface = ThenArg<ReturnType<typeof WASMInterface>>;
|
||||
export {};
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import { type IHasher } from "./WASMInterface";
|
||||
import type { IDataType } from "./util";
|
||||
/**
|
||||
* Calculates Adler-32 hash. The resulting 32-bit hash is stored in
|
||||
* network byte order (big-endian).
|
||||
*
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export declare function adler32(data: IDataType): Promise<string>;
|
||||
/**
|
||||
* Creates a new Adler-32 hash instance
|
||||
*/
|
||||
export declare function createAdler32(): Promise<IHasher>;
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
import { type IDataType } from "./util";
|
||||
export interface IArgon2Options {
|
||||
/**
|
||||
* Password (or message) to be hashed
|
||||
*/
|
||||
password: IDataType;
|
||||
/**
|
||||
* Salt (usually containing random bytes)
|
||||
*/
|
||||
salt: IDataType;
|
||||
/**
|
||||
* Secret for keyed hashing
|
||||
*/
|
||||
secret?: IDataType;
|
||||
/**
|
||||
* Number of iterations to perform
|
||||
*/
|
||||
iterations: number;
|
||||
/**
|
||||
* Degree of parallelism
|
||||
*/
|
||||
parallelism: number;
|
||||
/**
|
||||
* Amount of memory to be used in kibibytes (1024 bytes)
|
||||
*/
|
||||
memorySize: number;
|
||||
/**
|
||||
* Output size in bytes
|
||||
*/
|
||||
hashLength: number;
|
||||
/**
|
||||
* Desired output type. Defaults to 'hex'
|
||||
*/
|
||||
outputType?: "hex" | "binary" | "encoded";
|
||||
}
|
||||
interface IArgon2OptionsBinary {
|
||||
outputType: "binary";
|
||||
}
|
||||
type Argon2ReturnType<T> = T extends IArgon2OptionsBinary ? Uint8Array : string;
|
||||
/**
|
||||
* Calculates hash using the argon2i password-hashing function
|
||||
* @returns Computed hash
|
||||
*/
|
||||
export declare function argon2i<T extends IArgon2Options>(options: T): Promise<Argon2ReturnType<T>>;
|
||||
/**
|
||||
* Calculates hash using the argon2id password-hashing function
|
||||
* @returns Computed hash
|
||||
*/
|
||||
export declare function argon2id<T extends IArgon2Options>(options: T): Promise<Argon2ReturnType<T>>;
|
||||
/**
|
||||
* Calculates hash using the argon2d password-hashing function
|
||||
* @returns Computed hash
|
||||
*/
|
||||
export declare function argon2d<T extends IArgon2Options>(options: T): Promise<Argon2ReturnType<T>>;
|
||||
export interface Argon2VerifyOptions {
|
||||
/**
|
||||
* Password to be verified
|
||||
*/
|
||||
password: IDataType;
|
||||
/**
|
||||
* Secret used on hash creation
|
||||
*/
|
||||
secret?: IDataType;
|
||||
/**
|
||||
* A previously generated argon2 hash in the 'encoded' output format
|
||||
*/
|
||||
hash: string;
|
||||
}
|
||||
/**
|
||||
* Verifies password using the argon2 password-hashing function
|
||||
* @returns True if the encoded hash matches the password
|
||||
*/
|
||||
export declare function argon2Verify(options: Argon2VerifyOptions): Promise<boolean>;
|
||||
export {};
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
import { type IDataType } from "./util";
|
||||
export interface BcryptOptions {
|
||||
/**
|
||||
* Password to be hashed
|
||||
*/
|
||||
password: IDataType;
|
||||
/**
|
||||
* Salt (16 bytes long - usually containing random bytes)
|
||||
*/
|
||||
salt: IDataType;
|
||||
/**
|
||||
* Number of iterations to perform (4 - 31)
|
||||
*/
|
||||
costFactor: number;
|
||||
/**
|
||||
* Desired output type. Defaults to 'encoded'
|
||||
*/
|
||||
outputType?: "hex" | "binary" | "encoded";
|
||||
}
|
||||
interface IBcryptOptionsBinary {
|
||||
outputType: "binary";
|
||||
}
|
||||
type BcryptReturnType<T> = T extends IBcryptOptionsBinary ? Uint8Array : string;
|
||||
/**
|
||||
* Calculates hash using the bcrypt password-hashing function
|
||||
* @returns Computed hash
|
||||
*/
|
||||
export declare function bcrypt<T extends BcryptOptions>(options: T): Promise<BcryptReturnType<T>>;
|
||||
export interface BcryptVerifyOptions {
|
||||
/**
|
||||
* Password to be verified
|
||||
*/
|
||||
password: IDataType;
|
||||
/**
|
||||
* A previously generated bcrypt hash in the 'encoded' output format
|
||||
*/
|
||||
hash: string;
|
||||
}
|
||||
/**
|
||||
* Verifies password using bcrypt password-hashing function
|
||||
* @returns True if the encoded hash matches the password
|
||||
*/
|
||||
export declare function bcryptVerify(options: BcryptVerifyOptions): Promise<boolean>;
|
||||
export {};
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import { type IHasher } from "./WASMInterface";
|
||||
import { type IDataType } from "./util";
|
||||
/**
|
||||
* Calculates BLAKE2b hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @param bits Number of output bits, which has to be a number
|
||||
* divisible by 8, between 8 and 512. Defaults to 512.
|
||||
* @param key Optional key (string, Buffer or TypedArray). Maximum length is 64 bytes.
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export declare function blake2b(data: IDataType, bits?: number, key?: IDataType): Promise<string>;
|
||||
/**
|
||||
* Creates a new BLAKE2b hash instance
|
||||
* @param bits Number of output bits, which has to be a number
|
||||
* divisible by 8, between 8 and 512. Defaults to 512.
|
||||
* @param key Optional key (string, Buffer or TypedArray). Maximum length is 64 bytes.
|
||||
*/
|
||||
export declare function createBLAKE2b(bits?: number, key?: IDataType): Promise<IHasher>;
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import { type IHasher } from "./WASMInterface";
|
||||
import { type IDataType } from "./util";
|
||||
/**
|
||||
* Calculates BLAKE2s hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @param bits Number of output bits, which has to be a number
|
||||
* divisible by 8, between 8 and 256. Defaults to 256.
|
||||
* @param key Optional key (string, Buffer or TypedArray). Maximum length is 32 bytes.
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export declare function blake2s(data: IDataType, bits?: number, key?: IDataType): Promise<string>;
|
||||
/**
|
||||
* Creates a new BLAKE2s hash instance
|
||||
* @param bits Number of output bits, which has to be a number
|
||||
* divisible by 8, between 8 and 256. Defaults to 256.
|
||||
* @param key Optional key (string, Buffer or TypedArray). Maximum length is 32 bytes.
|
||||
*/
|
||||
export declare function createBLAKE2s(bits?: number, key?: IDataType): Promise<IHasher>;
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import { type IHasher } from "./WASMInterface";
|
||||
import { type IDataType } from "./util";
|
||||
/**
|
||||
* Calculates BLAKE3 hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @param bits Number of output bits, which has to be a number
|
||||
* divisible by 8. Defaults to 256.
|
||||
* @param key Optional key (string, Buffer or TypedArray). Length should be 32 bytes.
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export declare function blake3(data: IDataType, bits?: number, key?: IDataType): Promise<string>;
|
||||
/**
|
||||
* Creates a new BLAKE3 hash instance
|
||||
* @param bits Number of output bits, which has to be a number
|
||||
* divisible by 8. Defaults to 256.
|
||||
* @param key Optional key (string, Buffer or TypedArray). Length should be 32 bytes.
|
||||
*/
|
||||
export declare function createBLAKE3(bits?: number, key?: IDataType): Promise<IHasher>;
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import { type IHasher } from "./WASMInterface";
|
||||
import type { IDataType } from "./util";
|
||||
/**
|
||||
* Calculates CRC-32 hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @param polynomial Input polynomial (defaults to 0xedb88320, for CRC32C use 0x82f63b78)
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export declare function crc32(data: IDataType, polynomial?: number): Promise<string>;
|
||||
/**
|
||||
* Creates a new CRC-32 hash instance
|
||||
* @param polynomial Input polynomial (defaults to 0xedb88320, for CRC32C use 0x82f63b78)
|
||||
*/
|
||||
export declare function createCRC32(polynomial?: number): Promise<IHasher>;
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import { type IHasher } from "./WASMInterface";
|
||||
import type { IDataType } from "./util";
|
||||
/**
|
||||
* Calculates CRC-64 hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @param polynomial Input polynomial (defaults to 'c96c5795d7870f42' - ECMA)
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export declare function crc64(data: IDataType, polynomial?: string): Promise<string>;
|
||||
/**
|
||||
* Creates a new CRC-64 hash instance
|
||||
* @param polynomial Input polynomial (defaults to 'c96c5795d7870f42' - ECMA)
|
||||
*/
|
||||
export declare function createCRC64(polynomial?: string): Promise<IHasher>;
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import type { IHasher } from "./WASMInterface";
|
||||
import { type IDataType } from "./util";
|
||||
/**
|
||||
* Calculates HMAC hash
|
||||
* @param hash Hash algorithm to use. It has to be the return value of a function like createSHA1()
|
||||
* @param key Key (string, Buffer or TypedArray)
|
||||
*/
|
||||
export declare function createHMAC(hash: Promise<IHasher>, key: IDataType): Promise<IHasher>;
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
export * from "./adler32";
|
||||
export * from "./argon2";
|
||||
export * from "./blake2b";
|
||||
export * from "./blake2s";
|
||||
export * from "./blake3";
|
||||
export * from "./crc32";
|
||||
export * from "./crc64";
|
||||
export * from "./md4";
|
||||
export * from "./md5";
|
||||
export * from "./sha1";
|
||||
export * from "./sha3";
|
||||
export * from "./keccak";
|
||||
export * from "./sha224";
|
||||
export * from "./sha256";
|
||||
export * from "./sha384";
|
||||
export * from "./sha512";
|
||||
export * from "./xxhash32";
|
||||
export * from "./xxhash64";
|
||||
export * from "./xxhash3";
|
||||
export * from "./xxhash128";
|
||||
export * from "./ripemd160";
|
||||
export * from "./hmac";
|
||||
export * from "./pbkdf2";
|
||||
export * from "./scrypt";
|
||||
export * from "./bcrypt";
|
||||
export * from "./whirlpool";
|
||||
export * from "./sm3";
|
||||
export type { IDataType } from "./util";
|
||||
export type { IHasher } from "./WASMInterface";
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import { type IHasher } from "./WASMInterface";
|
||||
import type { IDataType } from "./util";
|
||||
type IValidBits = 224 | 256 | 384 | 512;
|
||||
/**
|
||||
* Calculates Keccak hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @param bits Number of output bits. Valid values: 224, 256, 384, 512
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export declare function keccak(data: IDataType, bits?: IValidBits): Promise<string>;
|
||||
/**
|
||||
* Creates a new Keccak hash instance
|
||||
* @param bits Number of output bits. Valid values: 224, 256, 384, 512
|
||||
*/
|
||||
export declare function createKeccak(bits?: IValidBits): Promise<IHasher>;
|
||||
export {};
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import { type IWASMInterface } from "./WASMInterface";
|
||||
import type Mutex from "./mutex";
|
||||
import type { IEmbeddedWasm } from "./util";
|
||||
export default function lockedCreate(mutex: Mutex, binary: IEmbeddedWasm, hashLength: number): Promise<IWASMInterface>;
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { type IHasher } from "./WASMInterface";
|
||||
import type { IDataType } from "./util";
|
||||
/**
|
||||
* Calculates MD4 hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export declare function md4(data: IDataType): Promise<string>;
|
||||
/**
|
||||
* Creates a new MD4 hash instance
|
||||
*/
|
||||
export declare function createMD4(): Promise<IHasher>;
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { type IHasher } from "./WASMInterface";
|
||||
import type { IDataType } from "./util";
|
||||
/**
|
||||
* Calculates MD5 hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export declare function md5(data: IDataType): Promise<string>;
|
||||
/**
|
||||
* Creates a new MD5 hash instance
|
||||
*/
|
||||
export declare function createMD5(): Promise<IHasher>;
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
declare class Mutex {
|
||||
private mutex;
|
||||
lock(): PromiseLike<() => void>;
|
||||
dispatch<T>(fn: () => PromiseLike<T>): Promise<T>;
|
||||
}
|
||||
export default Mutex;
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
import type { IHasher } from "./WASMInterface";
|
||||
import { type IDataType } from "./util";
|
||||
export interface IPBKDF2Options {
|
||||
/**
|
||||
* Password (or message) to be hashed
|
||||
*/
|
||||
password: IDataType;
|
||||
/**
|
||||
* Salt (usually containing random bytes)
|
||||
*/
|
||||
salt: IDataType;
|
||||
/**
|
||||
* Number of iterations to perform
|
||||
*/
|
||||
iterations: number;
|
||||
/**
|
||||
* Output size in bytes
|
||||
*/
|
||||
hashLength: number;
|
||||
/**
|
||||
* Hash algorithm to use. It has to be the return value of a function like createSHA1()
|
||||
*/
|
||||
hashFunction: Promise<IHasher>;
|
||||
/**
|
||||
* Desired output type. Defaults to 'hex'
|
||||
*/
|
||||
outputType?: "hex" | "binary";
|
||||
}
|
||||
interface IPBKDF2OptionsBinary {
|
||||
outputType: "binary";
|
||||
}
|
||||
type PBKDF2ReturnType<T> = T extends IPBKDF2OptionsBinary ? Uint8Array : string;
|
||||
/**
|
||||
* Generates a new PBKDF2 hash for the supplied password
|
||||
*/
|
||||
export declare function pbkdf2<T extends IPBKDF2Options>(options: T): Promise<PBKDF2ReturnType<T>>;
|
||||
export {};
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { type IHasher } from "./WASMInterface";
|
||||
import type { IDataType } from "./util";
|
||||
/**
|
||||
* Calculates RIPEMD-160 hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export declare function ripemd160(data: IDataType): Promise<string>;
|
||||
/**
|
||||
* Creates a new RIPEMD-160 hash instance
|
||||
*/
|
||||
export declare function createRIPEMD160(): Promise<IHasher>;
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
import { type IDataType } from "./util";
|
||||
export interface ScryptOptions {
|
||||
/**
|
||||
* Password (or message) to be hashed
|
||||
*/
|
||||
password: IDataType;
|
||||
/**
|
||||
* Salt (usually containing random bytes)
|
||||
*/
|
||||
salt: IDataType;
|
||||
/**
|
||||
* CPU / memory cost - must be a power of 2 (e.g. 1024)
|
||||
*/
|
||||
costFactor: number;
|
||||
/**
|
||||
* Block size (8 is commonly used)
|
||||
*/
|
||||
blockSize: number;
|
||||
/**
|
||||
* Degree of parallelism
|
||||
*/
|
||||
parallelism: number;
|
||||
/**
|
||||
* Output size in bytes
|
||||
*/
|
||||
hashLength: number;
|
||||
/**
|
||||
* Output data type. Defaults to hexadecimal string
|
||||
*/
|
||||
outputType?: "hex" | "binary";
|
||||
}
|
||||
interface IScryptOptionsBinary {
|
||||
outputType: "binary";
|
||||
}
|
||||
type ScryptReturnType<T> = T extends IScryptOptionsBinary ? Uint8Array : string;
|
||||
/**
|
||||
* Calculates hash using the scrypt password-based key derivation function
|
||||
* @returns Computed hash as a hexadecimal string or as
|
||||
* Uint8Array depending on the outputType option
|
||||
*/
|
||||
export declare function scrypt<T extends ScryptOptions>(options: T): Promise<ScryptReturnType<T>>;
|
||||
export {};
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { type IHasher } from "./WASMInterface";
|
||||
import type { IDataType } from "./util";
|
||||
/**
|
||||
* Calculates SHA-1 hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export declare function sha1(data: IDataType): Promise<string>;
|
||||
/**
|
||||
* Creates a new SHA-1 hash instance
|
||||
*/
|
||||
export declare function createSHA1(): Promise<IHasher>;
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { type IHasher } from "./WASMInterface";
|
||||
import type { IDataType } from "./util";
|
||||
/**
|
||||
* Calculates SHA-2 (SHA-224) hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export declare function sha224(data: IDataType): Promise<string>;
|
||||
/**
|
||||
* Creates a new SHA-2 (SHA-224) hash instance
|
||||
*/
|
||||
export declare function createSHA224(): Promise<IHasher>;
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { type IHasher } from "./WASMInterface";
|
||||
import type { IDataType } from "./util";
|
||||
/**
|
||||
* Calculates SHA-2 (SHA-256) hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export declare function sha256(data: IDataType): Promise<string>;
|
||||
/**
|
||||
* Creates a new SHA-2 (SHA-256) hash instance
|
||||
*/
|
||||
export declare function createSHA256(): Promise<IHasher>;
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import { type IHasher } from "./WASMInterface";
|
||||
import type { IDataType } from "./util";
|
||||
type IValidBits = 224 | 256 | 384 | 512;
|
||||
/**
|
||||
* Calculates SHA-3 hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @param bits Number of output bits. Valid values: 224, 256, 384, 512
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export declare function sha3(data: IDataType, bits?: IValidBits): Promise<string>;
|
||||
/**
|
||||
* Creates a new SHA-3 hash instance
|
||||
* @param bits Number of output bits. Valid values: 224, 256, 384, 512
|
||||
*/
|
||||
export declare function createSHA3(bits?: IValidBits): Promise<IHasher>;
|
||||
export {};
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { type IHasher } from "./WASMInterface";
|
||||
import type { IDataType } from "./util";
|
||||
/**
|
||||
* Calculates SHA-2 (SHA-384) hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export declare function sha384(data: IDataType): Promise<string>;
|
||||
/**
|
||||
* Creates a new SHA-2 (SHA-384) hash instance
|
||||
*/
|
||||
export declare function createSHA384(): Promise<IHasher>;
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { type IHasher } from "./WASMInterface";
|
||||
import type { IDataType } from "./util";
|
||||
/**
|
||||
* Calculates SHA-2 (SHA-512) hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export declare function sha512(data: IDataType): Promise<string>;
|
||||
/**
|
||||
* Creates a new SHA-2 (SHA-512) hash instance
|
||||
*/
|
||||
export declare function createSHA512(): Promise<IHasher>;
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { type IHasher } from "./WASMInterface";
|
||||
import type { IDataType } from "./util";
|
||||
/**
|
||||
* Calculates SM3 hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export declare function sm3(data: IDataType): Promise<string>;
|
||||
/**
|
||||
* Creates a new SM3 hash instance
|
||||
*/
|
||||
export declare function createSM3(): Promise<IHasher>;
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
export type ITypedArray = Uint8Array | Uint16Array | Uint32Array;
|
||||
export type IDataType = string | Buffer | ITypedArray;
|
||||
export type IEmbeddedWasm = {
|
||||
name: string;
|
||||
data: string;
|
||||
hash: string;
|
||||
};
|
||||
export declare function intArrayToString(arr: Uint8Array, len: number): string;
|
||||
export declare function writeHexToUInt8(buf: Uint8Array, str: string): void;
|
||||
export declare function hexStringEqualsUInt8(str: string, buf: Uint8Array): boolean;
|
||||
export declare function getDigestHex(tmpBuffer: Uint8Array, input: Uint8Array, hashLength: number): string;
|
||||
export declare const getUInt8Buffer: (data: IDataType) => Uint8Array;
|
||||
export declare function encodeBase64(data: Uint8Array, pad?: boolean): string;
|
||||
export declare function getDecodeBase64Length(data: string): number;
|
||||
export declare function decodeBase64(data: string): Uint8Array;
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { type IHasher } from "./WASMInterface";
|
||||
import type { IDataType } from "./util";
|
||||
/**
|
||||
* Calculates Whirlpool hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export declare function whirlpool(data: IDataType): Promise<string>;
|
||||
/**
|
||||
* Creates a new Whirlpool hash instance
|
||||
*/
|
||||
export declare function createWhirlpool(): Promise<IHasher>;
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
import { type IHasher } from "./WASMInterface";
|
||||
import type { IDataType } from "./util";
|
||||
/**
|
||||
* Calculates xxHash128 hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @param seedLow Lower 32 bits of the number used to
|
||||
* initialize the internal state of the algorithm (defaults to 0)
|
||||
* @param seedHigh Higher 32 bits of the number used to
|
||||
* initialize the internal state of the algorithm (defaults to 0)
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export declare function xxhash128(data: IDataType, seedLow?: number, seedHigh?: number): Promise<string>;
|
||||
/**
|
||||
* Creates a new xxHash128 hash instance
|
||||
* @param seedLow Lower 32 bits of the number used to
|
||||
* initialize the internal state of the algorithm (defaults to 0)
|
||||
* @param seedHigh Higher 32 bits of the number used to
|
||||
* initialize the internal state of the algorithm (defaults to 0)
|
||||
*/
|
||||
export declare function createXXHash128(seedLow?: number, seedHigh?: number): Promise<IHasher>;
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
import { type IHasher } from "./WASMInterface";
|
||||
import type { IDataType } from "./util";
|
||||
/**
|
||||
* Calculates xxHash3 hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @param seedLow Lower 32 bits of the number used to
|
||||
* initialize the internal state of the algorithm (defaults to 0)
|
||||
* @param seedHigh Higher 32 bits of the number used to
|
||||
* initialize the internal state of the algorithm (defaults to 0)
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export declare function xxhash3(data: IDataType, seedLow?: number, seedHigh?: number): Promise<string>;
|
||||
/**
|
||||
* Creates a new xxHash3 hash instance
|
||||
* @param seedLow Lower 32 bits of the number used to
|
||||
* initialize the internal state of the algorithm (defaults to 0)
|
||||
* @param seedHigh Higher 32 bits of the number used to
|
||||
* initialize the internal state of the algorithm (defaults to 0)
|
||||
*/
|
||||
export declare function createXXHash3(seedLow?: number, seedHigh?: number): Promise<IHasher>;
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import { type IHasher } from "./WASMInterface";
|
||||
import type { IDataType } from "./util";
|
||||
/**
|
||||
* Calculates xxHash32 hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @param seed Number used to initialize the internal state of the algorithm (defaults to 0)
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export declare function xxhash32(data: IDataType, seed?: number): Promise<string>;
|
||||
/**
|
||||
* Creates a new xxHash32 hash instance
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @param seed Number used to initialize the internal state of the algorithm (defaults to 0)
|
||||
*/
|
||||
export declare function createXXHash32(seed?: number): Promise<IHasher>;
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
import { type IHasher } from "./WASMInterface";
|
||||
import type { IDataType } from "./util";
|
||||
/**
|
||||
* Calculates xxHash64 hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @param seedLow Lower 32 bits of the number used to
|
||||
* initialize the internal state of the algorithm (defaults to 0)
|
||||
* @param seedHigh Higher 32 bits of the number used to
|
||||
* initialize the internal state of the algorithm (defaults to 0)
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export declare function xxhash64(data: IDataType, seedLow?: number, seedHigh?: number): Promise<string>;
|
||||
/**
|
||||
* Creates a new xxHash64 hash instance
|
||||
* @param seedLow Lower 32 bits of the number used to
|
||||
* initialize the internal state of the algorithm (defaults to 0)
|
||||
* @param seedHigh Higher 32 bits of the number used to
|
||||
* initialize the internal state of the algorithm (defaults to 0)
|
||||
*/
|
||||
export declare function createXXHash64(seedLow?: number, seedHigh?: number): Promise<IHasher>;
|
||||
+7
File diff suppressed because one or more lines are too long
+7
File diff suppressed because one or more lines are too long
+7
@@ -0,0 +1,7 @@
|
||||
/*!
|
||||
* hash-wasm (https://www.npmjs.com/package/hash-wasm)
|
||||
* (c) Dani Biro
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).hashwasm=e.hashwasm||{})}(this,(function(e){"use strict";function t(e,t,n,r){return new(n||(n=Promise))((function(i,o){function u(e){try{f(r.next(e))}catch(e){o(e)}}function a(e){try{f(r.throw(e))}catch(e){o(e)}}function f(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(u,a)}f((r=r.apply(e,t||[])).next())}))}var n;"function"==typeof SuppressedError&&SuppressedError;const r="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:global,i=null!==(n=r.Buffer)&&void 0!==n?n:null,o=r.TextEncoder?new r.TextEncoder:null,u="a".charCodeAt(0)-10,a="0".charCodeAt(0);const f=null!==i?e=>{if("string"==typeof e){const t=i.from(e,"utf8");return new Uint8Array(t.buffer,t.byteOffset,t.length)}if(i.isBuffer(e))return new Uint8Array(e.buffer,e.byteOffset,e.length);if(ArrayBuffer.isView(e))return new Uint8Array(e.buffer,e.byteOffset,e.byteLength);throw new Error("Invalid data type!")}:e=>{if("string"==typeof e)return o.encode(e);if(ArrayBuffer.isView(e))return new Uint8Array(e.buffer,e.byteOffset,e.byteLength);throw new Error("Invalid data type!")},s="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",d=new Uint8Array(256);for(let e=0;e<s.length;e++)d[s.charCodeAt(e)]=e;function l(e,t){e.init();const{blockSize:n}=e,r=function(e,t){const{blockSize:n}=e,r=f(t);if(r.length>n){e.update(r);const t=e.digest("binary");return e.init(),t}return new Uint8Array(r.buffer,r.byteOffset,r.length)}(e,t),i=new Uint8Array(n);i.set(r);const o=new Uint8Array(n);for(let e=0;e<n;e++){const t=i[e];o[e]=92^t,i[e]=54^t}e.update(i);const u={init:()=>(e.init(),e.update(i),u),update:t=>(e.update(t),u),digest:t=>{const n=e.digest("binary");return e.init(),e.update(o),e.update(n),e.digest(t)},save:()=>{throw new Error("save() not supported")},load:()=>{throw new Error("load() not supported")},blockSize:e.blockSize,digestSize:e.digestSize};return u}function h(e,n,r,i,o){return t(this,void 0,void 0,(function*(){const t=new Uint8Array(i),s=new Uint8Array(n.length+4),d=new DataView(s.buffer),l=f(n),h=new Uint8Array(l.buffer,l.byteOffset,l.length);s.set(h);let p=0;const c=e.digestSize,y=Math.ceil(i/c);let w=null,b=null;for(let o=1;o<=y;o++){d.setUint32(n.length,o),e.init(),e.update(s),w=e.digest("binary"),b=w.slice();for(let t=1;t<r;t++){e.init(),e.update(b),b=e.digest("binary");for(let e=0;e<c;e++)w[e]^=b[e]}t.set(w.subarray(0,i-p),p),p+=c}if("binary"===o)return t;return function(e,t,n){let r=0;for(let i=0;i<n;i++){let n=t[i]>>>4;e[r++]=n>9?n+u:n+a,n=15&t[i],e[r++]=n>9?n+u:n+a}return String.fromCharCode.apply(null,e)}(new Uint8Array(2*i),t,i)}))}e.pbkdf2=function(e){return t(this,void 0,void 0,(function*(){(e=>{if(!e||"object"!=typeof e)throw new Error("Invalid options parameter. It requires an object.");if(!e.hashFunction||!e.hashFunction.then)throw new Error('Invalid hash function is provided! Usage: pbkdf2("password", "salt", 1000, 32, createSHA1()).');if(!Number.isInteger(e.iterations)||e.iterations<1)throw new Error("Iterations should be a positive number");if(!Number.isInteger(e.hashLength)||e.hashLength<1)throw new Error("Hash length should be a positive number");if(void 0===e.outputType&&(e.outputType="hex"),!["hex","binary"].includes(e.outputType))throw new Error(`Insupported output type ${e.outputType}. Valid values: ['hex', 'binary']`)})(e);return h(yield function(e,t){if(!e||!e.then)throw new Error('Invalid hash function is provided! Usage: createHMAC(createMD5(), "key").');return e.then((e=>l(e,t)))}(e.hashFunction,e.password),e.salt,e.iterations,e.hashLength,e.outputType)}))}}));
|
||||
+7
File diff suppressed because one or more lines are too long
+7
File diff suppressed because one or more lines are too long
+7
File diff suppressed because one or more lines are too long
+7
File diff suppressed because one or more lines are too long
+7
File diff suppressed because one or more lines are too long
+7
File diff suppressed because one or more lines are too long
+7
File diff suppressed because one or more lines are too long
+7
File diff suppressed because one or more lines are too long
+7
File diff suppressed because one or more lines are too long
+7
File diff suppressed because one or more lines are too long
+7
File diff suppressed because one or more lines are too long
+7
File diff suppressed because one or more lines are too long
+7
File diff suppressed because one or more lines are too long
+7
File diff suppressed because one or more lines are too long
+9
@@ -0,0 +1,9 @@
|
||||
module.exports = {
|
||||
roots: [
|
||||
'<rootDir>/test',
|
||||
],
|
||||
transform: {
|
||||
'^.+\\.ts$': 'ts-jest',
|
||||
},
|
||||
cacheDirectory: '<rootDir>/.jest-cache',
|
||||
};
|
||||
+310
@@ -0,0 +1,310 @@
|
||||
import Mutex from "./mutex";
|
||||
import {
|
||||
type IDataType,
|
||||
type IEmbeddedWasm,
|
||||
decodeBase64,
|
||||
getDigestHex,
|
||||
getUInt8Buffer,
|
||||
hexStringEqualsUInt8,
|
||||
writeHexToUInt8,
|
||||
} from "./util";
|
||||
|
||||
export const MAX_HEAP = 16 * 1024;
|
||||
const WASM_FUNC_HASH_LENGTH = 4;
|
||||
const wasmMutex = new Mutex();
|
||||
|
||||
type ThenArg<T> = T extends Promise<infer U>
|
||||
? U
|
||||
: // biome-ignore lint/suspicious/noExplicitAny: TS quirks
|
||||
T extends (...args: any[]) => Promise<infer V>
|
||||
? V
|
||||
: T;
|
||||
|
||||
export type IHasher = {
|
||||
/**
|
||||
* Initializes hash state to default value
|
||||
*/
|
||||
init: () => IHasher;
|
||||
/**
|
||||
* Updates the hash content with the given data
|
||||
*/
|
||||
update: (data: IDataType) => IHasher;
|
||||
/**
|
||||
* Calculates the hash of all of the data passed to be hashed with hash.update().
|
||||
* Defaults to hexadecimal string
|
||||
* @param outputType If outputType is "binary", it returns Uint8Array. Otherwise it
|
||||
* returns hexadecimal string
|
||||
*/
|
||||
digest: {
|
||||
(outputType: "binary"): Uint8Array;
|
||||
(outputType?: "hex"): string;
|
||||
};
|
||||
/**
|
||||
* Save the current internal state of the hasher for later resumption with load().
|
||||
* Cannot be called before .init() or after .digest()
|
||||
*
|
||||
* Note that this state can include arbitrary information about the value being hashed (e.g.
|
||||
* could include N plaintext bytes from the value), so needs to be treated as being as
|
||||
* sensitive as the input value itself.
|
||||
*/
|
||||
save: () => Uint8Array;
|
||||
/**
|
||||
* Resume a state that was created by save(). If this state was not created by a
|
||||
* compatible build of hash-wasm, an exception will be thrown.
|
||||
*/
|
||||
load: (state: Uint8Array) => IHasher;
|
||||
/**
|
||||
* Block size in bytes
|
||||
*/
|
||||
blockSize: number;
|
||||
/**
|
||||
* Digest size in bytes
|
||||
*/
|
||||
digestSize: number;
|
||||
};
|
||||
|
||||
const wasmModuleCache = new Map<string, Promise<WebAssembly.Module>>();
|
||||
|
||||
export async function WASMInterface(binary: IEmbeddedWasm, hashLength: number) {
|
||||
let wasmInstance = null;
|
||||
let memoryView: Uint8Array = null;
|
||||
let initialized = false;
|
||||
|
||||
if (typeof WebAssembly === "undefined") {
|
||||
throw new Error("WebAssembly is not supported in this environment!");
|
||||
}
|
||||
|
||||
const writeMemory = (data: Uint8Array, offset = 0) => {
|
||||
memoryView.set(data, offset);
|
||||
};
|
||||
|
||||
const getMemory = () => memoryView;
|
||||
const getExports = () => wasmInstance.exports;
|
||||
|
||||
const setMemorySize = (totalSize: number) => {
|
||||
wasmInstance.exports.Hash_SetMemorySize(totalSize);
|
||||
const arrayOffset: number = wasmInstance.exports.Hash_GetBuffer();
|
||||
const memoryBuffer = wasmInstance.exports.memory.buffer;
|
||||
memoryView = new Uint8Array(memoryBuffer, arrayOffset, totalSize);
|
||||
};
|
||||
|
||||
const getStateSize = () => {
|
||||
const view = new DataView(wasmInstance.exports.memory.buffer);
|
||||
const stateSize = view.getUint32(wasmInstance.exports.STATE_SIZE, true);
|
||||
return stateSize;
|
||||
};
|
||||
|
||||
const loadWASMPromise = wasmMutex.dispatch(async () => {
|
||||
if (!wasmModuleCache.has(binary.name)) {
|
||||
const asm = decodeBase64(binary.data);
|
||||
const promise = WebAssembly.compile(asm);
|
||||
|
||||
wasmModuleCache.set(binary.name, promise);
|
||||
}
|
||||
|
||||
const module = await wasmModuleCache.get(binary.name);
|
||||
wasmInstance = await WebAssembly.instantiate(module, {
|
||||
// env: {
|
||||
// emscripten_memcpy_big: (dest, src, num) => {
|
||||
// const memoryBuffer = wasmInstance.exports.memory.buffer;
|
||||
// const memView = new Uint8Array(memoryBuffer, 0);
|
||||
// memView.set(memView.subarray(src, src + num), dest);
|
||||
// },
|
||||
// print_memory: (offset, len) => {
|
||||
// const memoryBuffer = wasmInstance.exports.memory.buffer;
|
||||
// const memView = new Uint8Array(memoryBuffer, 0);
|
||||
// console.log('print_int32', memView.subarray(offset, offset + len));
|
||||
// },
|
||||
// },
|
||||
});
|
||||
|
||||
// wasmInstance.exports._start();
|
||||
});
|
||||
|
||||
const setupInterface = async () => {
|
||||
if (!wasmInstance) {
|
||||
await loadWASMPromise;
|
||||
}
|
||||
|
||||
const arrayOffset: number = wasmInstance.exports.Hash_GetBuffer();
|
||||
const memoryBuffer = wasmInstance.exports.memory.buffer;
|
||||
memoryView = new Uint8Array(memoryBuffer, arrayOffset, MAX_HEAP);
|
||||
};
|
||||
|
||||
const init = (bits: number = null) => {
|
||||
initialized = true;
|
||||
wasmInstance.exports.Hash_Init(bits);
|
||||
};
|
||||
|
||||
const updateUInt8Array = (data: Uint8Array): void => {
|
||||
let read = 0;
|
||||
while (read < data.length) {
|
||||
const chunk = data.subarray(read, read + MAX_HEAP);
|
||||
read += chunk.length;
|
||||
memoryView.set(chunk);
|
||||
wasmInstance.exports.Hash_Update(chunk.length);
|
||||
}
|
||||
};
|
||||
|
||||
const update = (data: IDataType) => {
|
||||
if (!initialized) {
|
||||
throw new Error("update() called before init()");
|
||||
}
|
||||
const Uint8Buffer = getUInt8Buffer(data);
|
||||
updateUInt8Array(Uint8Buffer);
|
||||
};
|
||||
|
||||
const digestChars = new Uint8Array(hashLength * 2);
|
||||
|
||||
const digest = (
|
||||
outputType: "hex" | "binary",
|
||||
padding: number = null,
|
||||
): Uint8Array | string => {
|
||||
if (!initialized) {
|
||||
throw new Error("digest() called before init()");
|
||||
}
|
||||
initialized = false;
|
||||
|
||||
wasmInstance.exports.Hash_Final(padding);
|
||||
|
||||
if (outputType === "binary") {
|
||||
// the data is copied to allow GC of the original memory object
|
||||
return memoryView.slice(0, hashLength);
|
||||
}
|
||||
|
||||
return getDigestHex(digestChars, memoryView, hashLength);
|
||||
};
|
||||
|
||||
const save = (): Uint8Array => {
|
||||
if (!initialized) {
|
||||
throw new Error(
|
||||
"save() can only be called after init() and before digest()",
|
||||
);
|
||||
}
|
||||
|
||||
const stateOffset: number = wasmInstance.exports.Hash_GetState();
|
||||
const stateLength: number = getStateSize();
|
||||
const memoryBuffer = wasmInstance.exports.memory.buffer;
|
||||
const internalState = new Uint8Array(
|
||||
memoryBuffer,
|
||||
stateOffset,
|
||||
stateLength,
|
||||
);
|
||||
|
||||
// prefix is 4 bytes from SHA1 hash of the WASM binary
|
||||
// it is used to detect incompatible internal states between different versions of hash-wasm
|
||||
const prefixedState = new Uint8Array(WASM_FUNC_HASH_LENGTH + stateLength);
|
||||
writeHexToUInt8(prefixedState, binary.hash);
|
||||
prefixedState.set(internalState, WASM_FUNC_HASH_LENGTH);
|
||||
return prefixedState;
|
||||
};
|
||||
|
||||
const load = (state: Uint8Array) => {
|
||||
if (!(state instanceof Uint8Array)) {
|
||||
throw new Error("load() expects an Uint8Array generated by save()");
|
||||
}
|
||||
|
||||
const stateOffset: number = wasmInstance.exports.Hash_GetState();
|
||||
const stateLength: number = getStateSize();
|
||||
const overallLength: number = WASM_FUNC_HASH_LENGTH + stateLength;
|
||||
const memoryBuffer = wasmInstance.exports.memory.buffer;
|
||||
|
||||
if (state.length !== overallLength) {
|
||||
throw new Error(
|
||||
`Bad state length (expected ${overallLength} bytes, got ${state.length})`,
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
!hexStringEqualsUInt8(
|
||||
binary.hash,
|
||||
state.subarray(0, WASM_FUNC_HASH_LENGTH),
|
||||
)
|
||||
) {
|
||||
throw new Error(
|
||||
"This state was written by an incompatible hash implementation",
|
||||
);
|
||||
}
|
||||
|
||||
const internalState = state.subarray(WASM_FUNC_HASH_LENGTH);
|
||||
new Uint8Array(memoryBuffer, stateOffset, stateLength).set(internalState);
|
||||
initialized = true;
|
||||
};
|
||||
|
||||
const isDataShort = (data: IDataType) => {
|
||||
if (typeof data === "string") {
|
||||
// worst case is 4 bytes / char
|
||||
return data.length < MAX_HEAP / 4;
|
||||
}
|
||||
|
||||
return data.byteLength < MAX_HEAP;
|
||||
};
|
||||
|
||||
let canSimplify: (data: IDataType, initParam?: number) => boolean =
|
||||
isDataShort;
|
||||
|
||||
switch (binary.name) {
|
||||
case "argon2":
|
||||
case "scrypt":
|
||||
canSimplify = () => true;
|
||||
break;
|
||||
|
||||
case "blake2b":
|
||||
case "blake2s":
|
||||
// if there is a key at blake2 then cannot simplify
|
||||
canSimplify = (data, initParam) => initParam <= 512 && isDataShort(data);
|
||||
break;
|
||||
|
||||
case "blake3":
|
||||
// if there is a key at blake3 then cannot simplify
|
||||
canSimplify = (data, initParam) => initParam === 0 && isDataShort(data);
|
||||
break;
|
||||
|
||||
case "xxhash64": // cannot simplify
|
||||
case "xxhash3":
|
||||
case "xxhash128":
|
||||
case "crc64":
|
||||
canSimplify = () => false;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// shorthand for (init + update + digest) for better performance
|
||||
const calculate = (
|
||||
data: IDataType,
|
||||
initParam = null,
|
||||
digestParam = null,
|
||||
): string => {
|
||||
if (!canSimplify(data, initParam)) {
|
||||
init(initParam);
|
||||
update(data);
|
||||
return digest("hex", digestParam) as string;
|
||||
}
|
||||
|
||||
const buffer = getUInt8Buffer(data);
|
||||
memoryView.set(buffer);
|
||||
wasmInstance.exports.Hash_Calculate(buffer.length, initParam, digestParam);
|
||||
|
||||
return getDigestHex(digestChars, memoryView, hashLength);
|
||||
};
|
||||
|
||||
await setupInterface();
|
||||
|
||||
return {
|
||||
getMemory,
|
||||
writeMemory,
|
||||
getExports,
|
||||
setMemorySize,
|
||||
init,
|
||||
update,
|
||||
digest,
|
||||
save,
|
||||
load,
|
||||
calculate,
|
||||
hashLength,
|
||||
};
|
||||
}
|
||||
|
||||
export type IWASMInterface = ThenArg<ReturnType<typeof WASMInterface>>;
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
import wasmJson from "../wasm/adler32.wasm.json";
|
||||
import {
|
||||
type IHasher,
|
||||
type IWASMInterface,
|
||||
WASMInterface,
|
||||
} from "./WASMInterface";
|
||||
import lockedCreate from "./lockedCreate";
|
||||
import Mutex from "./mutex";
|
||||
import type { IDataType } from "./util";
|
||||
|
||||
const mutex = new Mutex();
|
||||
let wasmCache: IWASMInterface = null;
|
||||
|
||||
/**
|
||||
* Calculates Adler-32 hash. The resulting 32-bit hash is stored in
|
||||
* network byte order (big-endian).
|
||||
*
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export function adler32(data: IDataType): Promise<string> {
|
||||
if (wasmCache === null) {
|
||||
return lockedCreate(mutex, wasmJson, 4).then((wasm) => {
|
||||
wasmCache = wasm;
|
||||
return wasmCache.calculate(data);
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const hash = wasmCache.calculate(data);
|
||||
return Promise.resolve(hash);
|
||||
} catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Adler-32 hash instance
|
||||
*/
|
||||
export function createAdler32(): Promise<IHasher> {
|
||||
return WASMInterface(wasmJson, 4).then((wasm) => {
|
||||
wasm.init();
|
||||
const obj: IHasher = {
|
||||
init: () => {
|
||||
wasm.init();
|
||||
return obj;
|
||||
},
|
||||
update: (data) => {
|
||||
wasm.update(data);
|
||||
return obj;
|
||||
},
|
||||
// biome-ignore lint/suspicious/noExplicitAny: Conflict with IHasher type
|
||||
digest: (outputType) => wasm.digest(outputType) as any,
|
||||
save: () => wasm.save(),
|
||||
load: (data) => {
|
||||
wasm.load(data);
|
||||
return obj;
|
||||
},
|
||||
blockSize: 4,
|
||||
digestSize: 4,
|
||||
};
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
+396
@@ -0,0 +1,396 @@
|
||||
import wasmJson from "../wasm/argon2.wasm.json";
|
||||
import { type IHasher, WASMInterface } from "./WASMInterface";
|
||||
import { createBLAKE2b } from "./blake2b";
|
||||
import {
|
||||
type IDataType,
|
||||
decodeBase64,
|
||||
encodeBase64,
|
||||
getDecodeBase64Length,
|
||||
getDigestHex,
|
||||
getUInt8Buffer,
|
||||
writeHexToUInt8,
|
||||
} from "./util";
|
||||
|
||||
export interface IArgon2Options {
|
||||
/**
|
||||
* Password (or message) to be hashed
|
||||
*/
|
||||
password: IDataType;
|
||||
/**
|
||||
* Salt (usually containing random bytes)
|
||||
*/
|
||||
salt: IDataType;
|
||||
/**
|
||||
* Secret for keyed hashing
|
||||
*/
|
||||
secret?: IDataType;
|
||||
/**
|
||||
* Number of iterations to perform
|
||||
*/
|
||||
iterations: number;
|
||||
/**
|
||||
* Degree of parallelism
|
||||
*/
|
||||
parallelism: number;
|
||||
/**
|
||||
* Amount of memory to be used in kibibytes (1024 bytes)
|
||||
*/
|
||||
memorySize: number;
|
||||
/**
|
||||
* Output size in bytes
|
||||
*/
|
||||
hashLength: number;
|
||||
/**
|
||||
* Desired output type. Defaults to 'hex'
|
||||
*/
|
||||
outputType?: "hex" | "binary" | "encoded";
|
||||
}
|
||||
|
||||
interface IArgon2OptionsExtended extends IArgon2Options {
|
||||
hashType: "i" | "d" | "id";
|
||||
}
|
||||
|
||||
function encodeResult(
|
||||
salt: Uint8Array,
|
||||
options: IArgon2OptionsExtended,
|
||||
res: Uint8Array,
|
||||
): string {
|
||||
const parameters = [
|
||||
`m=${options.memorySize}`,
|
||||
`t=${options.iterations}`,
|
||||
`p=${options.parallelism}`,
|
||||
].join(",");
|
||||
|
||||
return `$argon2${options.hashType}$v=19$${parameters}$${encodeBase64(
|
||||
salt,
|
||||
false,
|
||||
)}$${encodeBase64(res, false)}`;
|
||||
}
|
||||
|
||||
const uint32View = new DataView(new ArrayBuffer(4));
|
||||
function int32LE(x: number): Uint8Array {
|
||||
uint32View.setInt32(0, x, true);
|
||||
return new Uint8Array(uint32View.buffer);
|
||||
}
|
||||
|
||||
async function hashFunc(
|
||||
blake512: IHasher,
|
||||
buf: Uint8Array,
|
||||
len: number,
|
||||
): Promise<Uint8Array> {
|
||||
if (len <= 64) {
|
||||
const blake = await createBLAKE2b(len * 8);
|
||||
blake.update(int32LE(len));
|
||||
blake.update(buf);
|
||||
return blake.digest("binary");
|
||||
}
|
||||
|
||||
const r = Math.ceil(len / 32) - 2;
|
||||
const ret = new Uint8Array(len);
|
||||
|
||||
blake512.init();
|
||||
blake512.update(int32LE(len));
|
||||
blake512.update(buf);
|
||||
let vp = blake512.digest("binary");
|
||||
ret.set(vp.subarray(0, 32), 0);
|
||||
|
||||
for (let i = 1; i < r; i++) {
|
||||
blake512.init();
|
||||
blake512.update(vp);
|
||||
vp = blake512.digest("binary");
|
||||
ret.set(vp.subarray(0, 32), i * 32);
|
||||
}
|
||||
|
||||
const partialBytesNeeded = len - 32 * r;
|
||||
|
||||
let blakeSmall: IHasher;
|
||||
if (partialBytesNeeded === 64) {
|
||||
blakeSmall = blake512;
|
||||
blakeSmall.init();
|
||||
} else {
|
||||
blakeSmall = await createBLAKE2b(partialBytesNeeded * 8);
|
||||
}
|
||||
|
||||
blakeSmall.update(vp);
|
||||
vp = blakeSmall.digest("binary");
|
||||
ret.set(vp.subarray(0, partialBytesNeeded), r * 32);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function getHashType(type: IArgon2OptionsExtended["hashType"]): number {
|
||||
switch (type) {
|
||||
case "d":
|
||||
return 0;
|
||||
case "i":
|
||||
return 1;
|
||||
default:
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
async function argon2Internal(
|
||||
options: IArgon2OptionsExtended,
|
||||
): Promise<string | Uint8Array> {
|
||||
const { parallelism, iterations, hashLength } = options;
|
||||
const password = getUInt8Buffer(options.password);
|
||||
const salt = getUInt8Buffer(options.salt);
|
||||
const version = 0x13;
|
||||
const hashType = getHashType(options.hashType);
|
||||
const { memorySize } = options; // in KB
|
||||
const secret = getUInt8Buffer(options.secret ?? "");
|
||||
|
||||
const [argon2Interface, blake512] = await Promise.all([
|
||||
WASMInterface(wasmJson, 1024),
|
||||
createBLAKE2b(512),
|
||||
]);
|
||||
|
||||
// last block is for storing the init vector
|
||||
argon2Interface.setMemorySize(memorySize * 1024 + 1024);
|
||||
|
||||
const initVector = new Uint8Array(24);
|
||||
const initVectorView = new DataView(initVector.buffer);
|
||||
initVectorView.setInt32(0, parallelism, true);
|
||||
initVectorView.setInt32(4, hashLength, true);
|
||||
initVectorView.setInt32(8, memorySize, true);
|
||||
initVectorView.setInt32(12, iterations, true);
|
||||
initVectorView.setInt32(16, version, true);
|
||||
initVectorView.setInt32(20, hashType, true);
|
||||
argon2Interface.writeMemory(initVector, memorySize * 1024);
|
||||
|
||||
blake512.init();
|
||||
blake512.update(initVector);
|
||||
blake512.update(int32LE(password.length));
|
||||
blake512.update(password);
|
||||
blake512.update(int32LE(salt.length));
|
||||
blake512.update(salt);
|
||||
blake512.update(int32LE(secret.length));
|
||||
blake512.update(secret);
|
||||
blake512.update(int32LE(0)); // associatedData length + associatedData
|
||||
|
||||
const segments = Math.floor(memorySize / (parallelism * 4)); // length of each lane
|
||||
const lanes = segments * 4;
|
||||
|
||||
const param = new Uint8Array(72);
|
||||
const H0 = blake512.digest("binary");
|
||||
param.set(H0);
|
||||
|
||||
for (let lane = 0; lane < parallelism; lane++) {
|
||||
param.set(int32LE(0), 64);
|
||||
param.set(int32LE(lane), 68);
|
||||
|
||||
let position = lane * lanes;
|
||||
let chunk = await hashFunc(blake512, param, 1024);
|
||||
argon2Interface.writeMemory(chunk, position * 1024);
|
||||
|
||||
position += 1;
|
||||
param.set(int32LE(1), 64);
|
||||
chunk = await hashFunc(blake512, param, 1024);
|
||||
argon2Interface.writeMemory(chunk, position * 1024);
|
||||
}
|
||||
|
||||
const C = new Uint8Array(1024);
|
||||
writeHexToUInt8(C, argon2Interface.calculate(new Uint8Array([]), memorySize));
|
||||
|
||||
const res = await hashFunc(blake512, C, hashLength);
|
||||
|
||||
if (options.outputType === "hex") {
|
||||
const digestChars = new Uint8Array(hashLength * 2);
|
||||
return getDigestHex(digestChars, res, hashLength);
|
||||
}
|
||||
|
||||
if (options.outputType === "encoded") {
|
||||
return encodeResult(salt, options, res);
|
||||
}
|
||||
|
||||
// return binary format
|
||||
return res;
|
||||
}
|
||||
|
||||
const validateOptions = (options: IArgon2Options) => {
|
||||
if (!options || typeof options !== "object") {
|
||||
throw new Error("Invalid options parameter. It requires an object.");
|
||||
}
|
||||
|
||||
if (!options.password) {
|
||||
throw new Error("Password must be specified");
|
||||
}
|
||||
|
||||
options.password = getUInt8Buffer(options.password);
|
||||
if (options.password.length < 1) {
|
||||
throw new Error("Password must be specified");
|
||||
}
|
||||
|
||||
if (!options.salt) {
|
||||
throw new Error("Salt must be specified");
|
||||
}
|
||||
|
||||
options.salt = getUInt8Buffer(options.salt);
|
||||
if (options.salt.length < 8) {
|
||||
throw new Error("Salt should be at least 8 bytes long");
|
||||
}
|
||||
|
||||
options.secret = getUInt8Buffer(options.secret ?? "");
|
||||
|
||||
if (!Number.isInteger(options.iterations) || options.iterations < 1) {
|
||||
throw new Error("Iterations should be a positive number");
|
||||
}
|
||||
|
||||
if (!Number.isInteger(options.parallelism) || options.parallelism < 1) {
|
||||
throw new Error("Parallelism should be a positive number");
|
||||
}
|
||||
|
||||
if (!Number.isInteger(options.hashLength) || options.hashLength < 4) {
|
||||
throw new Error("Hash length should be at least 4 bytes.");
|
||||
}
|
||||
|
||||
if (!Number.isInteger(options.memorySize)) {
|
||||
throw new Error("Memory size should be specified.");
|
||||
}
|
||||
|
||||
if (options.memorySize < 8 * options.parallelism) {
|
||||
throw new Error("Memory size should be at least 8 * parallelism.");
|
||||
}
|
||||
|
||||
if (options.outputType === undefined) {
|
||||
options.outputType = "hex";
|
||||
}
|
||||
|
||||
if (!["hex", "binary", "encoded"].includes(options.outputType)) {
|
||||
throw new Error(
|
||||
`Insupported output type ${options.outputType}. Valid values: ['hex', 'binary', 'encoded']`,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
interface IArgon2OptionsBinary {
|
||||
outputType: "binary";
|
||||
}
|
||||
|
||||
type Argon2ReturnType<T> = T extends IArgon2OptionsBinary ? Uint8Array : string;
|
||||
|
||||
/**
|
||||
* Calculates hash using the argon2i password-hashing function
|
||||
* @returns Computed hash
|
||||
*/
|
||||
export async function argon2i<T extends IArgon2Options>(
|
||||
options: T,
|
||||
): Promise<Argon2ReturnType<T>> {
|
||||
validateOptions(options);
|
||||
|
||||
return argon2Internal({
|
||||
...options,
|
||||
hashType: "i",
|
||||
}) as Promise<Argon2ReturnType<T>>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates hash using the argon2id password-hashing function
|
||||
* @returns Computed hash
|
||||
*/
|
||||
export async function argon2id<T extends IArgon2Options>(
|
||||
options: T,
|
||||
): Promise<Argon2ReturnType<T>> {
|
||||
validateOptions(options);
|
||||
|
||||
return argon2Internal({
|
||||
...options,
|
||||
hashType: "id",
|
||||
}) as Promise<Argon2ReturnType<T>>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates hash using the argon2d password-hashing function
|
||||
* @returns Computed hash
|
||||
*/
|
||||
export async function argon2d<T extends IArgon2Options>(
|
||||
options: T,
|
||||
): Promise<Argon2ReturnType<T>> {
|
||||
validateOptions(options);
|
||||
|
||||
return argon2Internal({
|
||||
...options,
|
||||
hashType: "d",
|
||||
}) as Promise<Argon2ReturnType<T>>;
|
||||
}
|
||||
|
||||
export interface Argon2VerifyOptions {
|
||||
/**
|
||||
* Password to be verified
|
||||
*/
|
||||
password: IDataType;
|
||||
/**
|
||||
* Secret used on hash creation
|
||||
*/
|
||||
secret?: IDataType;
|
||||
/**
|
||||
* A previously generated argon2 hash in the 'encoded' output format
|
||||
*/
|
||||
hash: string;
|
||||
}
|
||||
|
||||
const getHashParameters = (
|
||||
password: IDataType,
|
||||
encoded: string,
|
||||
secret?: IDataType,
|
||||
): IArgon2OptionsExtended => {
|
||||
const regex =
|
||||
/^\$argon2(id|i|d)\$v=([0-9]+)\$((?:[mtp]=[0-9]+,){2}[mtp]=[0-9]+)\$([A-Za-z0-9+/]+)\$([A-Za-z0-9+/]+)$/;
|
||||
const match = encoded.match(regex);
|
||||
if (!match) {
|
||||
throw new Error("Invalid hash");
|
||||
}
|
||||
|
||||
const [, hashType, version, parameters, salt, hash] = match;
|
||||
if (version !== "19") {
|
||||
throw new Error(`Unsupported version: ${version}`);
|
||||
}
|
||||
|
||||
const parsedParameters: Partial<IArgon2Options> = {};
|
||||
const paramMap = { m: "memorySize", p: "parallelism", t: "iterations" };
|
||||
for (const x of parameters.split(",")) {
|
||||
const [n, v] = x.split("=");
|
||||
parsedParameters[paramMap[n]] = Number(v);
|
||||
}
|
||||
|
||||
return {
|
||||
...parsedParameters,
|
||||
password,
|
||||
secret,
|
||||
hashType: hashType as IArgon2OptionsExtended["hashType"],
|
||||
salt: decodeBase64(salt),
|
||||
hashLength: getDecodeBase64Length(hash),
|
||||
outputType: "encoded",
|
||||
} as IArgon2OptionsExtended;
|
||||
};
|
||||
|
||||
const validateVerifyOptions = (options: Argon2VerifyOptions) => {
|
||||
if (!options || typeof options !== "object") {
|
||||
throw new Error("Invalid options parameter. It requires an object.");
|
||||
}
|
||||
|
||||
if (options.hash === undefined || typeof options.hash !== "string") {
|
||||
throw new Error("Hash should be specified");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Verifies password using the argon2 password-hashing function
|
||||
* @returns True if the encoded hash matches the password
|
||||
*/
|
||||
export async function argon2Verify(
|
||||
options: Argon2VerifyOptions,
|
||||
): Promise<boolean> {
|
||||
validateVerifyOptions(options);
|
||||
|
||||
const params = getHashParameters(
|
||||
options.password,
|
||||
options.hash,
|
||||
options.secret,
|
||||
);
|
||||
validateOptions(params);
|
||||
|
||||
const hashStart = options.hash.lastIndexOf("$") + 1;
|
||||
const result = (await argon2Internal(params)) as string;
|
||||
return result.substring(hashStart) === options.hash.substring(hashStart);
|
||||
}
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
import wasmJson from "../wasm/bcrypt.wasm.json";
|
||||
import { WASMInterface } from "./WASMInterface";
|
||||
import {
|
||||
type IDataType,
|
||||
getDigestHex,
|
||||
getUInt8Buffer,
|
||||
intArrayToString,
|
||||
} from "./util";
|
||||
|
||||
export interface BcryptOptions {
|
||||
/**
|
||||
* Password to be hashed
|
||||
*/
|
||||
password: IDataType;
|
||||
/**
|
||||
* Salt (16 bytes long - usually containing random bytes)
|
||||
*/
|
||||
salt: IDataType;
|
||||
/**
|
||||
* Number of iterations to perform (4 - 31)
|
||||
*/
|
||||
costFactor: number;
|
||||
/**
|
||||
* Desired output type. Defaults to 'encoded'
|
||||
*/
|
||||
outputType?: "hex" | "binary" | "encoded";
|
||||
}
|
||||
|
||||
async function bcryptInternal(
|
||||
options: BcryptOptions,
|
||||
): Promise<string | Uint8Array> {
|
||||
const { costFactor, password, salt } = options;
|
||||
|
||||
const bcryptInterface = await WASMInterface(wasmJson, 0);
|
||||
bcryptInterface.writeMemory(getUInt8Buffer(salt), 0);
|
||||
const passwordBuffer = getUInt8Buffer(password);
|
||||
bcryptInterface.writeMemory(passwordBuffer, 16);
|
||||
const shouldEncode = options.outputType === "encoded" ? 1 : 0;
|
||||
bcryptInterface
|
||||
.getExports()
|
||||
.bcrypt(passwordBuffer.length, costFactor, shouldEncode);
|
||||
const memory = bcryptInterface.getMemory();
|
||||
|
||||
if (options.outputType === "encoded") {
|
||||
return intArrayToString(memory, 60);
|
||||
}
|
||||
|
||||
if (options.outputType === "hex") {
|
||||
const digestChars = new Uint8Array(24 * 2);
|
||||
return getDigestHex(digestChars, memory, 24);
|
||||
}
|
||||
|
||||
// return binary format
|
||||
// the data is copied to allow GC of the original memory buffer
|
||||
return memory.slice(0, 24);
|
||||
}
|
||||
|
||||
const validateOptions = (options: BcryptOptions) => {
|
||||
if (!options || typeof options !== "object") {
|
||||
throw new Error("Invalid options parameter. It requires an object.");
|
||||
}
|
||||
|
||||
if (
|
||||
!Number.isInteger(options.costFactor) ||
|
||||
options.costFactor < 4 ||
|
||||
options.costFactor > 31
|
||||
) {
|
||||
throw new Error("Cost factor should be a number between 4 and 31");
|
||||
}
|
||||
|
||||
options.password = getUInt8Buffer(options.password);
|
||||
if (options.password.length < 1) {
|
||||
throw new Error("Password should be at least 1 byte long");
|
||||
}
|
||||
|
||||
if (options.password.length > 72) {
|
||||
throw new Error("Password should be at most 72 bytes long");
|
||||
}
|
||||
|
||||
options.salt = getUInt8Buffer(options.salt);
|
||||
if (options.salt.length !== 16) {
|
||||
throw new Error("Salt should be 16 bytes long");
|
||||
}
|
||||
|
||||
if (options.outputType === undefined) {
|
||||
options.outputType = "encoded";
|
||||
}
|
||||
|
||||
if (!["hex", "binary", "encoded"].includes(options.outputType)) {
|
||||
throw new Error(
|
||||
`Insupported output type ${options.outputType}. Valid values: ['hex', 'binary', 'encoded']`,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
interface IBcryptOptionsBinary {
|
||||
outputType: "binary";
|
||||
}
|
||||
|
||||
type BcryptReturnType<T> = T extends IBcryptOptionsBinary ? Uint8Array : string;
|
||||
|
||||
/**
|
||||
* Calculates hash using the bcrypt password-hashing function
|
||||
* @returns Computed hash
|
||||
*/
|
||||
export async function bcrypt<T extends BcryptOptions>(
|
||||
options: T,
|
||||
): Promise<BcryptReturnType<T>> {
|
||||
validateOptions(options);
|
||||
|
||||
return bcryptInternal(options) as Promise<BcryptReturnType<T>>;
|
||||
}
|
||||
|
||||
export interface BcryptVerifyOptions {
|
||||
/**
|
||||
* Password to be verified
|
||||
*/
|
||||
password: IDataType;
|
||||
/**
|
||||
* A previously generated bcrypt hash in the 'encoded' output format
|
||||
*/
|
||||
hash: string;
|
||||
}
|
||||
|
||||
const validateHashCharacters = (hash: string): boolean => {
|
||||
if (!/^\$2[axyb]\$[0-3][0-9]\$[./A-Za-z0-9]{53}$/.test(hash)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hash[4] === "0" && Number(hash[5]) < 4) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hash[4] === "3" && Number(hash[5]) > 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const validateVerifyOptions = (options: BcryptVerifyOptions) => {
|
||||
if (!options || typeof options !== "object") {
|
||||
throw new Error("Invalid options parameter. It requires an object.");
|
||||
}
|
||||
|
||||
if (options.hash === undefined || typeof options.hash !== "string") {
|
||||
throw new Error("Hash should be specified");
|
||||
}
|
||||
|
||||
if (options.hash.length !== 60) {
|
||||
throw new Error("Hash should be 60 bytes long");
|
||||
}
|
||||
|
||||
if (!validateHashCharacters(options.hash)) {
|
||||
throw new Error("Invalid hash");
|
||||
}
|
||||
|
||||
options.password = getUInt8Buffer(options.password);
|
||||
if (options.password.length < 1) {
|
||||
throw new Error("Password should be at least 1 byte long");
|
||||
}
|
||||
|
||||
if (options.password.length > 72) {
|
||||
throw new Error("Password should be at most 72 bytes long");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Verifies password using bcrypt password-hashing function
|
||||
* @returns True if the encoded hash matches the password
|
||||
*/
|
||||
export async function bcryptVerify(
|
||||
options: BcryptVerifyOptions,
|
||||
): Promise<boolean> {
|
||||
validateVerifyOptions(options);
|
||||
|
||||
const { hash, password } = options;
|
||||
|
||||
const bcryptInterface = await WASMInterface(wasmJson, 0);
|
||||
bcryptInterface.writeMemory(getUInt8Buffer(hash), 0);
|
||||
|
||||
const passwordBuffer = getUInt8Buffer(password);
|
||||
bcryptInterface.writeMemory(passwordBuffer, 60);
|
||||
|
||||
return !!bcryptInterface.getExports().bcrypt_verify(passwordBuffer.length);
|
||||
}
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
import wasmJson from "../wasm/blake2b.wasm.json";
|
||||
import {
|
||||
type IHasher,
|
||||
type IWASMInterface,
|
||||
WASMInterface,
|
||||
} from "./WASMInterface";
|
||||
import lockedCreate from "./lockedCreate";
|
||||
import Mutex from "./mutex";
|
||||
import { type IDataType, getUInt8Buffer } from "./util";
|
||||
|
||||
const mutex = new Mutex();
|
||||
let wasmCache: IWASMInterface = null;
|
||||
|
||||
function validateBits(bits: number) {
|
||||
if (!Number.isInteger(bits) || bits < 8 || bits > 512 || bits % 8 !== 0) {
|
||||
return new Error("Invalid variant! Valid values: 8, 16, ..., 512");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getInitParam(outputBits, keyBits) {
|
||||
return outputBits | (keyBits << 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates BLAKE2b hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @param bits Number of output bits, which has to be a number
|
||||
* divisible by 8, between 8 and 512. Defaults to 512.
|
||||
* @param key Optional key (string, Buffer or TypedArray). Maximum length is 64 bytes.
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export function blake2b(
|
||||
data: IDataType,
|
||||
bits = 512,
|
||||
key: IDataType = null,
|
||||
): Promise<string> {
|
||||
if (validateBits(bits)) {
|
||||
return Promise.reject(validateBits(bits));
|
||||
}
|
||||
|
||||
let keyBuffer = null;
|
||||
let initParam = bits;
|
||||
if (key !== null) {
|
||||
keyBuffer = getUInt8Buffer(key);
|
||||
if (keyBuffer.length > 64) {
|
||||
return Promise.reject(new Error("Max key length is 64 bytes"));
|
||||
}
|
||||
initParam = getInitParam(bits, keyBuffer.length);
|
||||
}
|
||||
|
||||
const hashLength = bits / 8;
|
||||
|
||||
if (wasmCache === null || wasmCache.hashLength !== hashLength) {
|
||||
return lockedCreate(mutex, wasmJson, hashLength).then((wasm) => {
|
||||
wasmCache = wasm;
|
||||
if (initParam > 512) {
|
||||
wasmCache.writeMemory(keyBuffer);
|
||||
}
|
||||
return wasmCache.calculate(data, initParam);
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
if (initParam > 512) {
|
||||
wasmCache.writeMemory(keyBuffer);
|
||||
}
|
||||
const hash = wasmCache.calculate(data, initParam);
|
||||
return Promise.resolve(hash);
|
||||
} catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new BLAKE2b hash instance
|
||||
* @param bits Number of output bits, which has to be a number
|
||||
* divisible by 8, between 8 and 512. Defaults to 512.
|
||||
* @param key Optional key (string, Buffer or TypedArray). Maximum length is 64 bytes.
|
||||
*/
|
||||
export function createBLAKE2b(
|
||||
bits = 512,
|
||||
key: IDataType = null,
|
||||
): Promise<IHasher> {
|
||||
if (validateBits(bits)) {
|
||||
return Promise.reject(validateBits(bits));
|
||||
}
|
||||
|
||||
let keyBuffer = null;
|
||||
let initParam = bits;
|
||||
if (key !== null) {
|
||||
keyBuffer = getUInt8Buffer(key);
|
||||
if (keyBuffer.length > 64) {
|
||||
return Promise.reject(new Error("Max key length is 64 bytes"));
|
||||
}
|
||||
initParam = getInitParam(bits, keyBuffer.length);
|
||||
}
|
||||
|
||||
const outputSize = bits / 8;
|
||||
|
||||
return WASMInterface(wasmJson, outputSize).then((wasm) => {
|
||||
if (initParam > 512) {
|
||||
wasm.writeMemory(keyBuffer);
|
||||
}
|
||||
wasm.init(initParam);
|
||||
|
||||
const obj: IHasher = {
|
||||
init:
|
||||
initParam > 512
|
||||
? () => {
|
||||
wasm.writeMemory(keyBuffer);
|
||||
wasm.init(initParam);
|
||||
return obj;
|
||||
}
|
||||
: () => {
|
||||
wasm.init(initParam);
|
||||
return obj;
|
||||
},
|
||||
update: (data) => {
|
||||
wasm.update(data);
|
||||
return obj;
|
||||
},
|
||||
// biome-ignore lint/suspicious/noExplicitAny: Conflict with IHasher type
|
||||
digest: (outputType) => wasm.digest(outputType) as any,
|
||||
save: () => wasm.save(),
|
||||
load: (data) => {
|
||||
wasm.load(data);
|
||||
return obj;
|
||||
},
|
||||
blockSize: 128,
|
||||
digestSize: outputSize,
|
||||
};
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
import wasmJson from "../wasm/blake2s.wasm.json";
|
||||
import {
|
||||
type IHasher,
|
||||
type IWASMInterface,
|
||||
WASMInterface,
|
||||
} from "./WASMInterface";
|
||||
import lockedCreate from "./lockedCreate";
|
||||
import Mutex from "./mutex";
|
||||
import { type IDataType, getUInt8Buffer } from "./util";
|
||||
|
||||
const mutex = new Mutex();
|
||||
let wasmCache: IWASMInterface = null;
|
||||
|
||||
function validateBits(bits: number) {
|
||||
if (!Number.isInteger(bits) || bits < 8 || bits > 256 || bits % 8 !== 0) {
|
||||
return new Error("Invalid variant! Valid values: 8, 16, ..., 256");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getInitParam(outputBits, keyBits) {
|
||||
return outputBits | (keyBits << 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates BLAKE2s hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @param bits Number of output bits, which has to be a number
|
||||
* divisible by 8, between 8 and 256. Defaults to 256.
|
||||
* @param key Optional key (string, Buffer or TypedArray). Maximum length is 32 bytes.
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export function blake2s(
|
||||
data: IDataType,
|
||||
bits = 256,
|
||||
key: IDataType = null,
|
||||
): Promise<string> {
|
||||
if (validateBits(bits)) {
|
||||
return Promise.reject(validateBits(bits));
|
||||
}
|
||||
|
||||
let keyBuffer = null;
|
||||
let initParam = bits;
|
||||
if (key !== null) {
|
||||
keyBuffer = getUInt8Buffer(key);
|
||||
if (keyBuffer.length > 32) {
|
||||
return Promise.reject(new Error("Max key length is 32 bytes"));
|
||||
}
|
||||
initParam = getInitParam(bits, keyBuffer.length);
|
||||
}
|
||||
|
||||
const hashLength = bits / 8;
|
||||
|
||||
if (wasmCache === null || wasmCache.hashLength !== hashLength) {
|
||||
return lockedCreate(mutex, wasmJson, hashLength).then((wasm) => {
|
||||
wasmCache = wasm;
|
||||
if (initParam > 512) {
|
||||
wasmCache.writeMemory(keyBuffer);
|
||||
}
|
||||
return wasmCache.calculate(data, initParam);
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
if (initParam > 512) {
|
||||
wasmCache.writeMemory(keyBuffer);
|
||||
}
|
||||
const hash = wasmCache.calculate(data, initParam);
|
||||
return Promise.resolve(hash);
|
||||
} catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new BLAKE2s hash instance
|
||||
* @param bits Number of output bits, which has to be a number
|
||||
* divisible by 8, between 8 and 256. Defaults to 256.
|
||||
* @param key Optional key (string, Buffer or TypedArray). Maximum length is 32 bytes.
|
||||
*/
|
||||
export function createBLAKE2s(
|
||||
bits = 256,
|
||||
key: IDataType = null,
|
||||
): Promise<IHasher> {
|
||||
if (validateBits(bits)) {
|
||||
return Promise.reject(validateBits(bits));
|
||||
}
|
||||
|
||||
let keyBuffer = null;
|
||||
let initParam = bits;
|
||||
if (key !== null) {
|
||||
keyBuffer = getUInt8Buffer(key);
|
||||
if (keyBuffer.length > 32) {
|
||||
return Promise.reject(new Error("Max key length is 32 bytes"));
|
||||
}
|
||||
initParam = getInitParam(bits, keyBuffer.length);
|
||||
}
|
||||
|
||||
const outputSize = bits / 8;
|
||||
|
||||
return WASMInterface(wasmJson, outputSize).then((wasm) => {
|
||||
if (initParam > 512) {
|
||||
wasm.writeMemory(keyBuffer);
|
||||
}
|
||||
wasm.init(initParam);
|
||||
|
||||
const obj: IHasher = {
|
||||
init:
|
||||
initParam > 512
|
||||
? () => {
|
||||
wasm.writeMemory(keyBuffer);
|
||||
wasm.init(initParam);
|
||||
return obj;
|
||||
}
|
||||
: () => {
|
||||
wasm.init(initParam);
|
||||
return obj;
|
||||
},
|
||||
update: (data) => {
|
||||
wasm.update(data);
|
||||
return obj;
|
||||
},
|
||||
// biome-ignore lint/suspicious/noExplicitAny: Conflict with IHasher type
|
||||
digest: (outputType) => wasm.digest(outputType) as any,
|
||||
save: () => wasm.save(),
|
||||
load: (data) => {
|
||||
wasm.load(data);
|
||||
return obj;
|
||||
},
|
||||
blockSize: 64,
|
||||
digestSize: outputSize,
|
||||
};
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
import wasmJson from "../wasm/blake3.wasm.json";
|
||||
import {
|
||||
type IHasher,
|
||||
type IWASMInterface,
|
||||
WASMInterface,
|
||||
} from "./WASMInterface";
|
||||
import lockedCreate from "./lockedCreate";
|
||||
import Mutex from "./mutex";
|
||||
import { type IDataType, getUInt8Buffer } from "./util";
|
||||
|
||||
const mutex = new Mutex();
|
||||
let wasmCache: IWASMInterface = null;
|
||||
|
||||
function validateBits(bits: number) {
|
||||
if (!Number.isInteger(bits) || bits < 8 || bits % 8 !== 0) {
|
||||
return new Error("Invalid variant! Valid values: 8, 16, ...");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates BLAKE3 hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @param bits Number of output bits, which has to be a number
|
||||
* divisible by 8. Defaults to 256.
|
||||
* @param key Optional key (string, Buffer or TypedArray). Length should be 32 bytes.
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export function blake3(
|
||||
data: IDataType,
|
||||
bits = 256,
|
||||
key: IDataType = null,
|
||||
): Promise<string> {
|
||||
if (validateBits(bits)) {
|
||||
return Promise.reject(validateBits(bits));
|
||||
}
|
||||
|
||||
let keyBuffer = null;
|
||||
let initParam = 0; // key is empty by default
|
||||
if (key !== null) {
|
||||
keyBuffer = getUInt8Buffer(key);
|
||||
if (keyBuffer.length !== 32) {
|
||||
return Promise.reject(new Error("Key length must be exactly 32 bytes"));
|
||||
}
|
||||
initParam = 32;
|
||||
}
|
||||
|
||||
const hashLength = bits / 8;
|
||||
const digestParam = hashLength;
|
||||
|
||||
if (wasmCache === null || wasmCache.hashLength !== hashLength) {
|
||||
return lockedCreate(mutex, wasmJson, hashLength).then((wasm) => {
|
||||
wasmCache = wasm;
|
||||
if (initParam === 32) {
|
||||
wasmCache.writeMemory(keyBuffer);
|
||||
}
|
||||
return wasmCache.calculate(data, initParam, digestParam);
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
if (initParam === 32) {
|
||||
wasmCache.writeMemory(keyBuffer);
|
||||
}
|
||||
const hash = wasmCache.calculate(data, initParam, digestParam);
|
||||
return Promise.resolve(hash);
|
||||
} catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new BLAKE3 hash instance
|
||||
* @param bits Number of output bits, which has to be a number
|
||||
* divisible by 8. Defaults to 256.
|
||||
* @param key Optional key (string, Buffer or TypedArray). Length should be 32 bytes.
|
||||
*/
|
||||
export function createBLAKE3(
|
||||
bits = 256,
|
||||
key: IDataType = null,
|
||||
): Promise<IHasher> {
|
||||
if (validateBits(bits)) {
|
||||
return Promise.reject(validateBits(bits));
|
||||
}
|
||||
|
||||
let keyBuffer = null;
|
||||
let initParam = 0; // key is empty by default
|
||||
if (key !== null) {
|
||||
keyBuffer = getUInt8Buffer(key);
|
||||
if (keyBuffer.length !== 32) {
|
||||
return Promise.reject(new Error("Key length must be exactly 32 bytes"));
|
||||
}
|
||||
initParam = 32;
|
||||
}
|
||||
|
||||
const outputSize = bits / 8;
|
||||
const digestParam = outputSize;
|
||||
|
||||
return WASMInterface(wasmJson, outputSize).then((wasm) => {
|
||||
if (initParam === 32) {
|
||||
wasm.writeMemory(keyBuffer);
|
||||
}
|
||||
wasm.init(initParam);
|
||||
|
||||
const obj: IHasher = {
|
||||
init:
|
||||
initParam === 32
|
||||
? () => {
|
||||
wasm.writeMemory(keyBuffer);
|
||||
wasm.init(initParam);
|
||||
return obj;
|
||||
}
|
||||
: () => {
|
||||
wasm.init(initParam);
|
||||
return obj;
|
||||
},
|
||||
update: (data) => {
|
||||
wasm.update(data);
|
||||
return obj;
|
||||
},
|
||||
// biome-ignore lint/suspicious/noExplicitAny: Conflict with IHasher type
|
||||
digest: (outputType) => wasm.digest(outputType, digestParam) as any,
|
||||
save: () => wasm.save(),
|
||||
load: (data) => {
|
||||
wasm.load(data);
|
||||
return obj;
|
||||
},
|
||||
blockSize: 64,
|
||||
digestSize: outputSize,
|
||||
};
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
import wasmJson from "../wasm/crc32.wasm.json";
|
||||
import {
|
||||
type IHasher,
|
||||
type IWASMInterface,
|
||||
WASMInterface,
|
||||
} from "./WASMInterface";
|
||||
import lockedCreate from "./lockedCreate";
|
||||
import Mutex from "./mutex";
|
||||
import type { IDataType } from "./util";
|
||||
|
||||
const mutex = new Mutex();
|
||||
let wasmCache: IWASMInterface = null;
|
||||
|
||||
function validatePoly(poly: number) {
|
||||
if (!Number.isInteger(poly) || poly < 0 || poly > 0xffffffff) {
|
||||
return new Error("Polynomial must be a valid 32-bit long unsigned integer");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates CRC-32 hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @param polynomial Input polynomial (defaults to 0xedb88320, for CRC32C use 0x82f63b78)
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export function crc32(
|
||||
data: IDataType,
|
||||
polynomial = 0xedb88320,
|
||||
): Promise<string> {
|
||||
if (validatePoly(polynomial)) {
|
||||
return Promise.reject(validatePoly(polynomial));
|
||||
}
|
||||
|
||||
if (wasmCache === null) {
|
||||
return lockedCreate(mutex, wasmJson, 4).then((wasm) => {
|
||||
wasmCache = wasm;
|
||||
return wasmCache.calculate(data, polynomial);
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const hash = wasmCache.calculate(data, polynomial);
|
||||
return Promise.resolve(hash);
|
||||
} catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new CRC-32 hash instance
|
||||
* @param polynomial Input polynomial (defaults to 0xedb88320, for CRC32C use 0x82f63b78)
|
||||
*/
|
||||
export function createCRC32(polynomial = 0xedb88320): Promise<IHasher> {
|
||||
if (validatePoly(polynomial)) {
|
||||
return Promise.reject(validatePoly(polynomial));
|
||||
}
|
||||
|
||||
return WASMInterface(wasmJson, 4).then((wasm) => {
|
||||
wasm.init(polynomial);
|
||||
const obj: IHasher = {
|
||||
init: () => {
|
||||
wasm.init(polynomial);
|
||||
return obj;
|
||||
},
|
||||
update: (data) => {
|
||||
wasm.update(data);
|
||||
return obj;
|
||||
},
|
||||
// biome-ignore lint/suspicious/noExplicitAny: Conflict with IHasher type
|
||||
digest: (outputType) => wasm.digest(outputType) as any,
|
||||
save: () => wasm.save(),
|
||||
load: (data) => {
|
||||
wasm.load(data);
|
||||
return obj;
|
||||
},
|
||||
blockSize: 4,
|
||||
digestSize: 4,
|
||||
};
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
import wasmJson from "../wasm/crc64.wasm.json";
|
||||
import {
|
||||
type IHasher,
|
||||
type IWASMInterface,
|
||||
WASMInterface,
|
||||
} from "./WASMInterface";
|
||||
import lockedCreate from "./lockedCreate";
|
||||
import Mutex from "./mutex";
|
||||
import type { IDataType } from "./util";
|
||||
|
||||
const mutex = new Mutex();
|
||||
let wasmCache: IWASMInterface = null;
|
||||
const polyBuffer = new Uint8Array(8);
|
||||
|
||||
function parsePoly(poly: string) {
|
||||
const errText = "Polynomial must be provided as a 16 char long hex string";
|
||||
|
||||
if (typeof poly !== "string" || poly.length !== 16) {
|
||||
return { hi: 0, lo: 0, err: new Error(errText) };
|
||||
}
|
||||
|
||||
const hi = Number(`0x${poly.slice(0, 8)}`);
|
||||
const lo = Number(`0x${poly.slice(8)}`);
|
||||
|
||||
if (Number.isNaN(hi) || Number.isNaN(lo)) {
|
||||
return { hi, lo, err: new Error(errText) };
|
||||
}
|
||||
|
||||
return { hi, lo, err: null };
|
||||
}
|
||||
|
||||
function writePoly(arr: ArrayBuffer, lo: number, hi: number) {
|
||||
// write in little-endian format
|
||||
const buffer = new DataView(arr);
|
||||
buffer.setUint32(0, lo, true);
|
||||
buffer.setUint32(4, hi, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates CRC-64 hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @param polynomial Input polynomial (defaults to 'c96c5795d7870f42' - ECMA)
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export function crc64(
|
||||
data: IDataType,
|
||||
polynomial = "c96c5795d7870f42",
|
||||
): Promise<string> {
|
||||
const { hi, lo, err } = parsePoly(polynomial);
|
||||
if (err !== null) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
|
||||
if (wasmCache === null) {
|
||||
return lockedCreate(mutex, wasmJson, 8).then((wasm) => {
|
||||
wasmCache = wasm;
|
||||
writePoly(polyBuffer.buffer, lo, hi);
|
||||
wasmCache.writeMemory(polyBuffer);
|
||||
return wasmCache.calculate(data);
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
writePoly(polyBuffer.buffer, lo, hi);
|
||||
wasmCache.writeMemory(polyBuffer);
|
||||
const hash = wasmCache.calculate(data);
|
||||
return Promise.resolve(hash);
|
||||
} catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new CRC-64 hash instance
|
||||
* @param polynomial Input polynomial (defaults to 'c96c5795d7870f42' - ECMA)
|
||||
*/
|
||||
export function createCRC64(polynomial = "c96c5795d7870f42"): Promise<IHasher> {
|
||||
const { hi, lo, err } = parsePoly(polynomial);
|
||||
if (err !== null) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
|
||||
return WASMInterface(wasmJson, 8).then((wasm) => {
|
||||
const instanceBuffer = new Uint8Array(8);
|
||||
writePoly(instanceBuffer.buffer, lo, hi);
|
||||
wasm.writeMemory(instanceBuffer);
|
||||
wasm.init();
|
||||
const obj: IHasher = {
|
||||
init: () => {
|
||||
wasm.writeMemory(instanceBuffer);
|
||||
wasm.init();
|
||||
return obj;
|
||||
},
|
||||
update: (data) => {
|
||||
wasm.update(data);
|
||||
return obj;
|
||||
},
|
||||
// biome-ignore lint/suspicious/noExplicitAny: Conflict with IHasher type
|
||||
digest: (outputType) => wasm.digest(outputType) as any,
|
||||
save: () => wasm.save(),
|
||||
load: (data) => {
|
||||
wasm.load(data);
|
||||
return obj;
|
||||
},
|
||||
blockSize: 8,
|
||||
digestSize: 8,
|
||||
};
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
import type { IHasher } from "./WASMInterface";
|
||||
import { type IDataType, getUInt8Buffer } from "./util";
|
||||
|
||||
function calculateKeyBuffer(hasher: IHasher, key: IDataType): Uint8Array {
|
||||
const { blockSize } = hasher;
|
||||
|
||||
const buf = getUInt8Buffer(key);
|
||||
|
||||
if (buf.length > blockSize) {
|
||||
hasher.update(buf);
|
||||
const uintArr = hasher.digest("binary");
|
||||
hasher.init();
|
||||
return uintArr;
|
||||
}
|
||||
|
||||
return new Uint8Array(buf.buffer, buf.byteOffset, buf.length);
|
||||
}
|
||||
|
||||
function calculateHmac(hasher: IHasher, key: IDataType): IHasher {
|
||||
hasher.init();
|
||||
|
||||
const { blockSize } = hasher;
|
||||
const keyBuf = calculateKeyBuffer(hasher, key);
|
||||
const keyBuffer = new Uint8Array(blockSize);
|
||||
keyBuffer.set(keyBuf);
|
||||
|
||||
const opad = new Uint8Array(blockSize);
|
||||
|
||||
for (let i = 0; i < blockSize; i++) {
|
||||
const v = keyBuffer[i];
|
||||
opad[i] = v ^ 0x5c;
|
||||
keyBuffer[i] = v ^ 0x36;
|
||||
}
|
||||
|
||||
hasher.update(keyBuffer);
|
||||
|
||||
const obj: IHasher = {
|
||||
init: () => {
|
||||
hasher.init();
|
||||
hasher.update(keyBuffer);
|
||||
return obj;
|
||||
},
|
||||
|
||||
update: (data: IDataType) => {
|
||||
hasher.update(data);
|
||||
return obj;
|
||||
},
|
||||
|
||||
digest: ((outputType) => {
|
||||
const uintArr = hasher.digest("binary");
|
||||
hasher.init();
|
||||
hasher.update(opad);
|
||||
hasher.update(uintArr);
|
||||
return hasher.digest(outputType);
|
||||
// biome-ignore lint/suspicious/noExplicitAny: Conflict with IHasher type
|
||||
}) as any,
|
||||
save: () => {
|
||||
throw new Error("save() not supported");
|
||||
},
|
||||
load: () => {
|
||||
throw new Error("load() not supported");
|
||||
},
|
||||
|
||||
blockSize: hasher.blockSize,
|
||||
digestSize: hasher.digestSize,
|
||||
};
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates HMAC hash
|
||||
* @param hash Hash algorithm to use. It has to be the return value of a function like createSHA1()
|
||||
* @param key Key (string, Buffer or TypedArray)
|
||||
*/
|
||||
export function createHMAC(
|
||||
hash: Promise<IHasher>,
|
||||
key: IDataType,
|
||||
): Promise<IHasher> {
|
||||
if (!hash || !hash.then) {
|
||||
throw new Error(
|
||||
'Invalid hash function is provided! Usage: createHMAC(createMD5(), "key").',
|
||||
);
|
||||
}
|
||||
|
||||
return hash.then((hasher) => calculateHmac(hasher, key));
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
export * from "./adler32";
|
||||
export * from "./argon2";
|
||||
export * from "./blake2b";
|
||||
export * from "./blake2s";
|
||||
export * from "./blake3";
|
||||
export * from "./crc32";
|
||||
export * from "./crc64";
|
||||
export * from "./md4";
|
||||
export * from "./md5";
|
||||
export * from "./sha1";
|
||||
export * from "./sha3";
|
||||
export * from "./keccak";
|
||||
export * from "./sha224";
|
||||
export * from "./sha256";
|
||||
export * from "./sha384";
|
||||
export * from "./sha512";
|
||||
export * from "./xxhash32";
|
||||
export * from "./xxhash64";
|
||||
export * from "./xxhash3";
|
||||
export * from "./xxhash128";
|
||||
export * from "./ripemd160";
|
||||
export * from "./hmac";
|
||||
export * from "./pbkdf2";
|
||||
export * from "./scrypt";
|
||||
export * from "./bcrypt";
|
||||
export * from "./whirlpool";
|
||||
export * from "./sm3";
|
||||
|
||||
export type { IDataType } from "./util";
|
||||
export type { IHasher } from "./WASMInterface";
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
import wasmJson from "../wasm/sha3.wasm.json";
|
||||
import {
|
||||
type IHasher,
|
||||
type IWASMInterface,
|
||||
WASMInterface,
|
||||
} from "./WASMInterface";
|
||||
import lockedCreate from "./lockedCreate";
|
||||
import Mutex from "./mutex";
|
||||
import type { IDataType } from "./util";
|
||||
|
||||
type IValidBits = 224 | 256 | 384 | 512;
|
||||
const mutex = new Mutex();
|
||||
let wasmCache: IWASMInterface = null;
|
||||
|
||||
function validateBits(bits: IValidBits) {
|
||||
if (![224, 256, 384, 512].includes(bits)) {
|
||||
return new Error("Invalid variant! Valid values: 224, 256, 384, 512");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates Keccak hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @param bits Number of output bits. Valid values: 224, 256, 384, 512
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export function keccak(
|
||||
data: IDataType,
|
||||
bits: IValidBits = 512,
|
||||
): Promise<string> {
|
||||
if (validateBits(bits)) {
|
||||
return Promise.reject(validateBits(bits));
|
||||
}
|
||||
|
||||
const hashLength = bits / 8;
|
||||
|
||||
if (wasmCache === null || wasmCache.hashLength !== hashLength) {
|
||||
return lockedCreate(mutex, wasmJson, hashLength).then((wasm) => {
|
||||
wasmCache = wasm;
|
||||
return wasmCache.calculate(data, bits, 0x01);
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const hash = wasmCache.calculate(data, bits, 0x01);
|
||||
return Promise.resolve(hash);
|
||||
} catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Keccak hash instance
|
||||
* @param bits Number of output bits. Valid values: 224, 256, 384, 512
|
||||
*/
|
||||
export function createKeccak(bits: IValidBits = 512): Promise<IHasher> {
|
||||
if (validateBits(bits)) {
|
||||
return Promise.reject(validateBits(bits));
|
||||
}
|
||||
|
||||
const outputSize = bits / 8;
|
||||
|
||||
return WASMInterface(wasmJson, outputSize).then((wasm) => {
|
||||
wasm.init(bits);
|
||||
const obj: IHasher = {
|
||||
init: () => {
|
||||
wasm.init(bits);
|
||||
return obj;
|
||||
},
|
||||
update: (data) => {
|
||||
wasm.update(data);
|
||||
return obj;
|
||||
},
|
||||
// biome-ignore lint/suspicious/noExplicitAny: Conflict with IHasher type
|
||||
digest: (outputType) => wasm.digest(outputType, 0x01) as any,
|
||||
save: () => wasm.save(),
|
||||
load: (data) => {
|
||||
wasm.load(data);
|
||||
return obj;
|
||||
},
|
||||
blockSize: 200 - 2 * outputSize,
|
||||
digestSize: outputSize,
|
||||
};
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import { type IWASMInterface, WASMInterface } from "./WASMInterface";
|
||||
import type Mutex from "./mutex";
|
||||
import type { IEmbeddedWasm } from "./util";
|
||||
|
||||
export default async function lockedCreate(
|
||||
mutex: Mutex,
|
||||
binary: IEmbeddedWasm,
|
||||
hashLength: number,
|
||||
): Promise<IWASMInterface> {
|
||||
const unlock = await mutex.lock();
|
||||
const wasm = await WASMInterface(binary, hashLength);
|
||||
unlock();
|
||||
return wasm;
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
import wasmJson from "../wasm/md4.wasm.json";
|
||||
import {
|
||||
type IHasher,
|
||||
type IWASMInterface,
|
||||
WASMInterface,
|
||||
} from "./WASMInterface";
|
||||
import lockedCreate from "./lockedCreate";
|
||||
import Mutex from "./mutex";
|
||||
import type { IDataType } from "./util";
|
||||
|
||||
const mutex = new Mutex();
|
||||
let wasmCache: IWASMInterface = null;
|
||||
|
||||
/**
|
||||
* Calculates MD4 hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export function md4(data: IDataType): Promise<string> {
|
||||
if (wasmCache === null) {
|
||||
return lockedCreate(mutex, wasmJson, 16).then((wasm) => {
|
||||
wasmCache = wasm;
|
||||
return wasmCache.calculate(data);
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const hash = wasmCache.calculate(data);
|
||||
return Promise.resolve(hash);
|
||||
} catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new MD4 hash instance
|
||||
*/
|
||||
export function createMD4(): Promise<IHasher> {
|
||||
return WASMInterface(wasmJson, 16).then((wasm) => {
|
||||
wasm.init();
|
||||
const obj: IHasher = {
|
||||
init: () => {
|
||||
wasm.init();
|
||||
return obj;
|
||||
},
|
||||
update: (data) => {
|
||||
wasm.update(data);
|
||||
return obj;
|
||||
},
|
||||
// biome-ignore lint/suspicious/noExplicitAny: Conflict with IHasher type
|
||||
digest: (outputType) => wasm.digest(outputType) as any,
|
||||
save: () => wasm.save(),
|
||||
load: (data) => {
|
||||
wasm.load(data);
|
||||
return obj;
|
||||
},
|
||||
blockSize: 64,
|
||||
digestSize: 16,
|
||||
};
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
import wasmJson from "../wasm/md5.wasm.json";
|
||||
import {
|
||||
type IHasher,
|
||||
type IWASMInterface,
|
||||
WASMInterface,
|
||||
} from "./WASMInterface";
|
||||
import lockedCreate from "./lockedCreate";
|
||||
import Mutex from "./mutex";
|
||||
import type { IDataType } from "./util";
|
||||
|
||||
const mutex = new Mutex();
|
||||
let wasmCache: IWASMInterface = null;
|
||||
|
||||
/**
|
||||
* Calculates MD5 hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export function md5(data: IDataType): Promise<string> {
|
||||
if (wasmCache === null) {
|
||||
return lockedCreate(mutex, wasmJson, 16).then((wasm) => {
|
||||
wasmCache = wasm;
|
||||
return wasmCache.calculate(data);
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const hash = wasmCache.calculate(data);
|
||||
return Promise.resolve(hash);
|
||||
} catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new MD5 hash instance
|
||||
*/
|
||||
export function createMD5(): Promise<IHasher> {
|
||||
return WASMInterface(wasmJson, 16).then((wasm) => {
|
||||
wasm.init();
|
||||
const obj: IHasher = {
|
||||
init: () => {
|
||||
wasm.init();
|
||||
return obj;
|
||||
},
|
||||
update: (data) => {
|
||||
wasm.update(data);
|
||||
return obj;
|
||||
},
|
||||
// biome-ignore lint/suspicious/noExplicitAny: Conflict with IHasher type
|
||||
digest: (outputType) => wasm.digest(outputType) as any,
|
||||
save: () => wasm.save(),
|
||||
load: (data) => {
|
||||
wasm.load(data);
|
||||
return obj;
|
||||
},
|
||||
blockSize: 64,
|
||||
digestSize: 16,
|
||||
};
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
class Mutex {
|
||||
private mutex = Promise.resolve();
|
||||
|
||||
lock(): PromiseLike<() => void> {
|
||||
let begin: (unlock: () => void) => void = () => {};
|
||||
|
||||
this.mutex = this.mutex.then(() => new Promise(begin));
|
||||
|
||||
return new Promise((res) => {
|
||||
begin = res;
|
||||
});
|
||||
}
|
||||
|
||||
async dispatch<T>(fn: () => PromiseLike<T>): Promise<T> {
|
||||
const unlock = await this.lock();
|
||||
try {
|
||||
return await Promise.resolve(fn());
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Mutex;
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
import type { IHasher } from "./WASMInterface";
|
||||
import { createHMAC } from "./hmac";
|
||||
import { type IDataType, getDigestHex, getUInt8Buffer } from "./util";
|
||||
|
||||
export interface IPBKDF2Options {
|
||||
/**
|
||||
* Password (or message) to be hashed
|
||||
*/
|
||||
password: IDataType;
|
||||
/**
|
||||
* Salt (usually containing random bytes)
|
||||
*/
|
||||
salt: IDataType;
|
||||
/**
|
||||
* Number of iterations to perform
|
||||
*/
|
||||
iterations: number;
|
||||
/**
|
||||
* Output size in bytes
|
||||
*/
|
||||
hashLength: number;
|
||||
/**
|
||||
* Hash algorithm to use. It has to be the return value of a function like createSHA1()
|
||||
*/
|
||||
hashFunction: Promise<IHasher>;
|
||||
/**
|
||||
* Desired output type. Defaults to 'hex'
|
||||
*/
|
||||
outputType?: "hex" | "binary";
|
||||
}
|
||||
|
||||
async function calculatePBKDF2(
|
||||
digest: IHasher,
|
||||
salt: IDataType,
|
||||
iterations: number,
|
||||
hashLength: number,
|
||||
outputType?: "hex" | "binary",
|
||||
): Promise<Uint8Array | string> {
|
||||
const DK = new Uint8Array(hashLength);
|
||||
const block1 = new Uint8Array(salt.length + 4);
|
||||
const block1View = new DataView(block1.buffer);
|
||||
const saltBuffer = getUInt8Buffer(salt);
|
||||
const saltUIntBuffer = new Uint8Array(
|
||||
saltBuffer.buffer,
|
||||
saltBuffer.byteOffset,
|
||||
saltBuffer.length,
|
||||
);
|
||||
block1.set(saltUIntBuffer);
|
||||
|
||||
let destPos = 0;
|
||||
const hLen = digest.digestSize;
|
||||
const l = Math.ceil(hashLength / hLen);
|
||||
|
||||
let T: Uint8Array = null;
|
||||
let U: Uint8Array = null;
|
||||
|
||||
for (let i = 1; i <= l; i++) {
|
||||
block1View.setUint32(salt.length, i);
|
||||
|
||||
digest.init();
|
||||
digest.update(block1);
|
||||
T = digest.digest("binary");
|
||||
U = T.slice();
|
||||
|
||||
for (let j = 1; j < iterations; j++) {
|
||||
digest.init();
|
||||
digest.update(U);
|
||||
U = digest.digest("binary");
|
||||
for (let k = 0; k < hLen; k++) {
|
||||
T[k] ^= U[k];
|
||||
}
|
||||
}
|
||||
|
||||
DK.set(T.subarray(0, hashLength - destPos), destPos);
|
||||
destPos += hLen;
|
||||
}
|
||||
|
||||
if (outputType === "binary") {
|
||||
return DK;
|
||||
}
|
||||
|
||||
const digestChars = new Uint8Array(hashLength * 2);
|
||||
return getDigestHex(digestChars, DK, hashLength);
|
||||
}
|
||||
|
||||
const validateOptions = (options: IPBKDF2Options) => {
|
||||
if (!options || typeof options !== "object") {
|
||||
throw new Error("Invalid options parameter. It requires an object.");
|
||||
}
|
||||
|
||||
if (!options.hashFunction || !options.hashFunction.then) {
|
||||
throw new Error(
|
||||
'Invalid hash function is provided! Usage: pbkdf2("password", "salt", 1000, 32, createSHA1()).',
|
||||
);
|
||||
}
|
||||
|
||||
if (!Number.isInteger(options.iterations) || options.iterations < 1) {
|
||||
throw new Error("Iterations should be a positive number");
|
||||
}
|
||||
|
||||
if (!Number.isInteger(options.hashLength) || options.hashLength < 1) {
|
||||
throw new Error("Hash length should be a positive number");
|
||||
}
|
||||
|
||||
if (options.outputType === undefined) {
|
||||
options.outputType = "hex";
|
||||
}
|
||||
|
||||
if (!["hex", "binary"].includes(options.outputType)) {
|
||||
throw new Error(
|
||||
`Insupported output type ${options.outputType}. Valid values: ['hex', 'binary']`,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
interface IPBKDF2OptionsBinary {
|
||||
outputType: "binary";
|
||||
}
|
||||
|
||||
type PBKDF2ReturnType<T> = T extends IPBKDF2OptionsBinary ? Uint8Array : string;
|
||||
|
||||
/**
|
||||
* Generates a new PBKDF2 hash for the supplied password
|
||||
*/
|
||||
export async function pbkdf2<T extends IPBKDF2Options>(
|
||||
options: T,
|
||||
): Promise<PBKDF2ReturnType<T>> {
|
||||
validateOptions(options);
|
||||
|
||||
const hmac = await createHMAC(options.hashFunction, options.password);
|
||||
return calculatePBKDF2(
|
||||
hmac,
|
||||
options.salt,
|
||||
options.iterations,
|
||||
options.hashLength,
|
||||
options.outputType,
|
||||
) as Promise<PBKDF2ReturnType<T>>;
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
import wasmJson from "../wasm/ripemd160.wasm.json";
|
||||
import {
|
||||
type IHasher,
|
||||
type IWASMInterface,
|
||||
WASMInterface,
|
||||
} from "./WASMInterface";
|
||||
import lockedCreate from "./lockedCreate";
|
||||
import Mutex from "./mutex";
|
||||
import type { IDataType } from "./util";
|
||||
|
||||
const mutex = new Mutex();
|
||||
let wasmCache: IWASMInterface = null;
|
||||
|
||||
/**
|
||||
* Calculates RIPEMD-160 hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export function ripemd160(data: IDataType): Promise<string> {
|
||||
if (wasmCache === null) {
|
||||
return lockedCreate(mutex, wasmJson, 20).then((wasm) => {
|
||||
wasmCache = wasm;
|
||||
return wasmCache.calculate(data);
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const hash = wasmCache.calculate(data);
|
||||
return Promise.resolve(hash);
|
||||
} catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new RIPEMD-160 hash instance
|
||||
*/
|
||||
export function createRIPEMD160(): Promise<IHasher> {
|
||||
return WASMInterface(wasmJson, 20).then((wasm) => {
|
||||
wasm.init();
|
||||
const obj: IHasher = {
|
||||
init: () => {
|
||||
wasm.init();
|
||||
return obj;
|
||||
},
|
||||
update: (data) => {
|
||||
wasm.update(data);
|
||||
return obj;
|
||||
},
|
||||
// biome-ignore lint/suspicious/noExplicitAny: Conflict with IHasher type
|
||||
digest: (outputType) => wasm.digest(outputType) as any,
|
||||
save: () => wasm.save(),
|
||||
load: (data) => {
|
||||
wasm.load(data);
|
||||
return obj;
|
||||
},
|
||||
blockSize: 64,
|
||||
digestSize: 20,
|
||||
};
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
import wasmJson from "../wasm/scrypt.wasm.json";
|
||||
import { WASMInterface } from "./WASMInterface";
|
||||
import { pbkdf2 } from "./pbkdf2";
|
||||
import { createSHA256 } from "./sha256";
|
||||
import { type IDataType, getDigestHex } from "./util";
|
||||
|
||||
export interface ScryptOptions {
|
||||
/**
|
||||
* Password (or message) to be hashed
|
||||
*/
|
||||
password: IDataType;
|
||||
/**
|
||||
* Salt (usually containing random bytes)
|
||||
*/
|
||||
salt: IDataType;
|
||||
/**
|
||||
* CPU / memory cost - must be a power of 2 (e.g. 1024)
|
||||
*/
|
||||
costFactor: number;
|
||||
/**
|
||||
* Block size (8 is commonly used)
|
||||
*/
|
||||
blockSize: number;
|
||||
/**
|
||||
* Degree of parallelism
|
||||
*/
|
||||
parallelism: number;
|
||||
/**
|
||||
* Output size in bytes
|
||||
*/
|
||||
hashLength: number;
|
||||
/**
|
||||
* Output data type. Defaults to hexadecimal string
|
||||
*/
|
||||
outputType?: "hex" | "binary";
|
||||
}
|
||||
|
||||
async function scryptInternal(
|
||||
options: ScryptOptions,
|
||||
): Promise<string | Uint8Array> {
|
||||
const { costFactor, blockSize, parallelism, hashLength } = options;
|
||||
const SHA256Hasher = createSHA256();
|
||||
|
||||
const blockData = await pbkdf2({
|
||||
password: options.password,
|
||||
salt: options.salt,
|
||||
iterations: 1,
|
||||
hashLength: 128 * blockSize * parallelism,
|
||||
hashFunction: SHA256Hasher,
|
||||
outputType: "binary",
|
||||
});
|
||||
|
||||
const scryptInterface = await WASMInterface(wasmJson, 0);
|
||||
|
||||
// last block is for storing the temporary vectors
|
||||
const VSize = 128 * blockSize * costFactor;
|
||||
const XYSize = 256 * blockSize;
|
||||
scryptInterface.setMemorySize(blockData.length + VSize + XYSize);
|
||||
scryptInterface.writeMemory(blockData, 0);
|
||||
|
||||
// mix blocks
|
||||
scryptInterface.getExports().scrypt(blockSize, costFactor, parallelism);
|
||||
|
||||
const expensiveSalt = scryptInterface
|
||||
.getMemory()
|
||||
.subarray(0, 128 * blockSize * parallelism);
|
||||
|
||||
const outputData = await pbkdf2({
|
||||
password: options.password,
|
||||
salt: expensiveSalt,
|
||||
iterations: 1,
|
||||
hashLength,
|
||||
hashFunction: SHA256Hasher,
|
||||
outputType: "binary",
|
||||
});
|
||||
|
||||
if (options.outputType === "hex") {
|
||||
const digestChars = new Uint8Array(hashLength * 2);
|
||||
return getDigestHex(digestChars, outputData, hashLength);
|
||||
}
|
||||
|
||||
// return binary format
|
||||
return outputData;
|
||||
}
|
||||
|
||||
const isPowerOfTwo = (v: number): boolean => v && !(v & (v - 1));
|
||||
|
||||
const validateOptions = (options: ScryptOptions) => {
|
||||
if (!options || typeof options !== "object") {
|
||||
throw new Error("Invalid options parameter. It requires an object.");
|
||||
}
|
||||
|
||||
if (!Number.isInteger(options.blockSize) || options.blockSize < 1) {
|
||||
throw new Error("Block size should be a positive number");
|
||||
}
|
||||
|
||||
if (
|
||||
!Number.isInteger(options.costFactor) ||
|
||||
options.costFactor < 2 ||
|
||||
!isPowerOfTwo(options.costFactor)
|
||||
) {
|
||||
throw new Error("Cost factor should be a power of 2, greater than 1");
|
||||
}
|
||||
|
||||
if (!Number.isInteger(options.parallelism) || options.parallelism < 1) {
|
||||
throw new Error("Parallelism should be a positive number");
|
||||
}
|
||||
|
||||
if (!Number.isInteger(options.hashLength) || options.hashLength < 1) {
|
||||
throw new Error("Hash length should be a positive number.");
|
||||
}
|
||||
|
||||
if (options.outputType === undefined) {
|
||||
options.outputType = "hex";
|
||||
}
|
||||
|
||||
if (!["hex", "binary"].includes(options.outputType)) {
|
||||
throw new Error(
|
||||
`Insupported output type ${options.outputType}. Valid values: ['hex', 'binary']`,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
interface IScryptOptionsBinary {
|
||||
outputType: "binary";
|
||||
}
|
||||
|
||||
type ScryptReturnType<T> = T extends IScryptOptionsBinary ? Uint8Array : string;
|
||||
|
||||
/**
|
||||
* Calculates hash using the scrypt password-based key derivation function
|
||||
* @returns Computed hash as a hexadecimal string or as
|
||||
* Uint8Array depending on the outputType option
|
||||
*/
|
||||
export async function scrypt<T extends ScryptOptions>(
|
||||
options: T,
|
||||
): Promise<ScryptReturnType<T>> {
|
||||
validateOptions(options);
|
||||
|
||||
return scryptInternal(options) as Promise<ScryptReturnType<T>>;
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
import wasmJson from "../wasm/sha1.wasm.json";
|
||||
import {
|
||||
type IHasher,
|
||||
type IWASMInterface,
|
||||
WASMInterface,
|
||||
} from "./WASMInterface";
|
||||
import lockedCreate from "./lockedCreate";
|
||||
import Mutex from "./mutex";
|
||||
import type { IDataType } from "./util";
|
||||
|
||||
const mutex = new Mutex();
|
||||
let wasmCache: IWASMInterface = null;
|
||||
|
||||
/**
|
||||
* Calculates SHA-1 hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export function sha1(data: IDataType): Promise<string> {
|
||||
if (wasmCache === null) {
|
||||
return lockedCreate(mutex, wasmJson, 20).then((wasm) => {
|
||||
wasmCache = wasm;
|
||||
return wasmCache.calculate(data);
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const hash = wasmCache.calculate(data);
|
||||
return Promise.resolve(hash);
|
||||
} catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new SHA-1 hash instance
|
||||
*/
|
||||
export function createSHA1(): Promise<IHasher> {
|
||||
return WASMInterface(wasmJson, 20).then((wasm) => {
|
||||
wasm.init();
|
||||
const obj: IHasher = {
|
||||
init: () => {
|
||||
wasm.init();
|
||||
return obj;
|
||||
},
|
||||
update: (data) => {
|
||||
wasm.update(data);
|
||||
return obj;
|
||||
},
|
||||
// biome-ignore lint/suspicious/noExplicitAny: Conflict with IHasher type
|
||||
digest: (outputType) => wasm.digest(outputType) as any,
|
||||
save: () => wasm.save(),
|
||||
load: (data) => {
|
||||
wasm.load(data);
|
||||
return obj;
|
||||
},
|
||||
blockSize: 64,
|
||||
digestSize: 20,
|
||||
};
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
import wasmJson from "../wasm/sha256.wasm.json";
|
||||
import {
|
||||
type IHasher,
|
||||
type IWASMInterface,
|
||||
WASMInterface,
|
||||
} from "./WASMInterface";
|
||||
import lockedCreate from "./lockedCreate";
|
||||
import Mutex from "./mutex";
|
||||
import type { IDataType } from "./util";
|
||||
|
||||
const mutex = new Mutex();
|
||||
let wasmCache: IWASMInterface = null;
|
||||
|
||||
/**
|
||||
* Calculates SHA-2 (SHA-224) hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export function sha224(data: IDataType): Promise<string> {
|
||||
if (wasmCache === null) {
|
||||
return lockedCreate(mutex, wasmJson, 28).then((wasm) => {
|
||||
wasmCache = wasm;
|
||||
return wasmCache.calculate(data, 224);
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const hash = wasmCache.calculate(data, 224);
|
||||
return Promise.resolve(hash);
|
||||
} catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new SHA-2 (SHA-224) hash instance
|
||||
*/
|
||||
export function createSHA224(): Promise<IHasher> {
|
||||
return WASMInterface(wasmJson, 28).then((wasm) => {
|
||||
wasm.init(224);
|
||||
const obj: IHasher = {
|
||||
init: () => {
|
||||
wasm.init(224);
|
||||
return obj;
|
||||
},
|
||||
update: (data) => {
|
||||
wasm.update(data);
|
||||
return obj;
|
||||
},
|
||||
// biome-ignore lint/suspicious/noExplicitAny: Conflict with IHasher type
|
||||
digest: (outputType) => wasm.digest(outputType) as any,
|
||||
save: () => wasm.save(),
|
||||
load: (data) => {
|
||||
wasm.load(data);
|
||||
return obj;
|
||||
},
|
||||
blockSize: 64,
|
||||
digestSize: 28,
|
||||
};
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
import wasmJson from "../wasm/sha256.wasm.json";
|
||||
import {
|
||||
type IHasher,
|
||||
type IWASMInterface,
|
||||
WASMInterface,
|
||||
} from "./WASMInterface";
|
||||
import lockedCreate from "./lockedCreate";
|
||||
import Mutex from "./mutex";
|
||||
import type { IDataType } from "./util";
|
||||
|
||||
const mutex = new Mutex();
|
||||
let wasmCache: IWASMInterface = null;
|
||||
|
||||
/**
|
||||
* Calculates SHA-2 (SHA-256) hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export function sha256(data: IDataType): Promise<string> {
|
||||
if (wasmCache === null) {
|
||||
return lockedCreate(mutex, wasmJson, 32).then((wasm) => {
|
||||
wasmCache = wasm;
|
||||
return wasmCache.calculate(data, 256);
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const hash = wasmCache.calculate(data, 256);
|
||||
return Promise.resolve(hash);
|
||||
} catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new SHA-2 (SHA-256) hash instance
|
||||
*/
|
||||
export function createSHA256(): Promise<IHasher> {
|
||||
return WASMInterface(wasmJson, 32).then((wasm) => {
|
||||
wasm.init(256);
|
||||
const obj: IHasher = {
|
||||
init: () => {
|
||||
wasm.init(256);
|
||||
return obj;
|
||||
},
|
||||
update: (data) => {
|
||||
wasm.update(data);
|
||||
return obj;
|
||||
},
|
||||
// biome-ignore lint/suspicious/noExplicitAny: Conflict with IHasher type
|
||||
digest: (outputType) => wasm.digest(outputType) as any,
|
||||
save: () => wasm.save(),
|
||||
load: (data) => {
|
||||
wasm.load(data);
|
||||
return obj;
|
||||
},
|
||||
blockSize: 64,
|
||||
digestSize: 32,
|
||||
};
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
import wasmJson from "../wasm/sha3.wasm.json";
|
||||
import {
|
||||
type IHasher,
|
||||
type IWASMInterface,
|
||||
WASMInterface,
|
||||
} from "./WASMInterface";
|
||||
import lockedCreate from "./lockedCreate";
|
||||
import Mutex from "./mutex";
|
||||
import type { IDataType } from "./util";
|
||||
|
||||
type IValidBits = 224 | 256 | 384 | 512;
|
||||
const mutex = new Mutex();
|
||||
let wasmCache: IWASMInterface = null;
|
||||
|
||||
function validateBits(bits: IValidBits) {
|
||||
if (![224, 256, 384, 512].includes(bits)) {
|
||||
return new Error("Invalid variant! Valid values: 224, 256, 384, 512");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates SHA-3 hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @param bits Number of output bits. Valid values: 224, 256, 384, 512
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export function sha3(data: IDataType, bits: IValidBits = 512): Promise<string> {
|
||||
if (validateBits(bits)) {
|
||||
return Promise.reject(validateBits(bits));
|
||||
}
|
||||
|
||||
const hashLength = bits / 8;
|
||||
|
||||
if (wasmCache === null || wasmCache.hashLength !== hashLength) {
|
||||
return lockedCreate(mutex, wasmJson, hashLength).then((wasm) => {
|
||||
wasmCache = wasm;
|
||||
return wasmCache.calculate(data, bits, 0x06);
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const hash = wasmCache.calculate(data, bits, 0x06);
|
||||
return Promise.resolve(hash);
|
||||
} catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new SHA-3 hash instance
|
||||
* @param bits Number of output bits. Valid values: 224, 256, 384, 512
|
||||
*/
|
||||
export function createSHA3(bits: IValidBits = 512): Promise<IHasher> {
|
||||
if (validateBits(bits)) {
|
||||
return Promise.reject(validateBits(bits));
|
||||
}
|
||||
|
||||
const outputSize = bits / 8;
|
||||
|
||||
return WASMInterface(wasmJson, outputSize).then((wasm) => {
|
||||
wasm.init(bits);
|
||||
const obj: IHasher = {
|
||||
init: () => {
|
||||
wasm.init(bits);
|
||||
return obj;
|
||||
},
|
||||
update: (data) => {
|
||||
wasm.update(data);
|
||||
return obj;
|
||||
},
|
||||
// biome-ignore lint/suspicious/noExplicitAny: Conflict with IHasher type
|
||||
digest: (outputType) => wasm.digest(outputType, 0x06) as any,
|
||||
save: () => wasm.save(),
|
||||
load: (data) => {
|
||||
wasm.load(data);
|
||||
return obj;
|
||||
},
|
||||
blockSize: 200 - 2 * outputSize,
|
||||
digestSize: outputSize,
|
||||
};
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
import wasmJson from "../wasm/sha512.wasm.json";
|
||||
import {
|
||||
type IHasher,
|
||||
type IWASMInterface,
|
||||
WASMInterface,
|
||||
} from "./WASMInterface";
|
||||
import lockedCreate from "./lockedCreate";
|
||||
import Mutex from "./mutex";
|
||||
import type { IDataType } from "./util";
|
||||
|
||||
const mutex = new Mutex();
|
||||
let wasmCache: IWASMInterface = null;
|
||||
|
||||
/**
|
||||
* Calculates SHA-2 (SHA-384) hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export function sha384(data: IDataType): Promise<string> {
|
||||
if (wasmCache === null) {
|
||||
return lockedCreate(mutex, wasmJson, 48).then((wasm) => {
|
||||
wasmCache = wasm;
|
||||
return wasmCache.calculate(data, 384);
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const hash = wasmCache.calculate(data, 384);
|
||||
return Promise.resolve(hash);
|
||||
} catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new SHA-2 (SHA-384) hash instance
|
||||
*/
|
||||
export function createSHA384(): Promise<IHasher> {
|
||||
return WASMInterface(wasmJson, 48).then((wasm) => {
|
||||
wasm.init(384);
|
||||
const obj: IHasher = {
|
||||
init: () => {
|
||||
wasm.init(384);
|
||||
return obj;
|
||||
},
|
||||
update: (data) => {
|
||||
wasm.update(data);
|
||||
return obj;
|
||||
},
|
||||
// biome-ignore lint/suspicious/noExplicitAny: Conflict with IHasher type
|
||||
digest: (outputType) => wasm.digest(outputType) as any,
|
||||
save: () => wasm.save(),
|
||||
load: (data) => {
|
||||
wasm.load(data);
|
||||
return obj;
|
||||
},
|
||||
blockSize: 128,
|
||||
digestSize: 48,
|
||||
};
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
import wasmJson from "../wasm/sha512.wasm.json";
|
||||
import {
|
||||
type IHasher,
|
||||
type IWASMInterface,
|
||||
WASMInterface,
|
||||
} from "./WASMInterface";
|
||||
import lockedCreate from "./lockedCreate";
|
||||
import Mutex from "./mutex";
|
||||
import type { IDataType } from "./util";
|
||||
|
||||
const mutex = new Mutex();
|
||||
let wasmCache: IWASMInterface = null;
|
||||
|
||||
/**
|
||||
* Calculates SHA-2 (SHA-512) hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export function sha512(data: IDataType): Promise<string> {
|
||||
if (wasmCache === null) {
|
||||
return lockedCreate(mutex, wasmJson, 64).then((wasm) => {
|
||||
wasmCache = wasm;
|
||||
return wasmCache.calculate(data, 512);
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const hash = wasmCache.calculate(data, 512);
|
||||
return Promise.resolve(hash);
|
||||
} catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new SHA-2 (SHA-512) hash instance
|
||||
*/
|
||||
export function createSHA512(): Promise<IHasher> {
|
||||
return WASMInterface(wasmJson, 64).then((wasm) => {
|
||||
wasm.init(512);
|
||||
const obj: IHasher = {
|
||||
init: () => {
|
||||
wasm.init(512);
|
||||
return obj;
|
||||
},
|
||||
update: (data) => {
|
||||
wasm.update(data);
|
||||
return obj;
|
||||
},
|
||||
// biome-ignore lint/suspicious/noExplicitAny: Conflict with IHasher type
|
||||
digest: (outputType) => wasm.digest(outputType) as any,
|
||||
save: () => wasm.save(),
|
||||
load: (data) => {
|
||||
wasm.load(data);
|
||||
return obj;
|
||||
},
|
||||
blockSize: 128,
|
||||
digestSize: 64,
|
||||
};
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
import wasmJson from "../wasm/sm3.wasm.json";
|
||||
import {
|
||||
type IHasher,
|
||||
type IWASMInterface,
|
||||
WASMInterface,
|
||||
} from "./WASMInterface";
|
||||
import lockedCreate from "./lockedCreate";
|
||||
import Mutex from "./mutex";
|
||||
import type { IDataType } from "./util";
|
||||
|
||||
const mutex = new Mutex();
|
||||
let wasmCache: IWASMInterface = null;
|
||||
|
||||
/**
|
||||
* Calculates SM3 hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export function sm3(data: IDataType): Promise<string> {
|
||||
if (wasmCache === null) {
|
||||
return lockedCreate(mutex, wasmJson, 32).then((wasm) => {
|
||||
wasmCache = wasm;
|
||||
return wasmCache.calculate(data);
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const hash = wasmCache.calculate(data);
|
||||
return Promise.resolve(hash);
|
||||
} catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new SM3 hash instance
|
||||
*/
|
||||
export function createSM3(): Promise<IHasher> {
|
||||
return WASMInterface(wasmJson, 32).then((wasm) => {
|
||||
wasm.init();
|
||||
const obj: IHasher = {
|
||||
init: () => {
|
||||
wasm.init();
|
||||
return obj;
|
||||
},
|
||||
update: (data) => {
|
||||
wasm.update(data);
|
||||
return obj;
|
||||
},
|
||||
// biome-ignore lint/suspicious/noExplicitAny: Conflict with IHasher type
|
||||
digest: (outputType) => wasm.digest(outputType) as any,
|
||||
save: () => wasm.save(),
|
||||
load: (data) => {
|
||||
wasm.load(data);
|
||||
return obj;
|
||||
},
|
||||
blockSize: 64,
|
||||
digestSize: 32,
|
||||
};
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
function getGlobal() {
|
||||
if (typeof globalThis !== "undefined") return globalThis;
|
||||
if (typeof self !== "undefined") return self;
|
||||
if (typeof window !== "undefined") return window;
|
||||
return global;
|
||||
}
|
||||
|
||||
const globalObject = getGlobal();
|
||||
const nodeBuffer = globalObject.Buffer ?? null;
|
||||
const textEncoder = globalObject.TextEncoder
|
||||
? new globalObject.TextEncoder()
|
||||
: null;
|
||||
|
||||
export type ITypedArray = Uint8Array | Uint16Array | Uint32Array;
|
||||
export type IDataType = string | Buffer | ITypedArray;
|
||||
export type IEmbeddedWasm = { name: string; data: string; hash: string };
|
||||
|
||||
export function intArrayToString(arr: Uint8Array, len: number): string {
|
||||
return String.fromCharCode(...arr.subarray(0, len));
|
||||
}
|
||||
|
||||
function hexCharCodesToInt(a: number, b: number): number {
|
||||
return (
|
||||
(((a & 0xf) + ((a >> 6) | ((a >> 3) & 0x8))) << 4) |
|
||||
((b & 0xf) + ((b >> 6) | ((b >> 3) & 0x8)))
|
||||
);
|
||||
}
|
||||
|
||||
export function writeHexToUInt8(buf: Uint8Array, str: string) {
|
||||
const size = str.length >> 1;
|
||||
for (let i = 0; i < size; i++) {
|
||||
const index = i << 1;
|
||||
buf[i] = hexCharCodesToInt(
|
||||
str.charCodeAt(index),
|
||||
str.charCodeAt(index + 1),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function hexStringEqualsUInt8(str: string, buf: Uint8Array): boolean {
|
||||
if (str.length !== buf.length * 2) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < buf.length; i++) {
|
||||
const strIndex = i << 1;
|
||||
if (
|
||||
buf[i] !==
|
||||
hexCharCodesToInt(str.charCodeAt(strIndex), str.charCodeAt(strIndex + 1))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const alpha = "a".charCodeAt(0) - 10;
|
||||
const digit = "0".charCodeAt(0);
|
||||
export function getDigestHex(
|
||||
tmpBuffer: Uint8Array,
|
||||
input: Uint8Array,
|
||||
hashLength: number,
|
||||
): string {
|
||||
let p = 0;
|
||||
for (let i = 0; i < hashLength; i++) {
|
||||
let nibble = input[i] >>> 4;
|
||||
tmpBuffer[p++] = nibble > 9 ? nibble + alpha : nibble + digit;
|
||||
nibble = input[i] & 0xf;
|
||||
tmpBuffer[p++] = nibble > 9 ? nibble + alpha : nibble + digit;
|
||||
}
|
||||
|
||||
return String.fromCharCode.apply(null, tmpBuffer);
|
||||
}
|
||||
|
||||
export const getUInt8Buffer =
|
||||
nodeBuffer !== null
|
||||
? (data: IDataType): Uint8Array => {
|
||||
if (typeof data === "string") {
|
||||
const buf = nodeBuffer.from(data, "utf8");
|
||||
return new Uint8Array(buf.buffer, buf.byteOffset, buf.length);
|
||||
}
|
||||
|
||||
if (nodeBuffer.isBuffer(data)) {
|
||||
return new Uint8Array(data.buffer, data.byteOffset, data.length);
|
||||
}
|
||||
|
||||
if (ArrayBuffer.isView(data)) {
|
||||
return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
||||
}
|
||||
|
||||
throw new Error("Invalid data type!");
|
||||
}
|
||||
: (data: IDataType): Uint8Array => {
|
||||
if (typeof data === "string") {
|
||||
return textEncoder.encode(data);
|
||||
}
|
||||
|
||||
if (ArrayBuffer.isView(data)) {
|
||||
return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
||||
}
|
||||
|
||||
throw new Error("Invalid data type!");
|
||||
};
|
||||
|
||||
const base64Chars =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
const base64Lookup = new Uint8Array(256);
|
||||
for (let i = 0; i < base64Chars.length; i++) {
|
||||
base64Lookup[base64Chars.charCodeAt(i)] = i;
|
||||
}
|
||||
|
||||
export function encodeBase64(data: Uint8Array, pad = true): string {
|
||||
const len = data.length;
|
||||
const extraBytes = len % 3;
|
||||
const parts = [];
|
||||
|
||||
const len2 = len - extraBytes;
|
||||
for (let i = 0; i < len2; i += 3) {
|
||||
const tmp =
|
||||
((data[i] << 16) & 0xff0000) +
|
||||
((data[i + 1] << 8) & 0xff00) +
|
||||
(data[i + 2] & 0xff);
|
||||
|
||||
const triplet =
|
||||
base64Chars.charAt((tmp >> 18) & 0x3f) +
|
||||
base64Chars.charAt((tmp >> 12) & 0x3f) +
|
||||
base64Chars.charAt((tmp >> 6) & 0x3f) +
|
||||
base64Chars.charAt(tmp & 0x3f);
|
||||
|
||||
parts.push(triplet);
|
||||
}
|
||||
|
||||
if (extraBytes === 1) {
|
||||
const tmp = data[len - 1];
|
||||
const a = base64Chars.charAt(tmp >> 2);
|
||||
const b = base64Chars.charAt((tmp << 4) & 0x3f);
|
||||
|
||||
parts.push(`${a}${b}`);
|
||||
if (pad) {
|
||||
parts.push("==");
|
||||
}
|
||||
} else if (extraBytes === 2) {
|
||||
const tmp = (data[len - 2] << 8) + data[len - 1];
|
||||
const a = base64Chars.charAt(tmp >> 10);
|
||||
const b = base64Chars.charAt((tmp >> 4) & 0x3f);
|
||||
const c = base64Chars.charAt((tmp << 2) & 0x3f);
|
||||
parts.push(`${a}${b}${c}`);
|
||||
if (pad) {
|
||||
parts.push("=");
|
||||
}
|
||||
}
|
||||
|
||||
return parts.join("");
|
||||
}
|
||||
|
||||
export function getDecodeBase64Length(data: string): number {
|
||||
let bufferLength = Math.floor(data.length * 0.75);
|
||||
const len = data.length;
|
||||
|
||||
if (data[len - 1] === "=") {
|
||||
bufferLength -= 1;
|
||||
if (data[len - 2] === "=") {
|
||||
bufferLength -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
return bufferLength;
|
||||
}
|
||||
|
||||
export function decodeBase64(data: string): Uint8Array {
|
||||
const bufferLength = getDecodeBase64Length(data);
|
||||
const len = data.length;
|
||||
|
||||
const bytes = new Uint8Array(bufferLength);
|
||||
|
||||
let p = 0;
|
||||
for (let i = 0; i < len; i += 4) {
|
||||
const encoded1 = base64Lookup[data.charCodeAt(i)];
|
||||
const encoded2 = base64Lookup[data.charCodeAt(i + 1)];
|
||||
const encoded3 = base64Lookup[data.charCodeAt(i + 2)];
|
||||
const encoded4 = base64Lookup[data.charCodeAt(i + 3)];
|
||||
|
||||
bytes[p] = (encoded1 << 2) | (encoded2 >> 4);
|
||||
p += 1;
|
||||
bytes[p] = ((encoded2 & 15) << 4) | (encoded3 >> 2);
|
||||
p += 1;
|
||||
bytes[p] = ((encoded3 & 3) << 6) | (encoded4 & 63);
|
||||
p += 1;
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
import wasmJson from "../wasm/whirlpool.wasm.json";
|
||||
import {
|
||||
type IHasher,
|
||||
type IWASMInterface,
|
||||
WASMInterface,
|
||||
} from "./WASMInterface";
|
||||
import lockedCreate from "./lockedCreate";
|
||||
import Mutex from "./mutex";
|
||||
import type { IDataType } from "./util";
|
||||
|
||||
const mutex = new Mutex();
|
||||
let wasmCache: IWASMInterface = null;
|
||||
|
||||
/**
|
||||
* Calculates Whirlpool hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export function whirlpool(data: IDataType): Promise<string> {
|
||||
if (wasmCache === null) {
|
||||
return lockedCreate(mutex, wasmJson, 64).then((wasm) => {
|
||||
wasmCache = wasm;
|
||||
return wasmCache.calculate(data);
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const hash = wasmCache.calculate(data);
|
||||
return Promise.resolve(hash);
|
||||
} catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Whirlpool hash instance
|
||||
*/
|
||||
export function createWhirlpool(): Promise<IHasher> {
|
||||
return WASMInterface(wasmJson, 64).then((wasm) => {
|
||||
wasm.init();
|
||||
const obj: IHasher = {
|
||||
init: () => {
|
||||
wasm.init();
|
||||
return obj;
|
||||
},
|
||||
update: (data) => {
|
||||
wasm.update(data);
|
||||
return obj;
|
||||
},
|
||||
// biome-ignore lint/suspicious/noExplicitAny: Conflict with IHasher type
|
||||
digest: (outputType) => wasm.digest(outputType) as any,
|
||||
save: () => wasm.save(),
|
||||
load: (data) => {
|
||||
wasm.load(data);
|
||||
return obj;
|
||||
},
|
||||
blockSize: 64,
|
||||
digestSize: 64,
|
||||
};
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
import wasmJson from "../wasm/xxhash128.wasm.json";
|
||||
import {
|
||||
type IHasher,
|
||||
type IWASMInterface,
|
||||
WASMInterface,
|
||||
} from "./WASMInterface";
|
||||
import lockedCreate from "./lockedCreate";
|
||||
import Mutex from "./mutex";
|
||||
import type { IDataType } from "./util";
|
||||
|
||||
const mutex = new Mutex();
|
||||
let wasmCache: IWASMInterface = null;
|
||||
const seedBuffer = new Uint8Array(8);
|
||||
|
||||
function validateSeed(seed: number) {
|
||||
if (!Number.isInteger(seed) || seed < 0 || seed > 0xffffffff) {
|
||||
return new Error(
|
||||
"Seed must be given as two valid 32-bit long unsigned integers (lo + high).",
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function writeSeed(arr: ArrayBuffer, low: number, high: number) {
|
||||
// write in little-endian format
|
||||
const buffer = new DataView(arr);
|
||||
buffer.setUint32(0, low, true);
|
||||
buffer.setUint32(4, high, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates xxHash128 hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @param seedLow Lower 32 bits of the number used to
|
||||
* initialize the internal state of the algorithm (defaults to 0)
|
||||
* @param seedHigh Higher 32 bits of the number used to
|
||||
* initialize the internal state of the algorithm (defaults to 0)
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export function xxhash128(
|
||||
data: IDataType,
|
||||
seedLow = 0,
|
||||
seedHigh = 0,
|
||||
): Promise<string> {
|
||||
if (validateSeed(seedLow)) {
|
||||
return Promise.reject(validateSeed(seedLow));
|
||||
}
|
||||
|
||||
if (validateSeed(seedHigh)) {
|
||||
return Promise.reject(validateSeed(seedHigh));
|
||||
}
|
||||
|
||||
if (wasmCache === null) {
|
||||
return lockedCreate(mutex, wasmJson, 16).then((wasm) => {
|
||||
wasmCache = wasm;
|
||||
writeSeed(seedBuffer.buffer, seedLow, seedHigh);
|
||||
wasmCache.writeMemory(seedBuffer);
|
||||
return wasmCache.calculate(data);
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
writeSeed(seedBuffer.buffer, seedLow, seedHigh);
|
||||
wasmCache.writeMemory(seedBuffer);
|
||||
const hash = wasmCache.calculate(data);
|
||||
return Promise.resolve(hash);
|
||||
} catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new xxHash128 hash instance
|
||||
* @param seedLow Lower 32 bits of the number used to
|
||||
* initialize the internal state of the algorithm (defaults to 0)
|
||||
* @param seedHigh Higher 32 bits of the number used to
|
||||
* initialize the internal state of the algorithm (defaults to 0)
|
||||
*/
|
||||
export function createXXHash128(seedLow = 0, seedHigh = 0): Promise<IHasher> {
|
||||
if (validateSeed(seedLow)) {
|
||||
return Promise.reject(validateSeed(seedLow));
|
||||
}
|
||||
|
||||
if (validateSeed(seedHigh)) {
|
||||
return Promise.reject(validateSeed(seedHigh));
|
||||
}
|
||||
|
||||
return WASMInterface(wasmJson, 16).then((wasm) => {
|
||||
const instanceBuffer = new Uint8Array(8);
|
||||
writeSeed(instanceBuffer.buffer, seedLow, seedHigh);
|
||||
wasm.writeMemory(instanceBuffer);
|
||||
wasm.init();
|
||||
const obj: IHasher = {
|
||||
init: () => {
|
||||
wasm.writeMemory(instanceBuffer);
|
||||
wasm.init();
|
||||
return obj;
|
||||
},
|
||||
update: (data) => {
|
||||
wasm.update(data);
|
||||
return obj;
|
||||
},
|
||||
// biome-ignore lint/suspicious/noExplicitAny: Conflict with IHasher type
|
||||
digest: (outputType) => wasm.digest(outputType) as any,
|
||||
save: () => wasm.save(),
|
||||
load: (data) => {
|
||||
wasm.load(data);
|
||||
return obj;
|
||||
},
|
||||
blockSize: 512,
|
||||
digestSize: 16,
|
||||
};
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
import wasmJson from "../wasm/xxhash3.wasm.json";
|
||||
import {
|
||||
type IHasher,
|
||||
type IWASMInterface,
|
||||
WASMInterface,
|
||||
} from "./WASMInterface";
|
||||
import lockedCreate from "./lockedCreate";
|
||||
import Mutex from "./mutex";
|
||||
import type { IDataType } from "./util";
|
||||
|
||||
const mutex = new Mutex();
|
||||
let wasmCache: IWASMInterface = null;
|
||||
const seedBuffer = new Uint8Array(8);
|
||||
|
||||
function validateSeed(seed: number) {
|
||||
if (!Number.isInteger(seed) || seed < 0 || seed > 0xffffffff) {
|
||||
return new Error(
|
||||
"Seed must be given as two valid 32-bit long unsigned integers (lo + high).",
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function writeSeed(arr: ArrayBuffer, low: number, high: number) {
|
||||
// write in little-endian format
|
||||
const buffer = new DataView(arr);
|
||||
buffer.setUint32(0, low, true);
|
||||
buffer.setUint32(4, high, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates xxHash3 hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @param seedLow Lower 32 bits of the number used to
|
||||
* initialize the internal state of the algorithm (defaults to 0)
|
||||
* @param seedHigh Higher 32 bits of the number used to
|
||||
* initialize the internal state of the algorithm (defaults to 0)
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export function xxhash3(
|
||||
data: IDataType,
|
||||
seedLow = 0,
|
||||
seedHigh = 0,
|
||||
): Promise<string> {
|
||||
if (validateSeed(seedLow)) {
|
||||
return Promise.reject(validateSeed(seedLow));
|
||||
}
|
||||
|
||||
if (validateSeed(seedHigh)) {
|
||||
return Promise.reject(validateSeed(seedHigh));
|
||||
}
|
||||
|
||||
if (wasmCache === null) {
|
||||
return lockedCreate(mutex, wasmJson, 8).then((wasm) => {
|
||||
wasmCache = wasm;
|
||||
writeSeed(seedBuffer.buffer, seedLow, seedHigh);
|
||||
wasmCache.writeMemory(seedBuffer);
|
||||
return wasmCache.calculate(data);
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
writeSeed(seedBuffer.buffer, seedLow, seedHigh);
|
||||
wasmCache.writeMemory(seedBuffer);
|
||||
const hash = wasmCache.calculate(data);
|
||||
return Promise.resolve(hash);
|
||||
} catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new xxHash3 hash instance
|
||||
* @param seedLow Lower 32 bits of the number used to
|
||||
* initialize the internal state of the algorithm (defaults to 0)
|
||||
* @param seedHigh Higher 32 bits of the number used to
|
||||
* initialize the internal state of the algorithm (defaults to 0)
|
||||
*/
|
||||
export function createXXHash3(seedLow = 0, seedHigh = 0): Promise<IHasher> {
|
||||
if (validateSeed(seedLow)) {
|
||||
return Promise.reject(validateSeed(seedLow));
|
||||
}
|
||||
|
||||
if (validateSeed(seedHigh)) {
|
||||
return Promise.reject(validateSeed(seedHigh));
|
||||
}
|
||||
|
||||
return WASMInterface(wasmJson, 8).then((wasm) => {
|
||||
const instanceBuffer = new Uint8Array(8);
|
||||
writeSeed(instanceBuffer.buffer, seedLow, seedHigh);
|
||||
wasm.writeMemory(instanceBuffer);
|
||||
wasm.init();
|
||||
const obj: IHasher = {
|
||||
init: () => {
|
||||
wasm.writeMemory(instanceBuffer);
|
||||
wasm.init();
|
||||
return obj;
|
||||
},
|
||||
update: (data) => {
|
||||
wasm.update(data);
|
||||
return obj;
|
||||
},
|
||||
// biome-ignore lint/suspicious/noExplicitAny: Conflict with IHasher type
|
||||
digest: (outputType) => wasm.digest(outputType) as any,
|
||||
save: () => wasm.save(),
|
||||
load: (data) => {
|
||||
wasm.load(data);
|
||||
return obj;
|
||||
},
|
||||
blockSize: 512,
|
||||
digestSize: 8,
|
||||
};
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
import wasmJson from "../wasm/xxhash32.wasm.json";
|
||||
import {
|
||||
type IHasher,
|
||||
type IWASMInterface,
|
||||
WASMInterface,
|
||||
} from "./WASMInterface";
|
||||
import lockedCreate from "./lockedCreate";
|
||||
import Mutex from "./mutex";
|
||||
import type { IDataType } from "./util";
|
||||
|
||||
const mutex = new Mutex();
|
||||
let wasmCache: IWASMInterface = null;
|
||||
|
||||
function validateSeed(seed: number) {
|
||||
if (!Number.isInteger(seed) || seed < 0 || seed > 0xffffffff) {
|
||||
return new Error("Seed must be a valid 32-bit long unsigned integer.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Calculates xxHash32 hash
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @param seed Number used to initialize the internal state of the algorithm (defaults to 0)
|
||||
* @returns Computed hash as a hexadecimal string
|
||||
*/
|
||||
export function xxhash32(data: IDataType, seed = 0): Promise<string> {
|
||||
if (validateSeed(seed)) {
|
||||
return Promise.reject(validateSeed(seed));
|
||||
}
|
||||
|
||||
if (wasmCache === null) {
|
||||
return lockedCreate(mutex, wasmJson, 4).then((wasm) => {
|
||||
wasmCache = wasm;
|
||||
return wasmCache.calculate(data, seed);
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const hash = wasmCache.calculate(data, seed);
|
||||
return Promise.resolve(hash);
|
||||
} catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new xxHash32 hash instance
|
||||
* @param data Input data (string, Buffer or TypedArray)
|
||||
* @param seed Number used to initialize the internal state of the algorithm (defaults to 0)
|
||||
*/
|
||||
export function createXXHash32(seed = 0): Promise<IHasher> {
|
||||
if (validateSeed(seed)) {
|
||||
return Promise.reject(validateSeed(seed));
|
||||
}
|
||||
|
||||
return WASMInterface(wasmJson, 4).then((wasm) => {
|
||||
wasm.init(seed);
|
||||
const obj: IHasher = {
|
||||
init: () => {
|
||||
wasm.init(seed);
|
||||
return obj;
|
||||
},
|
||||
update: (data) => {
|
||||
wasm.update(data);
|
||||
return obj;
|
||||
},
|
||||
// biome-ignore lint/suspicious/noExplicitAny: Conflict with IHasher type
|
||||
digest: (outputType) => wasm.digest(outputType) as any,
|
||||
save: () => wasm.save(),
|
||||
load: (data) => {
|
||||
wasm.load(data);
|
||||
return obj;
|
||||
},
|
||||
blockSize: 16,
|
||||
digestSize: 4,
|
||||
};
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user