From a85bc84d503556e1ed149a03e01daf4a029d53c9 Mon Sep 17 00:00:00 2001 From: Weiliang Li Date: Sat, 22 Feb 2025 08:25:38 +0900 Subject: [PATCH] Add details (#833) --- .github/workflows/ci.yml | 6 +- DETAILS.md | 126 ++++++++++++++++++++++++++ README.md | 4 +- example/browser/package.json | 2 +- package.json | 10 +-- pnpm-lock.yaml | 152 ++++++++++++++++---------------- tests-browser/package.json | 6 +- tests-browser/vitest.config.mts | 3 +- 8 files changed, 218 insertions(+), 91 deletions(-) create mode 100644 DETAILS.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 857ca62..dc18573 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,13 +46,13 @@ jobs: - name: Run browser tests (chromium, firefox) run: | - pnpm test:browser - pnpm test:browser -- --browser=firefox + pnpm test:browser --browser=chromium + pnpm test:browser --browser=firefox - name: Run browser tests (webkit) if: matrix.os == 'macos-latest' run: | - pnpm test:browser -- --browser=webkit + pnpm test:browser --browser=webkit check-runtimes: runs-on: ubuntu-latest diff --git a/DETAILS.md b/DETAILS.md new file mode 100644 index 0000000..5d8ca93 --- /dev/null +++ b/DETAILS.md @@ -0,0 +1,126 @@ +# Mechanism and Implementation in JavaScript + +> [!NOTE] +> +> This document is an adapted version of the original document in [eciespy](https://github.com/ecies/py/blob/master/DETAILS.md). You may go there for detailed documentation and learn the mechanism under the hood. + +This library combines `secp256k1` and `AES-256-GCM` (powered by [@noble/curves](https://github.com/paulmillr/noble-curves) and [@noble/ciphers](https://github.com/paulmillr/noble-ciphers)) to provide an API for encrypting with `secp256k1` public key and decrypting with `secp256k1`'s private key. It consists of two main parts: + +1. Use [ECDH](https://en.wikipedia.org/wiki/Elliptic-curve_Diffie–Hellman) to exchange an AES session key; + + > Note that the sender public key is generated every time when `encrypt` is called, thus, the AES session key varies. + > + > We use HKDF-SHA256 instead of SHA256 to derive the AES keys for better security. + +2. Use this AES session key to encrypt/decrypt the data under `AES-256-GCM`. + +The encrypted data structure is as follows: + +```plaintext ++-------------------------------+----------+----------+-----------------+ +| 65 Bytes | 16 Bytes | 16 Bytes | == data size | ++-------------------------------+----------+----------+-----------------+ +| Sender Public Key (ephemeral) | Nonce/IV | Tag/MAC | Encrypted data | ++-------------------------------+----------+----------+-----------------+ +| sender_pk | nonce | tag | encrypted_data | ++-------------------------------+----------+----------+-----------------+ +| Secp256k1 | AES-256-GCM | ++-------------------------------+---------------------------------------+ +``` + +## Secp256k1 in JavaScript + +### ECDH Implementation + +In JavaScript, we use the [@noble/curves](https://github.com/paulmillr/noble-curves) library which provides a pure JavaScript implementation of secp256k1. Here's a basic example: + +```typescript +import { secp256k1 } from '@noble/curves/secp256k1'; +import { equalBytes } from "@noble/ciphers/utils"; + +// Generate private keys (in production, use crypto.getRandomValues()) +const k1 = 3n; +const k2 = 2n; + +// Get public keys +const pub1 = secp256k1.getPublicKey(k1); +const pub2 = secp256k1.getPublicKey(k2); + +// Calculate shared secret - both parties will get the same result +const shared1 = secp256k1.getSharedSecret(k1, pub2); +const shared2 = secp256k1.getSharedSecret(k2, pub1); + +console.log(equalBytes(shared1, shared2)); +// true +``` + +### Public Key Formats + +Just like in the Python implementation, secp256k1 public keys can be represented in compressed (33 bytes) or uncompressed (65 bytes) format: + +- Uncompressed format (65 bytes): `04 || x || y` +- Compressed format (33 bytes): `02/03 || x` (02 if y is even, 03 if y is odd) + +The library handles both formats seamlessly: + +```typescript +import { secp256k1 } from '@noble/curves/secp256k1'; + +const privateKey = 3n; +const publicKeyUncompressed = secp256k1.getPublicKey(privateKey, false); // 65 bytes +const publicKeyCompressed = secp256k1.getPublicKey(privateKey, true); // 33 bytes +``` + +## AES in JavaScript + +For AES encryption, we use [@noble/ciphers](https://github.com/paulmillr/noble-ciphers) which provides a pure JavaScript implementation of AES-GCM. Here's a basic example: + +```typescript +import { gcm } from '@noble/ciphers/aes'; + +// 32-byte key from ECDH +const key = new Uint8Array(32); +// 16-byte nonce +const nonce = new Uint8Array(16); +const data = new TextEncoder().encode('hello world'); + +// Encrypt +const cipher = gcm(key, nonce); +const encrypted = cipher.encrypt(data); + +// Decrypt +const decipher = gcm(key, nonce); +const decrypted = decipher.decrypt(encrypted); + +console.log(new TextDecoder().decode(decrypted)); +// 'hello world' +``` + +Note that due to the format difference between @noble/ciphers with Python implementation, we need to adjust the position of nonce and tag in the encrypted data: + +```js +const encrypted = cipher.encrypt(data); +const cipherTextLength = encrypted.length - tagLength; +const cipherText = encrypted.subarray(0, cipherTextLength); +const tag = encrypted.subarray(cipherTextLength); +// ecies payload format: pk || nonce || tag || cipherText +const adjustedEncrypted = concatBytes(nonce, tag, cipherText); +``` + +## Key Derivation + +Instead of using plain SHA256 for key derivation, we use HKDF-SHA256 which is more secure: + +```typescript +import { hkdf } from '@noble/hashes/hkdf'; +import { sha256 } from '@noble/hashes/sha256'; + +// Derive AES key from ECDH shared secret +const ourPrivateKey = 3n; +// const ourPublicKey = secp256k1.getPublicKey(ourPrivateKey); +const theirPrivateKey = 2n; +const theirPublicKey = secp256k1.getPublicKey(theirPrivateKey); + +const sharedSecret = secp256k1.getSharedSecret(ourPrivateKey, theirPublicKey); +const sharedKey = hkdf(sha256, sharedSecret, undefined, undefined, 32); +``` diff --git a/README.md b/README.md index dd26b6a..30355fe 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,9 @@ Elliptic Curve Integrated Encryption Scheme for secp256k1/curve25519 in TypeScript. -This is the JavaScript/TypeScript version of [eciespy](https://github.com/ecies/py) with a built-in class-like secp256k1/curve25519 [API](#privatekey), you may go there for detailed documentation and learn the mechanism under the hood. +This is the JavaScript/TypeScript version of [eciespy](https://github.com/ecies/py) with a built-in class-like secp256k1/curve25519 [API](#privatekey). + +You can learn the details in [DETAILS.md](./DETAILS.md). ## Install diff --git a/example/browser/package.json b/example/browser/package.json index 0281718..1884686 100644 --- a/example/browser/package.json +++ b/example/browser/package.json @@ -16,7 +16,7 @@ "eciesjs": "file:../.." }, "devDependencies": { - "vite": "^6.0.7", + "vite": "^6.1.1", "vite-bundle-visualizer": "^1.2.1" } } diff --git a/package.json b/package.json index 0433c72..199b3b1 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "type": "git", "url": "git+https://github.com/ecies/js.git" }, - "version": "0.4.13", + "version": "0.4.14", "engines": { "node": ">=16", "bun": ">=1", @@ -62,11 +62,11 @@ "@noble/hashes": "^1.5.0" }, "devDependencies": { - "@types/node": "^22.13.0", - "@vitest/coverage-v8": "^3.0.4", + "@types/node": "^22.13.4", + "@vitest/coverage-v8": "^3.0.6", "typescript": "^5.7.3", "undici": "^7.3.0", - "vitest": "^3.0.4" + "vitest": "^3.0.6" }, - "packageManager": "pnpm@9.15.4+sha512.b2dc20e2fc72b3e18848459b37359a32064663e5627a51e4c74b2c29dd8e8e0491483c3abb40789cfd578bf362fb6ba8261b05f0387d76792ed6e23ea3b1b6a0" + "packageManager": "pnpm@10.4.1+sha512.c753b6c3ad7afa13af388fa6d808035a008e30ea9993f58c6663e2bc5ff21679aa834db094987129aa4d488b86df57f7b634981b2f827cdcacc698cc0cfb88af" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c8f0348..b2fa105 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -22,11 +22,11 @@ importers: version: 1.7.1 devDependencies: '@types/node': - specifier: ^22.13.0 - version: 22.13.0 + specifier: ^22.13.4 + version: 22.13.4 '@vitest/coverage-v8': - specifier: ^3.0.4 - version: 3.0.4(vitest@3.0.4(@types/node@22.13.0)) + specifier: ^3.0.6 + version: 3.0.6(vitest@3.0.6(@types/node@22.13.4)) typescript: specifier: ^5.7.3 version: 5.7.3 @@ -34,8 +34,8 @@ importers: specifier: ^7.3.0 version: 7.3.0 vitest: - specifier: ^3.0.4 - version: 3.0.4(@types/node@22.13.0) + specifier: ^3.0.6 + version: 3.0.6(@types/node@22.13.4) packages: @@ -360,23 +360,23 @@ packages: '@types/estree@1.0.6': resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} - '@types/node@22.13.0': - resolution: {integrity: sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA==} + '@types/node@22.13.4': + resolution: {integrity: sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg==} - '@vitest/coverage-v8@3.0.4': - resolution: {integrity: sha512-f0twgRCHgbs24Dp8cLWagzcObXMcuKtAwgxjJV/nnysPAJJk1JiKu/W0gIehZLmkljhJXU/E0/dmuQzsA/4jhA==} + '@vitest/coverage-v8@3.0.6': + resolution: {integrity: sha512-JRTlR8Bw+4BcmVTICa7tJsxqphAktakiLsAmibVLAWbu1lauFddY/tXeM6sAyl1cgkPuXtpnUgaCPhTdz1Qapg==} peerDependencies: - '@vitest/browser': 3.0.4 - vitest: 3.0.4 + '@vitest/browser': 3.0.6 + vitest: 3.0.6 peerDependenciesMeta: '@vitest/browser': optional: true - '@vitest/expect@3.0.4': - resolution: {integrity: sha512-Nm5kJmYw6P2BxhJPkO3eKKhGYKRsnqJqf+r0yOGRKpEP+bSCBDsjXgiu1/5QFrnPMEgzfC38ZEjvCFgaNBC0Eg==} + '@vitest/expect@3.0.6': + resolution: {integrity: sha512-zBduHf/ja7/QRX4HdP1DSq5XrPgdN+jzLOwaTq/0qZjYfgETNFCKf9nOAp2j3hmom3oTbczuUzrzg9Hafh7hNg==} - '@vitest/mocker@3.0.4': - resolution: {integrity: sha512-gEef35vKafJlfQbnyOXZ0Gcr9IBUsMTyTLXsEQwuyYAerpHqvXhzdBnDFuHLpFqth3F7b6BaFr4qV/Cs1ULx5A==} + '@vitest/mocker@3.0.6': + resolution: {integrity: sha512-KPztr4/tn7qDGZfqlSPQoF2VgJcKxnDNhmfR3VgZ6Fy1bO8T9Fc1stUiTXtqz0yG24VpD00pZP5f8EOFknjNuQ==} peerDependencies: msw: ^2.4.9 vite: ^5.0.0 || ^6.0.0 @@ -386,20 +386,20 @@ packages: vite: optional: true - '@vitest/pretty-format@3.0.4': - resolution: {integrity: sha512-ts0fba+dEhK2aC9PFuZ9LTpULHpY/nd6jhAQ5IMU7Gaj7crPCTdCFfgvXxruRBLFS+MLraicCuFXxISEq8C93g==} + '@vitest/pretty-format@3.0.6': + resolution: {integrity: sha512-Zyctv3dbNL+67qtHfRnUE/k8qxduOamRfAL1BurEIQSyOEFffoMvx2pnDSSbKAAVxY0Ej2J/GH2dQKI0W2JyVg==} - '@vitest/runner@3.0.4': - resolution: {integrity: sha512-dKHzTQ7n9sExAcWH/0sh1elVgwc7OJ2lMOBrAm73J7AH6Pf9T12Zh3lNE1TETZaqrWFXtLlx3NVrLRb5hCK+iw==} + '@vitest/runner@3.0.6': + resolution: {integrity: sha512-JopP4m/jGoaG1+CBqubV/5VMbi7L+NQCJTu1J1Pf6YaUbk7bZtaq5CX7p+8sY64Sjn1UQ1XJparHfcvTTdu9cA==} - '@vitest/snapshot@3.0.4': - resolution: {integrity: sha512-+p5knMLwIk7lTQkM3NonZ9zBewzVp9EVkVpvNta0/PlFWpiqLaRcF4+33L1it3uRUCh0BGLOaXPPGEjNKfWb4w==} + '@vitest/snapshot@3.0.6': + resolution: {integrity: sha512-qKSmxNQwT60kNwwJHMVwavvZsMGXWmngD023OHSgn873pV0lylK7dwBTfYP7e4URy5NiBCHHiQGA9DHkYkqRqg==} - '@vitest/spy@3.0.4': - resolution: {integrity: sha512-sXIMF0oauYyUy2hN49VFTYodzEAu744MmGcPR3ZBsPM20G+1/cSW/n1U+3Yu/zHxX2bIDe1oJASOkml+osTU6Q==} + '@vitest/spy@3.0.6': + resolution: {integrity: sha512-HfOGx/bXtjy24fDlTOpgiAEJbRfFxoX3zIGagCqACkFKKZ/TTOE6gYMKXlqecvxEndKFuNHcHqP081ggZ2yM0Q==} - '@vitest/utils@3.0.4': - resolution: {integrity: sha512-8BqC1ksYsHtbWH+DfpOAKrFw3jl3Uf9J7yeFh85Pz52IWuh1hBBtyfEbRNNZNjl8H8A5yMLH9/t+k7HIKzQcZQ==} + '@vitest/utils@3.0.6': + resolution: {integrity: sha512-18ktZpf4GQFTbf9jK543uspU03Q2qya7ZGya5yiZ0Gx0nnnalBvd5ZBislbl2EhLjM8A8rt4OilqKG7QwcGkvQ==} ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} @@ -431,8 +431,8 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} - chai@5.1.2: - resolution: {integrity: sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==} + chai@5.2.0: + resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} engines: {node: '>=12'} check-error@2.1.1: @@ -576,8 +576,8 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} - pathe@2.0.2: - resolution: {integrity: sha512-15Ztpk+nov8DR524R4BF7uEuzESgzUEAV4Ah7CUMNGXdE5ELuvxElxGXndBl32vMSsWa1jpNf22Z+Er3sKwq+w==} + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} pathval@2.0.0: resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} @@ -679,8 +679,8 @@ packages: resolution: {integrity: sha512-Qy96NND4Dou5jKoSJ2gm8ax8AJM/Ey9o9mz7KN1bb9GP+G0l20Zw8afxTnY2f4b7hmhn/z8aC2kfArVQlAhFBw==} engines: {node: '>=20.18.1'} - vite-node@3.0.4: - resolution: {integrity: sha512-7JZKEzcYV2Nx3u6rlvN8qdo3QV7Fxyt6hx+CCKz9fbWxdX5IvUOmTWEAxMrWxaiSf7CKGLJQ5rFu8prb/jBjOA==} + vite-node@3.0.6: + resolution: {integrity: sha512-s51RzrTkXKJrhNbUzQRsarjmAae7VmMPAsRT7lppVpIg6mK3zGthP9Hgz0YQQKuNcF+Ii7DfYk3Fxz40jRmePw==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true @@ -724,16 +724,16 @@ packages: yaml: optional: true - vitest@3.0.4: - resolution: {integrity: sha512-6XG8oTKy2gnJIFTHP6LD7ExFeNLxiTkK3CfMvT7IfR8IN+BYICCf0lXUQmX7i7JoxUP8QmeP4mTnWXgflu4yjw==} + vitest@3.0.6: + resolution: {integrity: sha512-/iL1Sc5VeDZKPDe58oGK4HUFLhw6b5XdY1MYawjuSaDA4sEfYlY9HnS6aCEG26fX+MgUi7MwlduTBHHAI/OvMA==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@types/debug': ^4.1.12 '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 - '@vitest/browser': 3.0.4 - '@vitest/ui': 3.0.4 + '@vitest/browser': 3.0.6 + '@vitest/ui': 3.0.6 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -969,11 +969,11 @@ snapshots: '@types/estree@1.0.6': {} - '@types/node@22.13.0': + '@types/node@22.13.4': dependencies: undici-types: 6.20.0 - '@vitest/coverage-v8@3.0.4(vitest@3.0.4(@types/node@22.13.0))': + '@vitest/coverage-v8@3.0.6(vitest@3.0.6(@types/node@22.13.4))': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 1.0.2 @@ -987,47 +987,47 @@ snapshots: std-env: 3.8.0 test-exclude: 7.0.1 tinyrainbow: 2.0.0 - vitest: 3.0.4(@types/node@22.13.0) + vitest: 3.0.6(@types/node@22.13.4) transitivePeerDependencies: - supports-color - '@vitest/expect@3.0.4': + '@vitest/expect@3.0.6': dependencies: - '@vitest/spy': 3.0.4 - '@vitest/utils': 3.0.4 - chai: 5.1.2 + '@vitest/spy': 3.0.6 + '@vitest/utils': 3.0.6 + chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.0.4(vite@6.0.11(@types/node@22.13.0))': + '@vitest/mocker@3.0.6(vite@6.0.11(@types/node@22.13.4))': dependencies: - '@vitest/spy': 3.0.4 + '@vitest/spy': 3.0.6 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - vite: 6.0.11(@types/node@22.13.0) + vite: 6.0.11(@types/node@22.13.4) - '@vitest/pretty-format@3.0.4': + '@vitest/pretty-format@3.0.6': dependencies: tinyrainbow: 2.0.0 - '@vitest/runner@3.0.4': + '@vitest/runner@3.0.6': dependencies: - '@vitest/utils': 3.0.4 - pathe: 2.0.2 + '@vitest/utils': 3.0.6 + pathe: 2.0.3 - '@vitest/snapshot@3.0.4': + '@vitest/snapshot@3.0.6': dependencies: - '@vitest/pretty-format': 3.0.4 + '@vitest/pretty-format': 3.0.6 magic-string: 0.30.17 - pathe: 2.0.2 + pathe: 2.0.3 - '@vitest/spy@3.0.4': + '@vitest/spy@3.0.6': dependencies: tinyspy: 3.0.2 - '@vitest/utils@3.0.4': + '@vitest/utils@3.0.6': dependencies: - '@vitest/pretty-format': 3.0.4 + '@vitest/pretty-format': 3.0.6 loupe: 3.1.3 tinyrainbow: 2.0.0 @@ -1051,7 +1051,7 @@ snapshots: cac@6.7.14: {} - chai@5.1.2: + chai@5.2.0: dependencies: assertion-error: 2.0.1 check-error: 2.1.1 @@ -1210,7 +1210,7 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.2 - pathe@2.0.2: {} + pathe@2.0.3: {} pathval@2.0.0: {} @@ -1311,13 +1311,13 @@ snapshots: undici@7.3.0: {} - vite-node@3.0.4(@types/node@22.13.0): + vite-node@3.0.6(@types/node@22.13.4): dependencies: cac: 6.7.14 debug: 4.4.0 es-module-lexer: 1.6.0 - pathe: 2.0.2 - vite: 6.0.11(@types/node@22.13.0) + pathe: 2.0.3 + vite: 6.0.11(@types/node@22.13.4) transitivePeerDependencies: - '@types/node' - jiti @@ -1332,39 +1332,39 @@ snapshots: - tsx - yaml - vite@6.0.11(@types/node@22.13.0): + vite@6.0.11(@types/node@22.13.4): dependencies: esbuild: 0.24.2 postcss: 8.5.1 rollup: 4.34.0 optionalDependencies: - '@types/node': 22.13.0 + '@types/node': 22.13.4 fsevents: 2.3.3 - vitest@3.0.4(@types/node@22.13.0): + vitest@3.0.6(@types/node@22.13.4): dependencies: - '@vitest/expect': 3.0.4 - '@vitest/mocker': 3.0.4(vite@6.0.11(@types/node@22.13.0)) - '@vitest/pretty-format': 3.0.4 - '@vitest/runner': 3.0.4 - '@vitest/snapshot': 3.0.4 - '@vitest/spy': 3.0.4 - '@vitest/utils': 3.0.4 - chai: 5.1.2 + '@vitest/expect': 3.0.6 + '@vitest/mocker': 3.0.6(vite@6.0.11(@types/node@22.13.4)) + '@vitest/pretty-format': 3.0.6 + '@vitest/runner': 3.0.6 + '@vitest/snapshot': 3.0.6 + '@vitest/spy': 3.0.6 + '@vitest/utils': 3.0.6 + chai: 5.2.0 debug: 4.4.0 expect-type: 1.1.0 magic-string: 0.30.17 - pathe: 2.0.2 + pathe: 2.0.3 std-env: 3.8.0 tinybench: 2.9.0 tinyexec: 0.3.2 tinypool: 1.0.2 tinyrainbow: 2.0.0 - vite: 6.0.11(@types/node@22.13.0) - vite-node: 3.0.4(@types/node@22.13.0) + vite: 6.0.11(@types/node@22.13.4) + vite-node: 3.0.6(@types/node@22.13.4) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 22.13.0 + '@types/node': 22.13.4 transitivePeerDependencies: - jiti - less diff --git a/tests-browser/package.json b/tests-browser/package.json index f84b656..e69e576 100644 --- a/tests-browser/package.json +++ b/tests-browser/package.json @@ -11,8 +11,8 @@ "eciesjs": "file:.." }, "devDependencies": { - "@vitest/browser": "^2.1.8", - "playwright": "^1.49.1", - "vitest": "^2.1.8" + "@vitest/browser": "^3.0.6", + "playwright": "^1.50.1", + "vitest": "^3.0.6" } } diff --git a/tests-browser/vitest.config.mts b/tests-browser/vitest.config.mts index 3796e43..840c44a 100644 --- a/tests-browser/vitest.config.mts +++ b/tests-browser/vitest.config.mts @@ -3,10 +3,9 @@ import { defineConfig } from "vitest/config"; export default defineConfig({ test: { browser: { + instances: [{ browser: "chromium" }, { browser: "firefox" }, { browser: "webkit" }], enabled: true, - name: "chromium", provider: "playwright", - providerOptions: {}, }, }, });