diff --git a/docs/generated/changelog.html b/docs/generated/changelog.html
index 895775af7..025f0f0f8 100644
--- a/docs/generated/changelog.html
+++ b/docs/generated/changelog.html
@@ -12,6 +12,10 @@
Agent-JS Changelog
Version x.x.x
+ -
+ feat: replaces the `js-sha256` library with `@noble/hashes` due to a breaking bug in
+ Chrome
+
- Fix: add `@dfinity/principal` as a peerDependency to `assets` and `candid`.
-
Feat: HttpAgent now uses a default address of https://icp-api.io. Users will be warned for
diff --git a/package-lock.json b/package-lock.json
index 4100fdc67..8289bdfda 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2039,6 +2039,17 @@
"darwin"
]
},
+ "node_modules/@noble/hashes": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz",
+ "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==",
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"dev": true,
@@ -6880,13 +6891,13 @@
}
},
"node_modules/cypress": {
- "version": "12.17.3",
- "resolved": "https://registry.npmjs.org/cypress/-/cypress-12.17.3.tgz",
- "integrity": "sha512-/R4+xdIDjUSLYkiQfwJd630S81KIgicmQOLXotFxVXkl+eTeVO+3bHXxdi5KBh/OgC33HWN33kHX+0tQR/ZWpg==",
+ "version": "12.17.4",
+ "resolved": "https://registry.npmjs.org/cypress/-/cypress-12.17.4.tgz",
+ "integrity": "sha512-gAN8Pmns9MA5eCDFSDJXWKUpaL3IDd89N9TtIupjYnzLSmlpVr+ZR+vb4U/qaMp+lB6tBvAmt7504c3Z4RU5KQ==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
- "@cypress/request": "^2.88.11",
+ "@cypress/request": "2.88.12",
"@cypress/xvfb": "^1.2.4",
"@types/node": "^16.18.39",
"@types/sinonjs__fake-timers": "8.1.1",
@@ -6921,6 +6932,7 @@
"minimist": "^1.2.8",
"ospath": "^1.2.2",
"pretty-bytes": "^5.6.0",
+ "process": "^0.11.10",
"proxy-from-env": "1.0.0",
"request-progress": "^3.0.0",
"semver": "^7.5.3",
@@ -13846,6 +13858,7 @@
"node_modules/pkcs11js": {
"version": "1.3.0",
"dev": true,
+ "hasInstallScript": true,
"license": "MIT",
"dependencies": {
"nan": "^2.15.0"
@@ -17706,9 +17719,9 @@
"version": "0.18.1",
"license": "Apache-2.0",
"dependencies": {
+ "@noble/hashes": "^1.3.1",
"base64-arraybuffer": "^0.2.0",
"borc": "^2.1.1",
- "js-sha256": "0.9.0",
"simple-cbor": "^0.4.1"
},
"devDependencies": {
@@ -17836,7 +17849,7 @@
"peerDependencies": {
"@dfinity/agent": "^0.18.1",
"@dfinity/principal": "^0.18.1",
- "js-sha256": "0.9.0"
+ "@noble/hashes": "^1.3.1"
}
},
"packages/assets/node_modules/@types/mime": {
@@ -18165,8 +18178,8 @@
"version": "0.18.1",
"license": "Apache-2.0",
"dependencies": {
+ "@noble/hashes": "^1.3.1",
"borc": "^2.1.1",
- "js-sha256": "^0.9.0",
"tweetnacl": "^1.0.1"
},
"devDependencies": {
@@ -18199,6 +18212,7 @@
"license": "Apache-2.0",
"dependencies": {
"@dfinity/agent": "^0.18.1",
+ "@noble/hashes": "^1.3.1",
"bip39": "^3.0.4",
"bs58check": "^2.1.2",
"secp256k1": "^4.0.3"
@@ -18286,7 +18300,7 @@
"version": "0.18.1",
"license": "Apache-2.0",
"dependencies": {
- "js-sha256": "^0.9.0"
+ "@noble/hashes": "^1.3.1"
},
"devDependencies": {
"@types/jest": "^28.1.4",
diff --git a/packages/agent/package.json b/packages/agent/package.json
index f3265eeca..070b257f9 100644
--- a/packages/agent/package.json
+++ b/packages/agent/package.json
@@ -54,9 +54,9 @@
"@dfinity/principal": "^0.18.1"
},
"dependencies": {
+ "@noble/hashes": "^1.3.1",
"base64-arraybuffer": "^0.2.0",
"borc": "^2.1.1",
- "js-sha256": "0.9.0",
"simple-cbor": "^0.4.1"
},
"devDependencies": {
diff --git a/packages/agent/src/request_id.ts b/packages/agent/src/request_id.ts
index 032f8ac79..782ea068f 100644
--- a/packages/agent/src/request_id.ts
+++ b/packages/agent/src/request_id.ts
@@ -1,8 +1,8 @@
import { lebEncode } from '@dfinity/candid';
import { Principal } from '@dfinity/principal';
import borc from 'borc';
-import { sha256 as jsSha256 } from 'js-sha256';
-import { compare, concat } from './utils/buffer';
+import { sha256 } from '@noble/hashes/sha256';
+import { compare, concat, uint8ToBuf } from './utils/buffer';
export type RequestId = ArrayBuffer & { __requestId__: void };
@@ -11,7 +11,7 @@ export type RequestId = ArrayBuffer & { __requestId__: void };
* @param data - input to hash function
*/
export function hash(data: ArrayBuffer): ArrayBuffer {
- return jsSha256.create().update(new Uint8Array(data)).arrayBuffer();
+ return uint8ToBuf(sha256.create().update(new Uint8Array(data)).digest());
}
interface ToHashable {
diff --git a/packages/agent/src/utils/buffer.ts b/packages/agent/src/utils/buffer.ts
index 4db40bdd7..9af5957b5 100644
--- a/packages/agent/src/utils/buffer.ts
+++ b/packages/agent/src/utils/buffer.ts
@@ -55,3 +55,12 @@ export function compare(b1: ArrayBuffer, b2: ArrayBuffer): number {
}
return 0;
}
+
+/**
+ * Returns a true ArrayBuffer from a Uint8Array, as Uint8Array.buffer is unsafe.
+ * @param {Uint8Array} arr Uint8Array to convert
+ * @returns ArrayBuffer
+ */
+export function uint8ToBuf(arr: Uint8Array): ArrayBuffer {
+ return new DataView(arr.buffer, arr.byteOffset, arr.byteLength).buffer;
+}
diff --git a/packages/assets/package.json b/packages/assets/package.json
index 30f7eb174..274800e05 100644
--- a/packages/assets/package.json
+++ b/packages/assets/package.json
@@ -52,7 +52,7 @@
"peerDependencies": {
"@dfinity/agent": "^0.18.1",
"@dfinity/principal": "^0.18.1",
- "js-sha256": "0.9.0"
+ "@noble/hashes": "^1.3.1"
},
"dependencies": {
"base64-arraybuffer": "^1.0.2",
diff --git a/packages/assets/src/index.ts b/packages/assets/src/index.ts
index 2a6cc18c1..626ebb78f 100644
--- a/packages/assets/src/index.ts
+++ b/packages/assets/src/index.ts
@@ -9,11 +9,12 @@ import {
HashTree,
lookup_path,
reconstruct,
+ uint8ToBuf,
} from '@dfinity/agent';
import { lebDecode } from '@dfinity/candid';
import { PipeArrayBuffer } from '@dfinity/candid/lib/cjs/utils/buffer';
import { AssetsCanisterRecord, getAssetsCanister } from './canisters/assets';
-import { Hasher, sha256 as jsSha256 } from 'js-sha256';
+import { sha256 } from '@noble/hashes/sha256';
import { BatchOperationKind } from './canisters/assets_service';
import * as base64Arraybuffer from 'base64-arraybuffer';
import { isReadable, Readable } from './readable/readable';
@@ -196,14 +197,12 @@ export class AssetManager {
await readable.open();
const bytes = await readable.slice(0, readable.length);
await readable.close();
- const sha256 =
- config?.sha256 ??
- new Uint8Array(jsSha256.create().update(new Uint8Array(bytes)).arrayBuffer());
+ const hash = config?.sha256 ?? sha256.create().update(new Uint8Array(bytes)).digest();
return this._actor.store({
key,
content: bytes,
content_type: readable.contentType,
- sha256: [sha256],
+ sha256: [hash],
content_encoding: config?.contentEncoding ?? 'identity',
});
});
@@ -268,11 +267,15 @@ export class AssetManager {
}
}
+// Required since the sha256 type is not exported
+const hasher = sha256.create();
+type SHA256TYPE = typeof hasher;
+
class AssetManagerBatch {
private _scheduledOperations: Array<
(batch_id: bigint, onProgress?: (progress: Progress) => void) => Promise
> = [];
- private _sha256: { [key: string]: Hasher } = {};
+ private _sha256: { [key: string]: SHA256TYPE } = {};
private _progress: { [key: string]: Progress } = {};
constructor(
@@ -290,7 +293,7 @@ class AssetManagerBatch {
const [, config] = args;
const key = [config?.path ?? '', config?.fileName ?? readable.fileName].join('/');
if (!config?.sha256) {
- this._sha256[key] = jsSha256.create();
+ this._sha256[key] = sha256.create();
}
this._progress[key] = { current: 0, total: readable.length };
config?.onProgress?.(this._progress[key]);
@@ -330,7 +333,7 @@ class AssetManagerBatch {
{
SetAssetContent: {
key,
- sha256: [config?.sha256 ?? new Uint8Array(this._sha256[key].arrayBuffer())],
+ sha256: [config?.sha256 ?? new Uint8Array(this._sha256[key].digest())],
chunk_ids: chunkIds,
content_encoding: config?.contentEncoding ?? 'identity',
},
@@ -573,12 +576,12 @@ class Asset {
if (!this.sha256?.buffer) {
return false;
}
- const sha256 = jsSha256.create();
+ const hash = sha256.create();
if (bytes) {
- sha256.update(Array.isArray(bytes) ? new Uint8Array(bytes) : bytes);
+ hash.update(Array.isArray(bytes) ? new Uint8Array(bytes) : bytes);
} else {
- await this.getChunks((_, chunk) => sha256.update(chunk), true);
+ await this.getChunks((_, chunk) => hash.update(chunk), true);
}
- return compare(this.sha256.buffer, sha256.arrayBuffer()) === 0;
+ return compare(uint8ToBuf(this.sha256), uint8ToBuf(hash.digest())) === 0;
}
}
diff --git a/packages/identity-secp256k1/package.json b/packages/identity-secp256k1/package.json
index 21bcb009f..591625349 100644
--- a/packages/identity-secp256k1/package.json
+++ b/packages/identity-secp256k1/package.json
@@ -15,6 +15,7 @@
},
"dependencies": {
"@dfinity/agent": "^0.18.1",
+ "@noble/hashes": "^1.3.1",
"bip39": "^3.0.4",
"bs58check": "^2.1.2",
"secp256k1": "^4.0.3"
diff --git a/packages/identity-secp256k1/src/secp256k1.test.ts b/packages/identity-secp256k1/src/secp256k1.test.ts
index 55f590257..d335507a2 100644
--- a/packages/identity-secp256k1/src/secp256k1.test.ts
+++ b/packages/identity-secp256k1/src/secp256k1.test.ts
@@ -1,7 +1,7 @@
import { DerEncodedPublicKey, PublicKey } from '@dfinity/agent';
import { toHexString } from '@dfinity/candid/lib/cjs/utils/buffer';
import { randomBytes } from 'crypto';
-import { sha256 } from 'js-sha256';
+import { sha256 } from '@noble/hashes/sha256';
import Secp256k1 from 'secp256k1';
import { Secp256k1KeyIdentity, Secp256k1PublicKey } from './secp256k1';
diff --git a/packages/identity-secp256k1/src/secp256k1.ts b/packages/identity-secp256k1/src/secp256k1.ts
index ecf41e69d..b28039a69 100644
--- a/packages/identity-secp256k1/src/secp256k1.ts
+++ b/packages/identity-secp256k1/src/secp256k1.ts
@@ -1,7 +1,7 @@
/* eslint-disable no-underscore-dangle */
import { DerEncodedPublicKey, KeyPair, Signature } from '@dfinity/agent';
import Secp256k1 from 'secp256k1';
-import { sha256 } from 'js-sha256';
+import { sha256 } from '@noble/hashes/sha256';
import { randomBytes } from 'tweetnacl';
import hdkey from './hdkey';
import { mnemonicToSeedSync } from 'bip39';
@@ -201,7 +201,7 @@ export class Secp256k1KeyIdentity extends SignIdentity {
*/
public async sign(challenge: ArrayBuffer): Promise {
const hash = sha256.create();
- hash.update(challenge);
+ hash.update(new Uint8Array(challenge));
const signature = Secp256k1.ecdsaSign(
new Uint8Array(hash.digest()),
new Uint8Array(this._privateKey),
diff --git a/packages/identity/package.json b/packages/identity/package.json
index 8822460bd..09b95ad8a 100644
--- a/packages/identity/package.json
+++ b/packages/identity/package.json
@@ -51,8 +51,8 @@
"@peculiar/webcrypto": "^1.4.0"
},
"dependencies": {
+ "@noble/hashes": "^1.3.1",
"borc": "^2.1.1",
- "js-sha256": "^0.9.0",
"tweetnacl": "^1.0.1"
},
"devDependencies": {
diff --git a/packages/identity/src/identity/ecdsa.test.ts b/packages/identity/src/identity/ecdsa.test.ts
index 7d32a6f9b..7593ce3ed 100644
--- a/packages/identity/src/identity/ecdsa.test.ts
+++ b/packages/identity/src/identity/ecdsa.test.ts
@@ -1,4 +1,4 @@
-import { sha256 } from 'js-sha256';
+import { sha256 } from '@noble/hashes/sha256';
import { ECDSAKeyIdentity } from './ecdsa';
import { Crypto } from '@peculiar/webcrypto';
diff --git a/packages/principal/package.json b/packages/principal/package.json
index 33c9f8855..cd21b9d87 100644
--- a/packages/principal/package.json
+++ b/packages/principal/package.json
@@ -50,8 +50,8 @@
"@typescript-eslint/eslint-plugin": "^5.30.5",
"@typescript-eslint/parser": "^5.30.5",
"esbuild": "^0.15.16",
- "eslint-plugin-jsdoc": "^39.3.3",
"eslint": "^8.19.0",
+ "eslint-plugin-jsdoc": "^39.3.3",
"jest": "^28.1.2",
"size-limit": "^8.2.6",
"text-encoding": "^0.7.0",
@@ -62,14 +62,14 @@
"typescript": "^4.2.3",
"whatwg-fetch": "^3.0.0"
},
- "dependencies": {
- "js-sha256": "^0.9.0"
- },
"size-limit": [
{
"path": "./dist/index.js",
"limit": "100 kB",
"webpack": false
}
- ]
+ ],
+ "dependencies": {
+ "@noble/hashes": "^1.3.1"
+ }
}
diff --git a/packages/principal/src/utils/sha224.ts b/packages/principal/src/utils/sha224.ts
index 5c86d4c6b..ece9680b4 100644
--- a/packages/principal/src/utils/sha224.ts
+++ b/packages/principal/src/utils/sha224.ts
@@ -1,11 +1,9 @@
-import { sha224 as jsSha224 } from 'js-sha256';
+import { sha224 as jsSha224 } from '@noble/hashes/sha256';
/**
* Returns the SHA224 hash of the buffer.
* @param data Arraybuffer to encode
*/
export function sha224(data: ArrayBuffer): Uint8Array {
- const shaObj = jsSha224.create();
- shaObj.update(data);
- return new Uint8Array(shaObj.array());
+ return jsSha224.create().update(new Uint8Array(data)).digest();
}