diff --git a/.eslintrc.js b/.eslintrc.cjs similarity index 67% rename from .eslintrc.js rename to .eslintrc.cjs index 6a58bb27..1be7c193 100644 --- a/.eslintrc.js +++ b/.eslintrc.cjs @@ -1,8 +1,15 @@ module.exports = { - 'extends': 'airbnb-base', + 'extends': [ + 'airbnb-base', + 'airbnb-typescript/base' + ], + + 'parser': '@typescript-eslint/parser', + 'parserOptions': { 'ecmaVersion': 11, - 'sourceType': 'module' + 'sourceType': 'module', + 'project': 'tsconfig.json' }, 'env': { @@ -12,10 +19,18 @@ module.exports = { }, 'plugins': [ + '@typescript-eslint', 'chai-friendly', - 'import' + 'import', + 'unicorn' ], + 'settings': { + 'import/resolver': { + 'typescript': {} + } + }, + 'globals': { // TODO are all these necessary? 'globalThis': true, 'console': true, @@ -42,11 +57,11 @@ module.exports = { 'arrow-body-style': 'off', 'arrow-parens': ['error','as-needed'], 'class-methods-use-this': 'off', - 'comma-dangle': ['error', 'never'], - 'comma-spacing': 'off', + '@typescript-eslint/comma-dangle': ['error', 'never'], + '@typescript-eslint/comma-spacing': 'off', 'consistent-return': 'off', 'default-case': 'off', - 'default-param-last': 'off', + '@typescript-eslint/default-param-last': 'off', 'eol-last': ['error', 'always'], 'function-call-argument-newline': 'off', 'func-names': ['error', 'never'], @@ -67,7 +82,7 @@ module.exports = { 'no-plusplus': 'off', 'no-restricted-syntax': ['error', 'ForInStatement', 'LabeledStatement', 'WithStatement'], 'object-curly-newline': 'off', - 'no-shadow': 'off', // TODO get rid of this + '@typescript-eslint/no-shadow': 'off', // TODO get rid of this 'object-property-newline': [ 'error', { @@ -88,31 +103,38 @@ module.exports = { 'prefer-template': 'off', 'quote-props': 'off', 'quotes': ['error', 'single', { 'avoidEscape': true }], - 'space-before-function-paren': 'off', + '@typescript-eslint/space-before-function-paren': ['error', { 'anonymous': 'ignore', 'named': 'never', 'asyncArrow': 'always' }], 'spaced-comment': 'off', - 'indent': ['error', 2, { 'SwitchCase': 1 }], - 'no-unused-vars': 'error', + 'indent': 'off', + '@typescript-eslint/indent': ['error', 2, { 'SwitchCase': 1 }], + 'no-unused-vars': 'off', + '@typescript-eslint/no-unused-vars': 'error', // eslint-plugin-import rules: 'import/named': 'error', - 'import/extensions': 'error', + 'import/extensions': 'off', // temporary: we use them in tests (ESM compliant), but not in the lib (to limit diff) + 'import/first': 'off', 'import/no-extraneous-dependencies': ['error', { 'devDependencies': true, 'optionalDependencies': false, 'peerDependencies': false }], 'import/no-unassigned-import': 'error', + 'import/no-unresolved': 'error', 'import/prefer-default-export': 'off', // Custom silencers: - 'camelcase': 'off', // used in tests, need to fix separately 'no-multi-assign': 'off', 'no-underscore-dangle': 'off', 'no-await-in-loop': 'off', + 'camelcase': 'off', // snake_case used in tests, need to fix separately + '@typescript-eslint/naming-convention': 'off', // supersedes 'camelcase' rule + '@typescript-eslint/lines-between-class-members': 'off', // Custom errors: - 'no-use-before-define': [2, { 'functions': false, 'classes': true, 'variables': false }], + '@typescript-eslint/no-use-before-define': ['error', { 'functions': false, 'classes': true, 'variables': false, 'allowNamedExports': true }], 'no-constant-condition': [2, { 'checkLoops': false }], 'new-cap': [2, { 'properties': false, 'capIsNewExceptionPattern': 'EAX|OCB|GCM|CMAC|CBC|OMAC|CTR', 'newIsCapExceptionPattern': 'type|hash*' }], 'max-lines': [2, { 'max': 620, 'skipBlankLines': true, 'skipComments': true }], - 'no-unused-expressions': 0, + '@typescript-eslint/no-unused-expressions': 0, 'chai-friendly/no-unused-expressions': [2, { 'allowShortCircuit': true }], + 'unicorn/switch-case-braces': ['error', 'avoid'], // Custom warnings: 'no-console': 1 diff --git a/.github/.dependabot.yml b/.github/.dependabot.yml new file mode 100644 index 00000000..fd6f4c38 --- /dev/null +++ b/.github/.dependabot.yml @@ -0,0 +1,29 @@ +version: 2 +updates: + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "daily" + allow: + - dependency-name: "playwright" + versioning-strategy: increase + + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "weekly" + allow: + - dependency-name: "@noble*" + - dependency-name: "fflate" + versioning-strategy: increase + groups: + # Any packages matching the pattern @noble* where the highest resolvable + # version is minor or patch will be grouped together. + # Grouping rules apply to version updates only. + noble: + applies-to: version-updates + patterns: + - "@noble*" + update-types: + - "minor" + - "patch" \ No newline at end of file diff --git a/.github/test-suite/config.json.template b/.github/test-suite/config.json.template index 7dd7e3fb..8e9a9d3f 100644 --- a/.github/test-suite/config.json.template +++ b/.github/test-suite/config.json.template @@ -11,7 +11,8 @@ "id": "sop-openpgpjs-main", "path": "__SOP_OPENPGPJS__", "env": { - "OPENPGPJS_PATH": "__OPENPGPJS_MAIN__" + "OPENPGPJS_PATH": "__OPENPGPJS_MAIN__", + "DISABLE_PROFILES": "true" } }, { @@ -21,10 +22,14 @@ "path": "__GPGME_SOP__" }, { - "path": "__GOSOP__" + "id": "gosop-v2", + "path": "__GOSOP_V2__" }, { "path": "__RNP_SOP__" + }, + { + "path": "__RSOP__" } ], "rlimits": { diff --git a/.github/test-suite/prepare_config.sh b/.github/test-suite/prepare_config.sh index debe9add..7cd12984 100755 --- a/.github/test-suite/prepare_config.sh +++ b/.github/test-suite/prepare_config.sh @@ -7,7 +7,8 @@ cat $CONFIG_TEMPLATE \ | sed "s@__OPENPGPJS_MAIN__@${OPENPGPJS_MAIN}@g" \ | sed "s@__SQOP__@${SQOP}@g" \ | sed "s@__GPGME_SOP__@${GPGME_SOP}@g" \ - | sed "s@__GOSOP__@${GOSOP}@g" \ - | sed "s@__SOP_OPENPGPJS__@${SOP_OPENPGPJS}@g" \ + | sed "s@__GOSOP_V2__@${GOSOP_V2}@g" \ + | sed "s@__SOP_OPENPGPJS__@${SOP_OPENPGPJS_V2}@g" \ | sed "s@__RNP_SOP__@${RNP_SOP}@g" \ + | sed "s@__RSOP__@${RSOP}@g" \ > $CONFIG_OUTPUT \ No newline at end of file diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 8f537a60..81f6145f 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -2,7 +2,7 @@ name: Performance Regression Test on: pull_request: - branches: [main] + branches: [main, v6] jobs: benchmark: @@ -11,15 +11,17 @@ jobs: steps: # check out pull request branch - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: path: pr # check out main branch (to compare performance) - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: main path: main - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 + with: + node-version: '>=20.6.0' - name: Run pull request time benchmark run: cd pr && npm install && npm run --silent benchmark-time > benchmarks.txt && cat benchmarks.txt diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 0a9e8c96..a8b7941c 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -4,7 +4,7 @@ on: push: branches: [main] pull_request: - branches: [main] + branches: [main, v6] jobs: lint: @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 - run: npm ci --ignore-scripts - run: npm run docs diff --git a/.github/workflows/sop-test-suite.yml b/.github/workflows/sop-test-suite.yml index 39f26c1a..16b56829 100644 --- a/.github/workflows/sop-test-suite.yml +++ b/.github/workflows/sop-test-suite.yml @@ -2,7 +2,7 @@ name: SOP interoperability test suite on: pull_request: - branches: [ main ] + branches: [ main, v6 ] jobs: @@ -10,34 +10,34 @@ jobs: name: Run interoperability test suite runs-on: ubuntu-latest container: - image: ghcr.io/protonmail/openpgp-interop-test-docker:v1.1.1 + image: ghcr.io/protonmail/openpgp-interop-test-docker:v1.1.12 credentials: username: ${{ github.actor }} password: ${{ secrets.github_token }} steps: # check out repo for scripts - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # check out pull request branch - name: Checkout openpgpjs-branch - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: path: openpgpjs-branch - name: Install openpgpjs-branch run: cd openpgpjs-branch && npm install - name: Print openpgpjs-branch version - run: $SOP_OPENPGPJS version --extended + run: $SOP_OPENPGPJS_V2 version --extended env: OPENPGPJS_PATH: ${{ github.workspace }}/openpgpjs-branch # check out main branch - name: Checkout openpgpjs-main - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: main path: openpgpjs-main - name: Install openpgpjs-main run: cd openpgpjs-main && npm install - name: Print openpgpjs-main version - run: $SOP_OPENPGPJS version --extended + run: $SOP_OPENPGPJS_V2 version --extended env: OPENPGPJS_PATH: ${{ github.workspace }}/openpgpjs-main # Run test suite @@ -56,12 +56,12 @@ jobs: RESULTS_HTML: .github/test-suite/test-suite-results.html # Upload results - name: Upload test results json artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: test-suite-results.json path: .github/test-suite/test-suite-results.json - name: Upload test results html artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: test-suite-results.html path: .github/test-suite/test-suite-results.html @@ -72,16 +72,16 @@ jobs: needs: test-suite steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Download test results json artifact id: download-test-results - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: test-suite-results.json - name: Compare with baseline - uses: ProtonMail/openpgp-interop-test-analyzer@v1 + uses: ProtonMail/openpgp-interop-test-analyzer@v2 with: results: ${{ steps.download-test-results.outputs.download-path }}/test-suite-results.json output: baseline-comparison.json baseline: sop-openpgpjs-main - target: sop-openpgpjs-main + target: sop-openpgpjs-branch diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index feed0fbe..291c02b3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -4,18 +4,18 @@ on: push: branches: [main] pull_request: - branches: [main] + branches: [main, v6] jobs: build: # cache both dist and tests (non-lightweight only), based on commit hash name: Build runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 - name: Check for cached folders id: cache-full - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | dist @@ -29,21 +29,22 @@ jobs: node: strategy: + fail-fast: false # if tests for one version fail, continue with the rest matrix: - node-version: [14.x, 16.x, 18.x, 20.x] + node-version: [18.x, 20.x, 22.x] # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ name: Node ${{ matrix.node-version }} runs-on: ubuntu-latest needs: build steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - run: npm ci --ignore-scripts # for mocha - name: Retrieve cached folders - uses: actions/cache/restore@v3 + uses: actions/cache/restore@v4 id: cache-full with: # test/lib is not needed, but the path must be specified fully for a cache-hit @@ -56,15 +57,22 @@ jobs: test-browsers-latest: name: Browsers (latest) - runs-on: ubuntu-latest needs: build + strategy: + fail-fast: false # if tests for one version fail, continue with the rest + matrix: + # run on all main platforms to test platform-specific code, if present + # (e.g. webkit's WebCrypto API implementation is different in macOS vs Linux) + # TODO: windows-latest fails to fetch resources from the wtr server; investigate if the problem is with path declaration or permissions + runner: ['ubuntu-latest', 'macos-latest'] + runs-on: ${{ matrix.runner }} steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 - name: Retrieve cached built folders - uses: actions/cache/restore@v3 + uses: actions/cache/restore@v4 id: cache-full with: path: | @@ -78,33 +86,35 @@ jobs: npm pkg delete scripts.prepare npm ci - - name: Get Playwright version + - name: Get Playwright version and cache location id: playwright-version run: | - PLAYWRIGHT_VERSION=$(npm ls playwright | grep playwright | sed 's/.*@//') + PLAYWRIGHT_VERSION=$(npm ls playwright --depth=0 | grep playwright | sed 's/.*@//') echo "version=$PLAYWRIGHT_VERSION" >> $GITHUB_OUTPUT + PLAYWRIGHT_CACHE=${{ fromJSON('{"ubuntu-latest": "~/.cache/ms-playwright", "macos-latest": "~/Library/Caches/ms-playwright"}')[matrix.runner] }} + echo "playwright_cache=$PLAYWRIGHT_CACHE" >> $GITHUB_OUTPUT - name: Check for cached browsers id: cache-playwright-browsers - uses: actions/cache@v3 + uses: actions/cache@v4 with: - path: ~/.cache/ms-playwright - key: playwright-browsers-${{ steps.playwright-version.outputs.version }} + path: ${{ steps.playwright-version.outputs.playwright_cache }} + key: playwright-browsers-${{ matrix.runner }}-${{ steps.playwright-version.outputs.version }} - name: Install browsers if: steps.cache-playwright-browsers.outputs.cache-hit != 'true' run: | - npx playwright install-deps chrome - npx playwright install-deps firefox + npx playwright install --with-deps chromium + npx playwright install --with-deps firefox - name: Install WebKit # caching not possible, external shared libraries required - run: npx playwright install-deps webkit + run: npx playwright install --with-deps webkit - name: Run browser tests - run: npm run test-browser + run: npm run test-browser:ci -- --static-logging - name: Run browser tests (lightweight) # overwrite test/lib run: | npm run build-test --lightweight - npm run test-browser + npm run test-browser:ci -- --static-logging test-browsers-compatibility: name: Browsers (older, on Browserstack) @@ -115,14 +125,23 @@ jobs: BROWSERSTACK_ACCESS_KEY: VjgBVRMxNVBj7SjJFiau steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + + - name: Generate self-signed HTTPS certificates for web-test-runner server + uses: kofemann/action-create-certificate@v0.0.4 + with: + hostcert: '127.0.0.1.pem' + hostkey: '127.0.0.1-key.pem' + cachain: 'ca-chain.pem' + - name: Adjust HTTPS certificates permissions + run: sudo chown runner:docker *.pem - name: Install dependencies run: npm ci --ignore-scripts - name: Retrieve cached dist folder - uses: actions/cache/restore@v3 + uses: actions/cache/restore@v4 id: cache-full with: path: | @@ -138,12 +157,12 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Run browserstack tests - run: npm run test-browserstack + run: npm run test-browserstack -- --static-logging - name: Run browserstack tests (lightweight) # overwrite test/lib run: | npm run build-test --lightweight - npm run test-browserstack + npm run test-browserstack -- --static-logging env: LIGHTWEIGHT: true @@ -153,11 +172,11 @@ jobs: needs: build steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 - run: npm ci --ignore-scripts # TS - name: Retrieve cached folders - uses: actions/cache/restore@v3 + uses: actions/cache/restore@v4 id: cache-full with: path: | @@ -172,11 +191,11 @@ jobs: needs: build steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 - run: npm ci --ignore-scripts # linter - name: Retrieve cached folders - uses: actions/cache/restore@v3 + uses: actions/cache/restore@v4 id: cache-full with: path: | diff --git a/.gitignore b/.gitignore index 805fe8d3..5781473f 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,4 @@ test/lib/ test/typescript/definitions.js dist/ openpgp.store/ -.nyc_output/ +coverage diff --git a/.jsdocrc.js b/.jsdocrc.cjs similarity index 100% rename from .jsdocrc.js rename to .jsdocrc.cjs diff --git a/.mocharc.json b/.mocharc.json new file mode 100644 index 00000000..7b963c2d --- /dev/null +++ b/.mocharc.json @@ -0,0 +1,6 @@ +{ + "node-option": [ + "experimental-specifier-resolution=node", + "loader=ts-node/esm" + ] +} diff --git a/README.md b/README.md index 1f719ee4..deddede6 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -OpenPGP.js [![BrowserStack Status](https://automate.browserstack.com/badge.svg?badge_key=N1l2eHFOanVBMU9wYWxJM3ZnWERnc1lidkt5UkRqa3BralV3SWVhOGpGTT0tLVljSjE4Z3dzVmdiQjl6RWgxb2c3T2c9PQ==--5864052cd523f751b6b907d547ac9c4c5f88c8a3)](https://automate.browserstack.com/public-build/N1l2eHFOanVBMU9wYWxJM3ZnWERnc1lidkt5UkRqa3BralV3SWVhOGpGTT0tLVljSjE4Z3dzVmdiQjl6RWgxb2c3T2c9PQ==--5864052cd523f751b6b907d547ac9c4c5f88c8a3) [![Join the chat on Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/openpgpjs/openpgpjs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +OpenPGP.js [![Join the chat on Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/openpgpjs/openpgpjs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) ========== -[OpenPGP.js](https://openpgpjs.org/) is a JavaScript implementation of the OpenPGP protocol. It implements [RFC4880](https://tools.ietf.org/html/rfc4880) and parts of [RFC4880bis](https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-10). +[OpenPGP.js](https://openpgpjs.org/) is a JavaScript implementation of the OpenPGP protocol. It implements [RFC9580](https://datatracker.ietf.org/doc/rfc9580/) (superseding [RFC4880](https://tools.ietf.org/html/rfc4880) and [RFC4880bis](https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-10)). **Table of Contents** @@ -33,61 +33,59 @@ OpenPGP.js [![BrowserStack Status](https://automate.browserstack.com/badge.svg?b ### Platform Support -* The `dist/openpgp.min.js` bundle works well with recent versions of Chrome, Firefox, Safari and Edge. +* The `dist/openpgp.min.js` (or `.mjs`) bundle works with recent versions of Chrome, Firefox, Edge and Safari 14+. -* The `dist/node/openpgp.min.js` bundle works well in Node.js. It is used by default when you `require('openpgp')` in Node.js. +* The `dist/node/openpgp.min.mjs` (or `.cjs`) bundle works in Node.js v18+: it is used by default when you `import ... from 'openpgp'` (resp. `require('openpgp')`). -* Currently, Chrome, Safari and Edge have partial implementations of the -[Streams specification](https://streams.spec.whatwg.org/), and Firefox -has a partial implementation behind feature flags. Chrome is the only -browser that implements `TransformStream`s, which we need, so we include -a [polyfill](https://github.com/MattiasBuelens/web-streams-polyfill) for -all other browsers. Please note that in those browsers, the global -`ReadableStream` property gets overwritten with the polyfill version if -it exists. In some edge cases, you might need to use the native +* Streaming support: + * in browsers: the latest versions of Chrome, Firefox, Edge and Safari implement the +[Streams specification](https://streams.spec.whatwg.org/), including `TransformStream`s. +These are needed if you use the library with streamed inputs. +In previous versions of OpenPGP.js, WebStreams were automatically polyfilled by the library, +but from v6 this task is left up to the library user, due to the more extensive browser support, and the +polyfilling side-effects. If you're working with [older browsers versions which do not implement e.g. TransformStreams](https://developer.mozilla.org/en-US/docs/Web/API/TransformStream), you can manually +load [WebStream polyfill](https://github.com/MattiasBuelens/web-streams-polyfills). +Please note that when you load the polyfills, the global `ReadableStream` property (if it exists) gets overwritten with the polyfill version. +In some edge cases, you might need to use the native `ReadableStream` (for example when using it to create a `Response` object), in which case you should store a reference to it before loading -OpenPGP.js. There is also the -[web-streams-adapter](https://github.com/MattiasBuelens/web-streams-adapter) +the polyfills. There is also the [web-streams-adapter](https://github.com/MattiasBuelens/web-streams-adapter) library to convert back and forth between them. + * in Node.js: OpenPGP.js v6 no longer supports native Node `Readable` streams in input, and instead expects (and outputs) [Node's WebStreams](https://nodejs.org/api/webstreams.html#class-readablestream). [Node v17+ includes utilities to convert from and to Web Streams](https://nodejs.org/api/stream.html#streamreadabletowebstreamreadable-options). + ### Performance -* Version 3.0.0 of the library introduces support for public-key cryptography using [elliptic curves](https://wiki.gnupg.org/ECC). We use native implementations on browsers and Node.js when available. Elliptic curve cryptography provides stronger security per bits of key, which allows for much faster operations. Currently the following curves are supported: +* Version 3.0.0 of the library introduced support for public-key cryptography using [elliptic curves](https://wiki.gnupg.org/ECC). We use native implementations on browsers and Node.js when available. Compared to RSA, elliptic curve cryptography provides stronger security per bits of key, which allows for much faster operations. Currently the following curves are supported: | Curve | Encryption | Signature | NodeCrypto | WebCrypto | Constant-Time | |:---------------:|:----------:|:---------:|:----------:|:---------:|:-----------------:| - | curve25519 | ECDH | N/A | No | No | Algorithmically** | - | ed25519 | N/A | EdDSA | No | No | Algorithmically** | - | p256 | ECDH | ECDSA | Yes* | Yes* | If native*** | - | p384 | ECDH | ECDSA | Yes* | Yes* | If native*** | - | p521 | ECDH | ECDSA | Yes* | Yes* | If native*** | - | brainpoolP256r1 | ECDH | ECDSA | Yes* | No | If native*** | - | brainpoolP384r1 | ECDH | ECDSA | Yes* | No | If native*** | - | brainpoolP512r1 | ECDH | ECDSA | Yes* | No | If native*** | - | secp256k1 | ECDH | ECDSA | Yes* | No | If native*** | + | curve25519 | ECDH | N/A | No | No | Algorithmically | + | ed25519 | N/A | EdDSA | No | Yes* | If native** | + | nistP256 | ECDH | ECDSA | Yes* | Yes* | If native** | + | nistP384 | ECDH | ECDSA | Yes* | Yes* | If native** | + | nistP521 | ECDH | ECDSA | Yes* | Yes* | If native** | + | brainpoolP256r1 | ECDH | ECDSA | Yes* | No | If native** | + | brainpoolP384r1 | ECDH | ECDSA | Yes* | No | If native** | + | brainpoolP512r1 | ECDH | ECDSA | Yes* | No | If native** | + | secp256k1 | ECDH | ECDSA | Yes* | No | If native** | - \* when available - \** the curve25519 and ed25519 implementations are algorithmically constant-time, but may not be constant-time after optimizations of the JavaScript compiler - \*** these curves are only constant-time if the underlying native implementation is available and constant-time - -* Version 2.x of the library has been built from the ground up with Uint8Arrays. This allows for much better performance and memory usage than strings. + \* when available + \** these curves are only constant-time if the underlying native implementation is available and constant-time * If the user's browser supports [native WebCrypto](https://caniuse.com/#feat=cryptography) via the `window.crypto.subtle` API, this will be used. Under Node.js the native [crypto module](https://nodejs.org/api/crypto.html#crypto_crypto) is used. -* The library implements the [RFC4880bis proposal](https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-10) for authenticated encryption using native AES-EAX, OCB, or GCM. This makes symmetric encryption up to 30x faster on supported platforms. Since the specification has not been finalized and other OpenPGP implementations haven't adopted it yet, the feature is currently behind a flag. **Note: activating this setting can break compatibility with other OpenPGP implementations, and also with future versions of OpenPGP.js. Don't use it with messages you want to store on disk or in a database.** You can enable it by setting `openpgp.config.aeadProtect = true`. +* The library implements authenticated encryption (AEAD) as per [RFC9580](https://datatracker.ietf.org/doc/rfc9580/) using AES-GCM, OCB, or EAX. This makes symmetric encryption faster on platforms with native implementations. However, since the specification is very recent and other OpenPGP implementations are in the process of adopting it, the feature is currently behind a flag. **Note: activating this setting can break compatibility with other OpenPGP implementations which have yet to implement the feature.** You can enable it by setting `openpgp.config.aeadProtect = true`. +Note that this setting has a different effect from the one in OpenPGP.js v5, which implemented support for a provisional version of AEAD from [RFC4880bis](https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-10), which was modified in RFC9580. You can change the AEAD mode by setting one of the following options: ``` - openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.eax // Default, native - openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.ocb // Non-native - openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.experimentalGCM // **Non-standard**, fastest + openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.gcm; // Default, native in WebCrypto and Node.js + openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.ocb; // Non-native, but supported across RFC9580 implementations + openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.eax; // Native in Node.js ``` -* For environments that don't provide native crypto, the library falls back to [asm.js](https://caniuse.com/#feat=asmjs) implementations of AES, SHA-1, and SHA-256. - - ### Getting started #### Node.js @@ -98,18 +96,17 @@ Install OpenPGP.js using npm and save it in your dependencies: npm install --save openpgp ``` -And import it as a CommonJS module: +And import it as an ES module, from a .mjs file: +```js +import * as openpgp from 'openpgp'; +``` + +Or as a CommonJS module: ```js const openpgp = require('openpgp'); ``` -Or as an ES6 module, from an .mjs file: - -```js -import * as openpgp from 'openpgp'; -``` - #### Deno (experimental) Import as an ES6 module, using /dist/openpgp.mjs. @@ -174,17 +171,17 @@ To offload cryptographic operations off the main thread, you can implement a Web #### TypeScript -Since TS is not fully integrated in the library, TS-only dependencies are currently listed as `devDependencies`, so to compile the project you’ll need to add `@openpgp/web-stream-tools` manually (NB: only versions below v0.12 are compatible with OpenPGP.js v5): +Since TS is not fully integrated in the library, TS-only dependencies are currently listed as `devDependencies`, so to compile the project you’ll need to add `@openpgp/web-stream-tools` manually: ```sh -npm install --save-dev @openpgp/web-stream-tools@0.0.11-patch-0 +npm install --save-dev @openpgp/web-stream-tools ``` If you notice missing or incorrect type definitions, feel free to open a PR. ### Examples -Here are some examples of how to use OpenPGP.js v5. For more elaborate examples and working code, please check out the [public API unit tests](https://github.com/openpgpjs/openpgpjs/blob/main/test/general/openpgp.js). If you're upgrading from v4 it might help to check out the [changelog](https://github.com/openpgpjs/openpgpjs/wiki/V5-Changelog) and [documentation](https://github.com/openpgpjs/openpgpjs#documentation). +Here are some examples of how to use OpenPGP.js v6. For more elaborate examples and working code, please check out the [public API unit tests](https://github.com/openpgpjs/openpgpjs/blob/main/test/general/openpgp.js). If you're upgrading from v4 it might help to check out the [changelog](https://github.com/openpgpjs/openpgpjs/wiki/v6-Changelog) and [documentation](https://github.com/openpgpjs/openpgpjs#documentation). #### Encrypt and decrypt *Uint8Array* data with a password @@ -389,14 +386,8 @@ Where the value can be any of: })(); ``` -For more information on using ReadableStreams, see [the MDN Documentation on the -Streams API](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API). - -You can also pass a [Node.js `Readable` -stream](https://nodejs.org/api/stream.html#stream_class_stream_readable), in -which case OpenPGP.js will return a Node.js `Readable` stream as well, which you -can `.pipe()` to a `Writable` stream, for example. - +For more information on using ReadableStreams (both in browsers and Node.js), see [the MDN Documentation on the +Streams API](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) . #### Streaming encrypt and decrypt *String* data with PGP keys @@ -453,7 +444,7 @@ can `.pipe()` to a `Writable` stream, for example. ECC keys (smaller and faster to generate): -Possible values for `curve` are: `curve25519`, `ed25519`, `p256`, `p384`, `p521`, +Possible values for `curve` are: `curve25519`, `ed25519`, `nistP256`, `nistP384`, `nistP521`, `brainpoolP256r1`, `brainpoolP384r1`, `brainpoolP512r1`, and `secp256k1`. Note that both the `curve25519` and `ed25519` options generate a primary key for signing using Ed25519 and a subkey for encryption using Curve25519. @@ -670,7 +661,7 @@ To create your own build of the library, just run the following command after cl npm install && npm test -For debugging browser errors, you can run `npm start` and open [`http://localhost:8080/test/unittests.html`](http://localhost:8080/test/unittests.html) in a browser, or run the following command: +For debugging browser errors, run the following command: npm run browsertest diff --git a/docs/AEADEncryptedDataPacket.html b/docs/AEADEncryptedDataPacket.html index 7c33af4c..1e1678fe 100644 --- a/docs/AEADEncryptedDataPacket.html +++ b/docs/AEADEncryptedDataPacket.html @@ -98,7 +98,7 @@ AEAD Protected Data Packet

Source:
@@ -200,7 +200,7 @@ AEAD Protected Data Packet

Source:
@@ -270,7 +270,7 @@ AEAD Protected Data Packet

Source:
@@ -298,215 +298,6 @@ AEAD Protected Data Packet

-

(async) crypt(fn, key, data) → {Promise.<(Uint8Array|ReadableStream.<Uint8Array>)>}

- - - - - - -
-

En/decrypt the payload.

-
- - - - - - - - - -
Parameters:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
fn - - -encrypt -| - -decrypt - - - -

Whether to encrypt or decrypt

key - - -Uint8Array - - - -

The session key used to en/decrypt the payload

data - - -Uint8Array -| - -ReadableStream.<Uint8Array> - - - -

The data to en/decrypt

- - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - - - - - -
Returns:
- - - - -
-
- Type -
-
- -Promise.<(Uint8Array|ReadableStream.<Uint8Array>)> - - -
-
- - - - - - - - - - - - -

(async) decrypt(sessionKeyAlgorithm, key, configopt)

@@ -684,7 +475,7 @@ AEAD Protected Data Packet

Source:
@@ -926,7 +717,7 @@ AEAD Protected Data Packet

Source:
@@ -1097,7 +888,7 @@ AEAD Protected Data Packet

Source:
@@ -1216,7 +1007,7 @@ AEAD Protected Data Packet

Source:
@@ -1287,7 +1078,7 @@ AEAD Protected Data Packet


diff --git a/docs/Argon2S2K.html b/docs/Argon2S2K.html new file mode 100644 index 00000000..71db5758 --- /dev/null +++ b/docs/Argon2S2K.html @@ -0,0 +1,986 @@ + + + + + JSDoc: Class: Argon2S2K + + + + + + + + + + +
+ +

Class: Argon2S2K

+ + + + + + +
+ +
+ +

Argon2S2K(configopt)

+ + +
+ +
+
+ + + + + + +

new Argon2S2K(configopt)

+ + + + + + + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
config + + +Object + + + + + + <optional>
+ + + + + +

Full configuration, defaults to openpgp.config

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +

Members

+ + + +

encodedM :Integer

+ + + + +
+

exponent indicating memory size

+
+ + + +
Type:
+
    +
  • + +Integer + + +
  • +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

p :Integer

+ + + + +
+

degree of parallelism (lanes)

+
+ + + +
Type:
+
    +
  • + +Integer + + +
  • +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

salt :Uint8Array

+ + + + +
+

16 bytes of salt

+
+ + + +
Type:
+
    +
  • + +Uint8Array + + +
  • +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

t :Integer

+ + + + +
+

number of passes

+
+ + + +
Type:
+
    +
  • + +Integer + + +
  • +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + +

Methods

+ + + + + + + +

(async) produceKey(passphrase) → {Promise.<Uint8Array>}

+ + + + + + +
+

Produces a key using the specified passphrase and the defined +hashAlgorithm

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
passphrase + + +String + + + +

Passphrase containing user input

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Throws:
+ + + +
+ + +Argon2OutOfMemoryError +| + +Errors + + + +
+ + + + + +
Returns:
+ + +
+

Produced key with a length corresponding to keySize

+
+ + + +
+
+ Type +
+
+ +Promise.<Uint8Array> + + +
+
+ + + + + + + + + + + + + +

read(bytes) → {Integer}

+ + + + + + +
+

Parsing function for argon2 string-to-key specifier.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
bytes + + +Uint8Array + + + +

Payload of argon2 string-to-key specifier

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Actual length of the object.

+
+ + + +
+
+ Type +
+
+ +Integer + + +
+
+ + + + + + + + + + + + + +

write() → {Uint8Array}

+ + + + + + +
+

Serializes s2k information

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Binary representation of s2k.

+
+ + + +
+
+ Type +
+
+ +Uint8Array + + +
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/CleartextMessage.html b/docs/CleartextMessage.html index 376f419c..6f620e61 100644 --- a/docs/CleartextMessage.html +++ b/docs/CleartextMessage.html @@ -168,7 +168,7 @@ See https://tools.ietf.o
Source:
@@ -346,7 +346,7 @@ See https://tools.ietf.o
Source:
@@ -461,7 +461,7 @@ See https://tools.ietf.o
Source:
@@ -573,7 +573,7 @@ See https://tools.ietf.o
Source:
@@ -974,7 +974,7 @@ See https://tools.ietf.o
Source:
@@ -1211,7 +1211,7 @@ See https://tools.ietf.o
Source:
@@ -1279,7 +1279,7 @@ See https://tools.ietf.o
diff --git a/docs/CompressedDataPacket.html b/docs/CompressedDataPacket.html index 50f63a44..56aee417 100644 --- a/docs/CompressedDataPacket.html +++ b/docs/CompressedDataPacket.html @@ -160,7 +160,7 @@ a Signature or One-Pass Signature packet, and contains a literal data packet.

Source:
@@ -266,7 +266,7 @@ a Signature or One-Pass Signature packet, and contains a literal data packet.

Source:
@@ -343,71 +343,7 @@ a Signature or One-Pass Signature packet, and contains a literal data packet.

Source:
- - - - - - - - - - - - - - - - -

deflateLevel

- - - - -
-

zip/zlib compression level, between 1 and 9

-
- - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
@@ -481,7 +417,7 @@ a Signature or One-Pass Signature packet, and contains a literal data packet.

Source:
@@ -563,7 +499,7 @@ a Signature or One-Pass Signature packet, and contains a literal data packet.

Source:
@@ -715,7 +651,7 @@ read by read_packet

Source:
@@ -900,7 +836,7 @@ read by read_packet

Source:
@@ -990,7 +926,7 @@ read by read_packet

Source:
@@ -1061,7 +997,7 @@ read by read_packet


diff --git a/docs/Key.html b/docs/Key.html index eb962465..4f4585c1 100644 --- a/docs/Key.html +++ b/docs/Key.html @@ -96,7 +96,7 @@ Can contain additional subkeys, signatures, user ids, user attributes.

Source:
@@ -333,7 +333,7 @@ if it is a valid revocation signature.

Source:
@@ -514,7 +514,7 @@ if it is a valid revocation signature.

Source:
@@ -626,7 +626,7 @@ if it is a valid revocation signature.

Source:
@@ -738,7 +738,7 @@ if it is a valid revocation signature.

Source:
@@ -1006,7 +1006,7 @@ if it is a valid revocation signature.

Source:
@@ -1225,7 +1225,7 @@ Returns Infinity if the key doesn't expire, or null if
Source:
@@ -1333,7 +1333,7 @@ Returns Infinity if the key doesn't expire, or null if
Source:
@@ -1445,7 +1445,7 @@ Returns Infinity if the key doesn't expire, or null if
Source:
@@ -1557,7 +1557,7 @@ Returns Infinity if the key doesn't expire, or null if
Source:
@@ -1735,7 +1735,7 @@ If no keyID is given, returns all keys, starting with the primary key.

Source:
@@ -1793,6 +1793,248 @@ If no keyID is given, returns all keys, starting with the primary key.

+

(async) getPrimarySelfSignature(dateopt, userIDopt, configopt) → {Promise.<SignaturePacket>}

+ + + + + + +
+

For V4 keys, returns the self-signature of the primary user. +For V5 keys, returns the latest valid direct-key self-signature. +This self-signature is to be used to check the key expiration, +algorithm preferences, and so on.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
date + + +Date + + + + + + <optional>
+ + + + + +

Use the given date for verification instead of the current time

userID + + +Object + + + + + + <optional>
+ + + + + +

User ID to get instead of the primary user for V4 keys, if it exists

config + + +Object + + + + + + <optional>
+ + + + + +

Full configuration, defaults to openpgp.config

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

The primary self-signature

+
+ + + +
+
+ Type +
+
+ +Promise.<SignaturePacket> + + +
+
+ + + + + + + + + + + + +

(async) getPrimaryUser(dateopt, userIDopt, configopt) → {Promise.<{user: User, selfCertification: SignaturePacket}>}

@@ -1978,7 +2220,7 @@ If no keyID is given, returns all keys, starting with the primary key.

Source:
@@ -2183,7 +2425,7 @@ If no keyID is given, returns all keys, starting with the primary key.

Source:
@@ -2475,7 +2717,7 @@ If no keyID is given, returns all keys, starting with the primary key.

Source:
@@ -2669,7 +2911,7 @@ If no keyID is given, returns all subkeys.

Source:
@@ -2781,7 +3023,7 @@ If no keyID is given, returns all subkeys.

Source:
@@ -2893,7 +3135,7 @@ If no keyID is given, returns all subkeys.

Source:
@@ -3170,7 +3412,7 @@ If no keyID is given, returns all subkeys.

Source:
@@ -3354,7 +3596,7 @@ If no keyID is given, returns all subkeys.

Source:
@@ -3569,7 +3811,7 @@ If no keyID is given, returns all subkeys.

Source:
@@ -3839,7 +4081,7 @@ If no keyID is given, returns all subkeys.

Source:
@@ -3951,7 +4193,7 @@ If no keyID is given, returns all subkeys.

Source:
@@ -4192,7 +4434,7 @@ a private key is returned.

Source:
@@ -4435,7 +4677,7 @@ a private key is returned.

Source:
@@ -4676,7 +4918,7 @@ and valid self signature. Throws if the primary key is invalid.

Source:
@@ -4959,7 +5201,7 @@ and valid self signature. Throws if the primary key is invalid.

Source:
@@ -5072,7 +5314,7 @@ Signature validity is null if the verification keys do not correspond to the cer
Source:
@@ -5140,7 +5382,7 @@ Signature validity is null if the verification keys do not correspond to the cer
diff --git a/docs/LiteralDataPacket.html b/docs/LiteralDataPacket.html index d86e7ca9..70c71a40 100644 --- a/docs/LiteralDataPacket.html +++ b/docs/LiteralDataPacket.html @@ -147,7 +147,7 @@ further interpreted.

Source:
@@ -326,7 +326,7 @@ further interpreted.

Source:
@@ -441,7 +441,7 @@ further interpreted.

Source:
@@ -623,7 +623,7 @@ with normalized end of line to \n

Source:
@@ -790,7 +790,7 @@ with normalized end of line to \n

Source:
@@ -977,7 +977,7 @@ with normalized end of line to \n

Source:
@@ -1116,7 +1116,7 @@ with normalized end of line to \n

Source:
@@ -1302,7 +1302,7 @@ will be normalized to \r\n and by default text is converted to UTF8

Source:
@@ -1392,7 +1392,7 @@ will be normalized to \r\n and by default text is converted to UTF8

Source:
@@ -1507,7 +1507,7 @@ will be normalized to \r\n and by default text is converted to UTF8

Source:
@@ -1575,7 +1575,7 @@ will be normalized to \r\n and by default text is converted to UTF8


diff --git a/docs/MarkerPacket.html b/docs/MarkerPacket.html index 906e493f..28d65ca3 100644 --- a/docs/MarkerPacket.html +++ b/docs/MarkerPacket.html @@ -106,7 +106,7 @@ software is necessary to process the message.

Source:
@@ -265,7 +265,7 @@ software is necessary to process the message.

Source:
@@ -333,7 +333,7 @@ software is necessary to process the message.


diff --git a/docs/Message.html b/docs/Message.html index 7bace1e7..f93d7717 100644 --- a/docs/Message.html +++ b/docs/Message.html @@ -146,7 +146,7 @@ See https://tools.iet
Source:
@@ -661,7 +661,7 @@ See https://tools.iet
Source:
@@ -933,7 +933,7 @@ See https://tools.iet
Source:
@@ -1140,7 +1140,7 @@ See https://tools.iet
Source:
@@ -1291,7 +1291,7 @@ See https://tools.iet
Source:
@@ -1495,7 +1495,7 @@ See https://tools.iet
Source:
@@ -1800,7 +1800,7 @@ See https://tools.iet
Source:
@@ -1858,7 +1858,7 @@ See https://tools.iet -

(async) decryptSessionKeys(decryptionKeysopt, passwordsopt, dateopt, configopt) → {Promise.<Array.<{data: Uint8Array, algorithm: String}>>}

+

(async) decryptSessionKeys(decryptionKeysopt, passwordsopt, expectedSymmetricAlgorithmopt, dateopt, configopt) → {Promise.<Array.<{data: Uint8Array, algorithm: String}>>}

@@ -1968,6 +1968,39 @@ See
https://tools.iet + + + expectedSymmetricAlgorithm + + + + + +enums.symmetric + + + + + + + + + <optional>
+ + + + + + + + + + +

The symmetric algorithm the SEIPDv2 / AEAD packet is encrypted with (if applicable)

+ + + + date @@ -2072,7 +2105,7 @@ See
https://tools.iet
Source:
@@ -2512,7 +2545,7 @@ See https://tools.iet
Source:
@@ -2624,7 +2657,7 @@ See https://tools.iet
Source:
@@ -2736,7 +2769,7 @@ See https://tools.iet
Source:
@@ -2851,7 +2884,7 @@ See https://tools.iet
Source:
@@ -2966,7 +2999,7 @@ See https://tools.iet
Source:
@@ -3078,7 +3111,7 @@ See https://tools.iet
Source:
@@ -3482,7 +3515,7 @@ See https://tools.iet
Source:
@@ -3883,7 +3916,7 @@ See https://tools.iet
Source:
@@ -3995,7 +4028,7 @@ See https://tools.iet
Source:
@@ -4232,7 +4265,7 @@ See https://tools.iet
Source:
@@ -4498,7 +4531,7 @@ See https://tools.iet
Source:
@@ -4610,7 +4643,7 @@ See https://tools.iet
Source:
@@ -4678,7 +4711,7 @@ See https://tools.iet
diff --git a/docs/OnePassSignaturePacket.html b/docs/OnePassSignaturePacket.html index 0fdea241..93533815 100644 --- a/docs/OnePassSignaturePacket.html +++ b/docs/OnePassSignaturePacket.html @@ -101,7 +101,7 @@ can compute the entire signed message in one pass.

Source:
@@ -199,7 +199,7 @@ that describes another signature to be applied to the same message data.

Source:
@@ -273,7 +273,7 @@ that describes another signature to be applied to the same message data.

Source:
@@ -298,13 +298,13 @@ that describes another signature to be applied to the same message data.

-

issuerKeyID

+

issuerFingerprint

-

An eight-octet number holding the Key ID of the signing key.

+

Only for v6 packets, 32 octets of the fingerprint of the signing key.

@@ -344,7 +344,71 @@ that describes another signature to be applied to the same message data.

Source:
+ + + + + + + +
+ + + + + + + + +

issuerKeyID

+ + + + +
+

Only for v3 packets, an eight-octet number holding the Key ID of the signing key.

+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
@@ -418,7 +482,7 @@ that describes another signature to be applied to the same message data.

Source:
@@ -443,6 +507,70 @@ that describes another signature to be applied to the same message data.

+

salt

+ + + + +
+

Only for v6, a variable-length field containing the salt.

+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + +

signatureType :enums.signature

@@ -501,7 +629,7 @@ Signature types are described in
Source:
@@ -525,7 +653,7 @@ Signature types are described in
-

A one-octet version number. The current version is 3.

+

A one-octet version number. The current versions are 3 and 6.

@@ -565,7 +693,7 @@ Signature types are described in
Source:
@@ -696,7 +824,7 @@ Signature types are described in
Source:
@@ -808,7 +936,7 @@ Signature types are described in
Source:
@@ -876,7 +1004,7 @@ Signature types are described in
diff --git a/docs/PacketList.html b/docs/PacketList.html index 7d2af1a5..f4ad9a85 100644 --- a/docs/PacketList.html +++ b/docs/PacketList.html @@ -97,7 +97,7 @@ are stored as numerical indices.

Source:
@@ -345,7 +345,7 @@ Equivalent to calling read on an empty PacketList instance.

Source:
@@ -530,7 +530,7 @@ Equivalent to calling read on an empty PacketList instance.

Source:
@@ -687,7 +687,7 @@ Equivalent to calling read on an empty PacketList instance.

Source:
@@ -859,7 +859,7 @@ Equivalent to calling read on an empty PacketList instance.

Source:
@@ -1097,7 +1097,7 @@ Equivalent to calling read on an empty PacketList instance.

Source:
@@ -1200,7 +1200,7 @@ class instance.

Source:
@@ -1268,7 +1268,7 @@ class instance.


diff --git a/docs/PaddingPacket.html b/docs/PaddingPacket.html new file mode 100644 index 00000000..aedccc51 --- /dev/null +++ b/docs/PaddingPacket.html @@ -0,0 +1,600 @@ + + + + + JSDoc: Class: PaddingPacket + + + + + + + + + + +
+ +

Class: PaddingPacket

+ + + + + + +
+ +
+ +

PaddingPacket()

+ + + + +
+ +
+
+ + + + +

Constructor

+ + + +

new PaddingPacket()

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(async) createPadding(length)

+ + + + + + +
+

Create random padding.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
length + + +Number + + + +

The length of padding to be generated.

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Throws:
+ + + +
+
+
+

if padding generation was not successful

+
+
+
+
+
+
+ Type +
+
+ +Error + + +
+
+
+
+
+ + + + + + + + + + + + + + + + +

read(bytes)

+ + + + + + +
+

Read a padding packet

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
bytes + + +Uint8Array +| + +ReadableStream.<Uint8Array> + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

write() → {Uint8Array}

+ + + + + + +
+

Write the padding packet

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

The padding packet.

+
+ + + +
+
+ Type +
+
+ +Uint8Array + + +
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/PrivateKey.html b/docs/PrivateKey.html index 76110aaa..16ea6745 100644 --- a/docs/PrivateKey.html +++ b/docs/PrivateKey.html @@ -144,7 +144,7 @@
Source:
@@ -209,7 +209,8 @@

Generates a new OpenPGP subkey, and returns a clone of the Key object with the new subkey added. -Supports RSA and ECC keys. Defaults to the algorithm and bit size/curve of the primary key. DSA primary keys default to RSA subkeys.

+Supports RSA and ECC keys, as well as the newer Curve448 and Curve25519. +Defaults to the algorithm and bit size/curve of the primary key. DSA primary keys default to RSA subkeys.

@@ -255,6 +256,12 @@ Supports RSA and ECC keys. Defaults to the algorithm and bit size/curve of the p | rsa +| + +curve25519 +| + +curve448 @@ -264,7 +271,8 @@ Supports RSA and ECC keys. Defaults to the algorithm and bit size/curve of the p -

The subkey algorithm: ECC or RSA

+

The subkey algorithm: ECC, RSA, Curve448 or Curve25519 (new format). +Note: Curve448 and Curve25519 are not widely supported yet.

@@ -445,7 +453,7 @@ Supports RSA and ECC keys. Defaults to the algorithm and bit size/curve of the p
Source:
@@ -614,7 +622,7 @@ Supports RSA and ECC keys. Defaults to the algorithm and bit size/curve of the p
Source:
@@ -726,7 +734,7 @@ Supports RSA and ECC keys. Defaults to the algorithm and bit size/curve of the p
Source:
@@ -971,7 +979,7 @@ This is useful to retrieve keys for session key decryption

Source:
@@ -994,6 +1002,35 @@ This is useful to retrieve keys for session key decryption

+
Throws:
+ + + +
+
+
+

if no decryption key is found

+
+
+
+
+
+
+ Type +
+
+ +Error + + +
+
+
+
+
+ + +
Returns:
@@ -1084,7 +1121,7 @@ A dummy key is considered encrypted.

Source:
@@ -1174,7 +1211,7 @@ A dummy key is considered encrypted.

Source:
@@ -1477,7 +1514,7 @@ A dummy key is considered encrypted.

Source:
@@ -1589,7 +1626,7 @@ A dummy key is considered encrypted.

Source:
@@ -1766,7 +1803,7 @@ If only gnu-dummy keys are found, we cannot properly validate so we throw an err
Source:
@@ -1841,7 +1878,7 @@ If only gnu-dummy keys are found, we cannot properly validate so we throw an err
diff --git a/docs/PublicKey.html b/docs/PublicKey.html index d8ec91cf..f6717ddd 100644 --- a/docs/PublicKey.html +++ b/docs/PublicKey.html @@ -144,7 +144,7 @@
Source:
@@ -315,7 +315,7 @@
Source:
@@ -427,7 +427,7 @@
Source:
@@ -535,7 +535,7 @@
Source:
@@ -603,7 +603,7 @@
diff --git a/docs/PublicKeyEncryptedSessionKeyPacket.html b/docs/PublicKeyEncryptedSessionKeyPacket.html index 8807e20c..9d3b1827 100644 --- a/docs/PublicKeyEncryptedSessionKeyPacket.html +++ b/docs/PublicKeyEncryptedSessionKeyPacket.html @@ -107,7 +107,7 @@ decrypt the message.

Source:
@@ -209,7 +209,7 @@ decrypt the message.

Source:
@@ -283,7 +283,7 @@ decrypt the message.

Source:
@@ -458,7 +458,7 @@ This is needed for constant-time processing. Expected object of the form: { sess
Source:
@@ -626,7 +626,7 @@ This is needed for constant-time processing. Expected object of the form: { sess
Source:
@@ -794,7 +794,7 @@ This is needed for constant-time processing. Expected object of the form: { sess
Source:
@@ -884,7 +884,7 @@ This is needed for constant-time processing. Expected object of the form: { sess
Source:
@@ -952,7 +952,7 @@ This is needed for constant-time processing. Expected object of the form: { sess
diff --git a/docs/PublicKeyPacket.html b/docs/PublicKeyPacket.html index a09d1466..d6cc0850 100644 --- a/docs/PublicKeyPacket.html +++ b/docs/PublicKeyPacket.html @@ -195,7 +195,7 @@ key (sometimes called an OpenPGP certificate).

Source:
@@ -301,7 +301,7 @@ key (sometimes called an OpenPGP certificate).

Source:
@@ -375,7 +375,7 @@ key (sometimes called an OpenPGP certificate).

Source:
@@ -449,7 +449,7 @@ key (sometimes called an OpenPGP certificate).

Source:
@@ -523,7 +523,7 @@ key (sometimes called an OpenPGP certificate).

Source:
@@ -597,7 +597,7 @@ key (sometimes called an OpenPGP certificate).

Source:
@@ -671,7 +671,7 @@ key (sometimes called an OpenPGP certificate).

Source:
@@ -735,7 +735,7 @@ key (sometimes called an OpenPGP certificate).

Source:
@@ -816,7 +816,7 @@ key (sometimes called an OpenPGP certificate).

Source:
@@ -880,7 +880,7 @@ key (sometimes called an OpenPGP certificate).

Source:
@@ -1018,7 +1018,7 @@ key (sometimes called an OpenPGP certificate).

Source:
@@ -1130,7 +1130,7 @@ key (sometimes called an OpenPGP certificate).

Source:
@@ -1220,7 +1220,7 @@ key (sometimes called an OpenPGP certificate).

Source:
@@ -1310,7 +1310,7 @@ key (sometimes called an OpenPGP certificate).

Source:
@@ -1422,7 +1422,7 @@ key (sometimes called an OpenPGP certificate).

Source:
@@ -1530,7 +1530,7 @@ key (sometimes called an OpenPGP certificate).

Source:
@@ -1642,7 +1642,7 @@ key (sometimes called an OpenPGP certificate).

Source:
@@ -1754,7 +1754,7 @@ key (sometimes called an OpenPGP certificate).

Source:
@@ -1866,7 +1866,7 @@ key (sometimes called an OpenPGP certificate).

Source:
@@ -1978,7 +1978,7 @@ key (sometimes called an OpenPGP certificate).

Source:
@@ -2138,7 +2138,7 @@ key (sometimes called an OpenPGP certificate).

Source:
@@ -2250,7 +2250,7 @@ key (sometimes called an OpenPGP certificate).

Source:
@@ -2411,7 +2411,7 @@ key (sometimes called an OpenPGP certificate).

Source:
@@ -2457,7 +2457,7 @@ key (sometimes called an OpenPGP certificate).


diff --git a/docs/PublicSubkeyPacket.html b/docs/PublicSubkeyPacket.html index 66cf54ed..6bd9cee6 100644 --- a/docs/PublicSubkeyPacket.html +++ b/docs/PublicSubkeyPacket.html @@ -193,7 +193,7 @@ services.

Source:
@@ -315,7 +315,7 @@ services.

Source:
@@ -394,7 +394,7 @@ services.

Source:
@@ -473,7 +473,7 @@ services.

Source:
@@ -552,7 +552,7 @@ services.

Source:
@@ -631,7 +631,7 @@ services.

Source:
@@ -710,7 +710,7 @@ services.

Source:
@@ -779,7 +779,7 @@ services.

Source:
@@ -865,7 +865,7 @@ services.

Source:
@@ -934,7 +934,7 @@ services.

Source:
@@ -1072,7 +1072,7 @@ services.

Source:
@@ -1189,7 +1189,7 @@ services.

Source:
@@ -1284,7 +1284,7 @@ services.

Source:
@@ -1379,7 +1379,7 @@ services.

Source:
@@ -1496,7 +1496,7 @@ services.

Source:
@@ -1609,7 +1609,7 @@ services.

Source:
@@ -1726,7 +1726,7 @@ services.

Source:
@@ -1843,7 +1843,7 @@ services.

Source:
@@ -1960,7 +1960,7 @@ services.

Source:
@@ -2077,7 +2077,7 @@ services.

Source:
@@ -2242,7 +2242,7 @@ services.

Source:
@@ -2359,7 +2359,7 @@ services.

Source:
@@ -2525,7 +2525,7 @@ services.

Source:
@@ -2571,7 +2571,7 @@ services.


diff --git a/docs/SecretKeyPacket.html b/docs/SecretKeyPacket.html index b7b90a6a..065f5ef8 100644 --- a/docs/SecretKeyPacket.html +++ b/docs/SecretKeyPacket.html @@ -191,7 +191,7 @@ includes the secret-key material after all the public-key fields.

Source:
@@ -308,7 +308,7 @@ includes the secret-key material after all the public-key fields.

Source:
@@ -387,7 +387,7 @@ includes the secret-key material after all the public-key fields.

Source:
@@ -466,7 +466,7 @@ includes the secret-key material after all the public-key fields.

Source:
@@ -545,7 +545,7 @@ includes the secret-key material after all the public-key fields.

Source:
@@ -624,7 +624,7 @@ includes the secret-key material after all the public-key fields.

Source:
@@ -688,7 +688,7 @@ includes the secret-key material after all the public-key fields.

Source:
@@ -767,7 +767,7 @@ includes the secret-key material after all the public-key fields.

Source:
@@ -831,7 +831,7 @@ includes the secret-key material after all the public-key fields.

Source:
@@ -905,7 +905,7 @@ includes the secret-key material after all the public-key fields.

Source:
@@ -984,7 +984,7 @@ includes the secret-key material after all the public-key fields.

Source:
@@ -1053,7 +1053,7 @@ includes the secret-key material after all the public-key fields.

Source:
@@ -1134,7 +1134,7 @@ includes the secret-key material after all the public-key fields.

Source:
@@ -1208,7 +1208,7 @@ includes the secret-key material after all the public-key fields.

Source:
@@ -1282,7 +1282,7 @@ includes the secret-key material after all the public-key fields.

Source:
@@ -1361,7 +1361,7 @@ includes the secret-key material after all the public-key fields.

Source:
@@ -1430,7 +1430,7 @@ includes the secret-key material after all the public-key fields.

Source:
@@ -1519,7 +1519,7 @@ includes the secret-key material after all the public-key fields.

Source:
@@ -1614,7 +1614,7 @@ includes the secret-key material after all the public-key fields.

Source:
@@ -1709,7 +1709,7 @@ includes the secret-key material after all the public-key fields.

Source:
@@ -1851,7 +1851,7 @@ otherwise calls to this function will throw an error.

Source:
@@ -2065,7 +2065,7 @@ This can be used to remove passphrase protection after calling decrypt().

Source:
@@ -2189,7 +2189,7 @@ This can be used to remove passphrase protection after calling decrypt().

Source:
@@ -2306,7 +2306,7 @@ This can be used to remove passphrase protection after calling decrypt().

Source:
@@ -2419,7 +2419,7 @@ This can be used to remove passphrase protection after calling decrypt().

Source:
@@ -2536,7 +2536,7 @@ This can be used to remove passphrase protection after calling decrypt().

Source:
@@ -2653,7 +2653,7 @@ This can be used to remove passphrase protection after calling decrypt().

Source:
@@ -2770,7 +2770,7 @@ This can be used to remove passphrase protection after calling decrypt().

Source:
@@ -2888,7 +2888,7 @@ Returns false for gnu-dummy keys and null for public keys.

Source:
@@ -2999,7 +2999,7 @@ Returns false for gnu-dummy keys and null for public keys.

Source:
@@ -3114,7 +3114,7 @@ Such keys are:

Source:
@@ -3266,7 +3266,7 @@ The resulting key cannot be used for signing/decrypting but can still verify sig
Source:
@@ -3411,7 +3411,7 @@ The resulting key cannot be used for signing/decrypting but can still verify sig
Source:
@@ -3501,7 +3501,7 @@ The resulting key cannot be used for signing/decrypting but can still verify sig
Source:
@@ -3625,7 +3625,7 @@ The resulting key cannot be used for signing/decrypting but can still verify sig
Source:
@@ -3791,7 +3791,7 @@ The resulting key cannot be used for signing/decrypting but can still verify sig
Source:
@@ -3837,7 +3837,7 @@ The resulting key cannot be used for signing/decrypting but can still verify sig
diff --git a/docs/SecretSubkeyPacket.html b/docs/SecretSubkeyPacket.html index 2020daa9..567923fa 100644 --- a/docs/SecretSubkeyPacket.html +++ b/docs/SecretSubkeyPacket.html @@ -190,7 +190,7 @@ Key packet and has exactly the same format.

Source:
@@ -312,7 +312,7 @@ Key packet and has exactly the same format.

Source:
@@ -391,7 +391,7 @@ Key packet and has exactly the same format.

Source:
@@ -470,7 +470,7 @@ Key packet and has exactly the same format.

Source:
@@ -549,7 +549,7 @@ Key packet and has exactly the same format.

Source:
@@ -628,7 +628,7 @@ Key packet and has exactly the same format.

Source:
@@ -697,7 +697,7 @@ Key packet and has exactly the same format.

Source:
@@ -776,7 +776,7 @@ Key packet and has exactly the same format.

Source:
@@ -845,7 +845,7 @@ Key packet and has exactly the same format.

Source:
@@ -924,7 +924,7 @@ Key packet and has exactly the same format.

Source:
@@ -1003,7 +1003,7 @@ Key packet and has exactly the same format.

Source:
@@ -1072,7 +1072,7 @@ Key packet and has exactly the same format.

Source:
@@ -1158,7 +1158,7 @@ Key packet and has exactly the same format.

Source:
@@ -1237,7 +1237,7 @@ Key packet and has exactly the same format.

Source:
@@ -1316,7 +1316,7 @@ Key packet and has exactly the same format.

Source:
@@ -1395,7 +1395,7 @@ Key packet and has exactly the same format.

Source:
@@ -1464,7 +1464,7 @@ Key packet and has exactly the same format.

Source:
@@ -1558,7 +1558,7 @@ Key packet and has exactly the same format.

Source:
@@ -1653,7 +1653,7 @@ Key packet and has exactly the same format.

Source:
@@ -1748,7 +1748,7 @@ Key packet and has exactly the same format.

Source:
@@ -1895,7 +1895,7 @@ otherwise calls to this function will throw an error.

Source:
@@ -2114,7 +2114,7 @@ This can be used to remove passphrase protection after calling decrypt().

Source:
@@ -2238,7 +2238,7 @@ This can be used to remove passphrase protection after calling decrypt().

Source:
@@ -2355,7 +2355,7 @@ This can be used to remove passphrase protection after calling decrypt().

Source:
@@ -2468,7 +2468,7 @@ This can be used to remove passphrase protection after calling decrypt().

Source:
@@ -2585,7 +2585,7 @@ This can be used to remove passphrase protection after calling decrypt().

Source:
@@ -2702,7 +2702,7 @@ This can be used to remove passphrase protection after calling decrypt().

Source:
@@ -2819,7 +2819,7 @@ This can be used to remove passphrase protection after calling decrypt().

Source:
@@ -2937,7 +2937,7 @@ Returns false for gnu-dummy keys and null for public keys.

Source:
@@ -3053,7 +3053,7 @@ Returns false for gnu-dummy keys and null for public keys.

Source:
@@ -3173,7 +3173,7 @@ Such keys are:

Source:
@@ -3330,7 +3330,7 @@ The resulting key cannot be used for signing/decrypting but can still verify sig
Source:
@@ -3475,7 +3475,7 @@ The resulting key cannot be used for signing/decrypting but can still verify sig
Source:
@@ -3570,7 +3570,7 @@ The resulting key cannot be used for signing/decrypting but can still verify sig
Source:
@@ -3694,7 +3694,7 @@ The resulting key cannot be used for signing/decrypting but can still verify sig
Source:
@@ -3860,7 +3860,7 @@ The resulting key cannot be used for signing/decrypting but can still verify sig
Source:
@@ -3906,7 +3906,7 @@ The resulting key cannot be used for signing/decrypting but can still verify sig
diff --git a/docs/Signature.html b/docs/Signature.html index 1de23bea..60ea8fd1 100644 --- a/docs/Signature.html +++ b/docs/Signature.html @@ -144,7 +144,7 @@
Source:
@@ -322,7 +322,7 @@
Source:
@@ -434,7 +434,7 @@
Source:
@@ -546,7 +546,7 @@
Source:
@@ -614,7 +614,7 @@
diff --git a/docs/SignaturePacket.html b/docs/SignaturePacket.html index 4c6ce868..a44a3a8b 100644 --- a/docs/SignaturePacket.html +++ b/docs/SignaturePacket.html @@ -99,7 +99,7 @@ block of text, and a signature that is a certification of a User ID.

Source:
@@ -201,7 +201,7 @@ block of text, and a signature that is a certification of a User ID.

Source:
@@ -271,7 +271,7 @@ block of text, and a signature that is a certification of a User ID.

Source:
@@ -341,7 +341,7 @@ block of text, and a signature that is a certification of a User ID.

Source:
@@ -423,7 +423,7 @@ block of text, and a signature that is a certification of a User ID.

Source:
@@ -599,7 +599,7 @@ block of text, and a signature that is a certification of a User ID.

Source:
@@ -760,7 +760,7 @@ block of text, and a signature that is a certification of a User ID.

Source:
@@ -1048,7 +1048,7 @@ block of text, and a signature that is a certification of a User ID.

Source:
@@ -1427,7 +1427,7 @@ block of text, and a signature that is a certification of a User ID.

Source:
@@ -1546,7 +1546,7 @@ block of text, and a signature that is a certification of a User ID.

Source:
@@ -1654,7 +1654,7 @@ block of text, and a signature that is a certification of a User ID.

Source:
@@ -1765,7 +1765,7 @@ block of text, and a signature that is a certification of a User ID.

Source:
@@ -1833,7 +1833,7 @@ block of text, and a signature that is a certification of a User ID.


diff --git a/docs/SymEncryptedIntegrityProtectedDataPacket.html b/docs/SymEncryptedIntegrityProtectedDataPacket.html index 8c35b427..9e1863d8 100644 --- a/docs/SymEncryptedIntegrityProtectedDataPacket.html +++ b/docs/SymEncryptedIntegrityProtectedDataPacket.html @@ -101,7 +101,7 @@ packet.

Source:
@@ -147,6 +147,150 @@ packet.

+

Members

+ + + +

aeadAlgorithm :enums.aead

+ + + + + + +
Type:
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

cipherAlgorithm :enums.symmetric

+ + + + + + +
Type:
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + +

Methods

@@ -334,7 +478,7 @@ packet.

Source:
@@ -594,7 +738,7 @@ packet.

Source:
@@ -687,7 +831,7 @@ packet.


diff --git a/docs/SymEncryptedSessionKeyPacket.html b/docs/SymEncryptedSessionKeyPacket.html index 789e85a6..91f8b57a 100644 --- a/docs/SymEncryptedSessionKeyPacket.html +++ b/docs/SymEncryptedSessionKeyPacket.html @@ -165,7 +165,7 @@ the Symmetric-Key Encrypted Session Key packet.

Source:
@@ -271,7 +271,7 @@ the Symmetric-Key Encrypted Session Key packet.

Source:
@@ -345,7 +345,7 @@ the Symmetric-Key Encrypted Session Key packet.

Source:
@@ -419,7 +419,7 @@ the Symmetric-Key Encrypted Session Key packet.

Source:
@@ -550,7 +550,7 @@ the Symmetric-Key Encrypted Session Key packet.

Source:
@@ -761,7 +761,7 @@ the Symmetric-Key Encrypted Session Key packet.

Source:
@@ -929,7 +929,7 @@ the Symmetric-Key Encrypted Session Key packet.

Source:
@@ -1019,7 +1019,7 @@ the Symmetric-Key Encrypted Session Key packet.

Source:
@@ -1087,7 +1087,7 @@ the Symmetric-Key Encrypted Session Key packet.


diff --git a/docs/SymmetricallyEncryptedDataPacket.html b/docs/SymmetricallyEncryptedDataPacket.html index 4a453c0e..74cbb54e 100644 --- a/docs/SymmetricallyEncryptedDataPacket.html +++ b/docs/SymmetricallyEncryptedDataPacket.html @@ -101,7 +101,7 @@ that form whole OpenPGP messages).

Source:
@@ -197,7 +197,7 @@ that form whole OpenPGP messages).

Source:
@@ -271,7 +271,7 @@ that form whole OpenPGP messages).

Source:
@@ -477,7 +477,7 @@ See RFC 4880 9.2 f
Source:
@@ -720,7 +720,7 @@ See RFC 4880 9.2 f
Source:
@@ -795,7 +795,7 @@ See RFC 4880 9.2 f
diff --git a/docs/TrustPacket.html b/docs/TrustPacket.html index e6e775f9..ba1e690f 100644 --- a/docs/TrustPacket.html +++ b/docs/TrustPacket.html @@ -105,7 +105,7 @@ other than local keyring files.

Source:
@@ -216,7 +216,7 @@ Currently not implemented as we ignore trust packets

Source:
@@ -262,7 +262,7 @@ Currently not implemented as we ignore trust packets


diff --git a/docs/UserAttributePacket.html b/docs/UserAttributePacket.html index 43640c78..9781021c 100644 --- a/docs/UserAttributePacket.html +++ b/docs/UserAttributePacket.html @@ -107,7 +107,7 @@ an implementation may use any method desired.

Source:
@@ -266,7 +266,7 @@ an implementation may use any method desired.

Source:
@@ -427,7 +427,7 @@ an implementation may use any method desired.

Source:
@@ -517,7 +517,7 @@ an implementation may use any method desired.

Source:
@@ -585,7 +585,7 @@ an implementation may use any method desired.


diff --git a/docs/UserIDPacket.html b/docs/UserIDPacket.html index f81007d5..ea8111cd 100644 --- a/docs/UserIDPacket.html +++ b/docs/UserIDPacket.html @@ -100,7 +100,7 @@ specifies the length of the User ID.

Source:
@@ -207,7 +207,7 @@ John Doe john@example.com

Source:
@@ -338,7 +338,7 @@ John Doe john@example.com

Source:
@@ -495,7 +495,7 @@ John Doe john@example.com

Source:
@@ -585,7 +585,7 @@ John Doe john@example.com

Source:
@@ -653,7 +653,7 @@ John Doe john@example.com


diff --git a/docs/global.html b/docs/global.html index 2812d70b..f05ae33e 100644 --- a/docs/global.html +++ b/docs/global.html @@ -106,98 +106,7 @@ -

aes()

- - - - - - -
-

Javascript AES implementation. -This is used as fallback if the native Crypto APIs are not available.

-
- - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -

armor(messageType, body, partIndexopt, partTotalopt, customCommentopt) → {String|ReadableStream.<String>}

+

armor(messageType, body, partIndexopt, partTotalopt, customCommentopt, emitChecksumopt, configopt) → {String|ReadableStream.<String>}

@@ -404,6 +313,73 @@ This is used as fallback if the native Crypto APIs are not available.

+ + + + emitChecksum + + + + + +Boolean + + + + + + + + + <optional>
+ + + + + + + + + + +

Whether to compute and include the CRC checksum +(NB: some types of data must not include it, but compliance is left as responsibility of the caller: this function does not carry out any checks)

+ + + + + + + config + + + + + +Object + + + + + + + + + <optional>
+ + + + + + + + + + +

Full configuration, defaults to openpgp.config

+ + + @@ -443,7 +419,7 @@ This is used as fallback if the native Crypto APIs are not available.

Source:
@@ -656,7 +632,7 @@ This is used as fallback if the native Crypto APIs are not available.

Source:
@@ -795,7 +771,7 @@ This is used as fallback if the native Crypto APIs are not available.

Source:
@@ -1204,7 +1180,7 @@ This is used as fallback if the native Crypto APIs are not available.

Source:
@@ -1785,7 +1761,7 @@ One of decryptionKeys, sessionkeys or passwords<
Source:
@@ -2088,7 +2064,7 @@ This method does not change the original key.

Source:
@@ -2447,7 +2423,7 @@ One of decryptionKeys or passwords must be specified.<
Source:
@@ -3251,7 +3227,7 @@ must be specified. If signing keys are specified, those will be used to sign the
Source:
@@ -3539,7 +3515,7 @@ This method does not change the original key.

Source:
@@ -4159,7 +4135,7 @@ At least one of encryptionKeys or passwords must be sp
Source:
@@ -4375,7 +4351,7 @@ At least one of encryptionKeys or passwords must be sp
Source:
@@ -4443,7 +4419,8 @@ At least one of encryptionKeys or passwords must be sp
-

Generates a new OpenPGP key pair. Supports RSA and ECC keys. By default, primary and subkeys will be of same type. +

Generates a new OpenPGP key pair. Supports RSA and ECC keys, as well as the newer Curve448 and Curve25519 keys. +By default, primary and subkeys will be of same type. The generated primary key will have signing capabilities. By default, one subkey with encryption capabilities is also generated.

@@ -4574,6 +4551,12 @@ The generated primary key will have signing capabilities. By default, one subkey | 'rsa' +| + +'curve448' +| + +'curve25519' @@ -4599,7 +4582,8 @@ The generated primary key will have signing capabilities. By default, one subkey -

The primary key algorithm type: ECC (default) or RSA

+

The primary key algorithm type: ECC (default for v4 keys), RSA, Curve448 or Curve25519 (new format, default for v6 keys). +Note: Curve448 and Curve25519 (new format) are not widely supported yet.

@@ -4711,13 +4695,13 @@ The generated primary key will have signing capabilities. By default, one subkey - 'curve25519' + 'curve25519Legacy'

Elliptic curve for ECC keys: -curve25519 (default), p256, p384, p521, secp256k1, +curve25519Legacy (default), nistP256, nistP384, nistP521, secp256k1, brainpoolP256r1, brainpoolP384r1, or brainpoolP512r1

@@ -4968,7 +4952,7 @@ default to main key options, except for sign parameter that default
Source:
@@ -5318,7 +5302,7 @@ default to main key options, except for sign parameter that default
Source:
@@ -5376,6 +5360,591 @@ default to main key options, except for sign parameter that default +

getCipherBlockSize(algo)

+ + + + + + +
+

Get block size for given cipher algo

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +module:enums.symmetric + + + +

alrogithm identifier

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

getCipherKeySize(algo)

+ + + + + + +
+

Get key size for given cipher algo

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +module:enums.symmetric + + + +

alrogithm identifier

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

getCipherParams(algo)

+ + + + + + +
+

Get block and key size for given cipher algo

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +module:enums.symmetric + + + +

alrogithm identifier

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

getCompressionStreamInstantiators(compressionFormat) → {Object}

+ + + + + + +
+

Get Compression Stream API instatiators if the constructors are implemented. +NB: the return instatiator functions will throw when called if the provided compressionFormat is not supported +(supported formats cannot be determined in advance).

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
compressionFormat + + +'deflate-raw' +| + +'deflate' +| + +'gzip' +| + +string + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Object + + +
+
+ + + + + + + + + + + + +

newPacketFromTag(tag, allowedPackets) → {Object}

@@ -5502,7 +6071,7 @@ default to main key options, except for sign parameter that default
Source:
@@ -5592,6 +6161,688 @@ default to main key options, except for sign parameter that default +

newS2KFromConfig() → {Object}

+ + + + + + +
+

Instantiate a new S2K instance based on the config settings

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Throws:
+ + + +
+
+
+

for unknown or unsupported types

+
+
+
+
+
+
+ Type +
+
+ +Error + + +
+
+
+
+
+ + + + + +
Returns:
+ + +
+

New s2k object

+
+ + + +
+
+ Type +
+
+ +Object + + +
+
+ + + + + + + + + + + + + +

newS2KFromType(type) → {Object}

+ + + + + + +
+

Instantiate a new S2K instance of the given type

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
type + + +module:enums.s2k + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Throws:
+ + + +
+
+
+

for unknown or unsupported types

+
+
+
+
+
+
+ Type +
+
+ +Error + + +
+
+
+
+
+ + + + + +
Returns:
+ + +
+

New s2k object

+
+ + + +
+
+ Type +
+
+ +Object + + +
+
+ + + + + + + + + + + + + +

(async) produceEncryptionKey(keyVersion, s2k, passphrase, cipherAlgo, aeadModeopt, serializedPacketTagopt, isLegacyAEADopt)

+ + + + + + +
+

Derive encryption key

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
keyVersion + + +Number + + + + + + + + + +

key derivation differs for v5 keys

s2k + + +module:type/s2k + + + + + + + + + +
passphrase + + +String + + + + + + + + + +
cipherAlgo + + +module:enums.symmetric + + + + + + + + + +
aeadMode + + +module:enums.aead + + + + + + <optional>
+ + + + + +

for AEAD-encrypted keys only (excluding v5)

serializedPacketTag + + +Uint8Array + + + + + + <optional>
+ + + + + +

for AEAD-encrypted keys only (excluding v5)

isLegacyAEAD + + +Boolean + + + + + + <optional>
+ + + + + +

for AEAD-encrypted keys from RFC4880bis (v4 and v5 only)

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

encryption key

+
+ + + + + + + + + + + + + + +

(async) readCleartextMessage(options) → {Promise.<CleartextMessage>}

@@ -5787,7 +7038,7 @@ default to main key options, except for sign parameter that default
Source:
@@ -6075,7 +7326,7 @@ default to main key options, except for sign parameter that default
Source:
@@ -6363,7 +7614,7 @@ default to main key options, except for sign parameter that default
Source:
@@ -6657,7 +7908,7 @@ default to main key options, except for sign parameter that default
Source:
@@ -6945,7 +8196,7 @@ default to main key options, except for sign parameter that default
Source:
@@ -7233,7 +8484,7 @@ default to main key options, except for sign parameter that default
Source:
@@ -7521,7 +8772,7 @@ default to main key options, except for sign parameter that default
Source:
@@ -7983,7 +9234,7 @@ to set the same date as the key creation time to ensure that old message signatu
Source:
@@ -8512,7 +9763,7 @@ If a revocation certificate is passed, the reasonForRevocation parameter will be
Source:
@@ -8572,6 +9823,215 @@ If a revocation certificate is passed, the reasonForRevocation parameter will be +

(async) runAEAD(fn, key, data) → {Promise.<(Uint8Array|ReadableStream.<Uint8Array>)>}

+ + + + + + +
+

En/decrypt the payload.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
fn + + +encrypt +| + +decrypt + + + +

Whether to encrypt or decrypt

key + + +Uint8Array + + + +

The session key used to en/decrypt the payload

data + + +Uint8Array +| + +ReadableStream.<Uint8Array> + + + +

The data to en/decrypt

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Promise.<(Uint8Array|ReadableStream.<Uint8Array>)> + + +
+
+ + + + + + + + + + + + +

(async) sign(options) → {Promise.<MaybeStream.<(String|Uint8Array)>>}

@@ -9067,7 +10527,7 @@ If a revocation certificate is passed, the reasonForRevocation parameter will be
Source:
@@ -9229,7 +10689,7 @@ the encoded bytes

Source:
@@ -9691,7 +11151,7 @@ an attribute "data" containing a stream of bytes and "type"
Source:
@@ -9936,7 +11396,7 @@ The new key includes a revocation certificate that must be removed before return
Source:
@@ -9985,6 +11445,190 @@ The new key includes a revocation certificate that must be removed before return + + + + + +

zlib(compressionStreamInstantiator, ZlibStreamedConstructor) → {ReadableStream.<Uint8Array>}

+ + + + + + +
+

Zlib processor relying on Compression Stream API if available, or falling back to fflate otherwise.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
compressionStreamInstantiator + + +function + + + +
ZlibStreamedConstructor + + +FunctionConstructor + + + +

fflate constructor

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

compressed or decompressed data

+
+ + + +
+
+ Type +
+
+ +ReadableStream.<Uint8Array> + + +
+
+ + + + + + + + @@ -10000,7 +11644,7 @@ The new key includes a revocation certificate that must be removed before return
diff --git a/docs/index.html b/docs/index.html index 59ac47bd..6144c371 100644 --- a/docs/index.html +++ b/docs/index.html @@ -44,7 +44,7 @@

OpenPGP.js BrowserStack Status Join the chat on Gitter

-

OpenPGP.js is a JavaScript implementation of the OpenPGP protocol. It implements RFC4880 and parts of RFC4880bis.

+

OpenPGP.js is a JavaScript implementation of the OpenPGP protocol. It implements the crypto-refresh (superseding RFC4880 and RFC4880bis).

Table of Contents

  • OpenPGP.js @@ -85,24 +85,24 @@

    Platform Support

    • -

      The dist/openpgp.min.js bundle works well with recent versions of Chrome, Firefox, Safari and Edge.

      +

      The dist/openpgp.min.js (or .mjs) bundle works with recent versions of Chrome, Firefox, Edge and Safari 14+.

    • -

      The dist/node/openpgp.min.js bundle works well in Node.js. It is used by default when you require('openpgp') in Node.js.

      +

      The dist/node/openpgp.min.mjs (or .cjs) bundle works in Node.js v18+: it is used by default when you import ... from 'openpgp' (resp. require('openpgp')).

    • -

      Currently, Chrome, Safari and Edge have partial implementations of the -Streams specification, and Firefox -has a partial implementation behind feature flags. Chrome is the only -browser that implements TransformStreams, which we need, so we include -a polyfill for -all other browsers. Please note that in those browsers, the global -ReadableStream property gets overwritten with the polyfill version if -it exists. In some edge cases, you might need to use the native +

      Streaming support: the latest versions of Chrome, Firefox, Edge and Safari implement the +Streams specification, including TransformStreams. +These are needed if you use the library with streamed inputs. +In previous versions of OpenPGP.js, WebStreams were automatically polyfilled by the library, +but from v6 this task is left up to the library user, due to the more extensive browser support, and the +polyfilling side-effects. If you're working with older browsers versions which do not implement e.g. TransformStreams, you can manually +load WebStream polyfill. +Please note that when you load the polyfills, the global ReadableStream property (if it exists) gets overwritten with the polyfill version. +In some edge cases, you might need to use the native ReadableStream (for example when using it to create a Response object), in which case you should store a reference to it before loading -OpenPGP.js. There is also the -web-streams-adapter +the polyfills. There is also the web-streams-adapter library to convert back and forth between them.

    @@ -127,40 +127,40 @@ library to convert back and forth between them.

    ECDH N/A No -No -Algorithmically** +Yes* +If native** ed25519 N/A EdDSA No -No -Algorithmically** +Yes* +If native** -p256 +nistP256 ECDH ECDSA Yes* Yes* -If native*** +If native** -p384 +nistP384 ECDH ECDSA Yes* Yes* -If native*** +If native** -p521 +nistP521 ECDH ECDSA Yes* Yes* -If native*** +If native** brainpoolP256r1 @@ -168,7 +168,7 @@ library to convert back and forth between them.

    ECDSA Yes* No -If native*** +If native** brainpoolP384r1 @@ -176,7 +176,7 @@ library to convert back and forth between them.

    ECDSA Yes* No -If native*** +If native** brainpoolP512r1 @@ -184,7 +184,7 @@ library to convert back and forth between them.

    ECDSA Yes* No -If native*** +If native** secp256k1 @@ -192,30 +192,27 @@ library to convert back and forth between them.

    ECDSA Yes* No -If native*** +If native** -

    * when available
    -** the curve25519 and ed25519 implementations are algorithmically constant-time, but may not be constant-time after optimizations of the JavaScript compiler
    -*** these curves are only constant-time if the underlying native implementation is available and constant-time

    -
  • -
  • -

    Version 2.x of the library has been built from the ground up with Uint8Arrays. This allows for much better performance and memory usage than strings.

    +

    * when available +** these curves are only constant-time if the underlying native implementation is available and constant-time

  • If the user's browser supports native WebCrypto via the window.crypto.subtle API, this will be used. Under Node.js the native crypto module is used.

  • -

    The library implements the RFC4880bis proposal for authenticated encryption using native AES-EAX, OCB, or GCM. This makes symmetric encryption up to 30x faster on supported platforms. Since the specification has not been finalized and other OpenPGP implementations haven't adopted it yet, the feature is currently behind a flag. Note: activating this setting can break compatibility with other OpenPGP implementations, and also with future versions of OpenPGP.js. Don't use it with messages you want to store on disk or in a database. You can enable it by setting openpgp.config.aeadProtect = true.

    +

    The library implements authenticated encryption (AEAD) as per the "crypto refresh" draft standard using AES-OCB, EAX, or GCM. This makes symmetric encryption faster on platforms with native implementations. However, since the specification is very recent and other OpenPGP implementations are in the process of adopting it, the feature is currently behind a flag. Note: activating this setting can break compatibility with other OpenPGP implementations which have yet to implement the feature. You can enable it by setting openpgp.config.aeadProtect = true. +Note that this setting has a different effect from the one in OpenPGP.js v6, which implemented support for a provisional version of AEAD from RFC4880bis, which was modified in a later draft of the crypto refresh.

    You can change the AEAD mode by setting one of the following options:

    -
    openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.eax // Default, native
    -openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.ocb // Non-native
    -openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.experimentalGCM // **Non-standard**, fastest
    +
    openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.ocb; // Default (widest ecosystem support), non-native
    +openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.gcm; // Native in WebCrypto and Node.js
    +openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.eax; // Native in Node.js
     
  • -

    For environments that don't provide native crypto, the library falls back to asm.js implementations of AES, SHA-1, and SHA-256.

    +

    For environments that don't provide native crypto, the library falls back to asm.js AES and AEAD implementations.

Getting started

@@ -223,12 +220,12 @@ openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.experimentalGCM // **

Install OpenPGP.js using npm and save it in your dependencies:

npm install --save openpgp
 
-

And import it as a CommonJS module:

-
const openpgp = require('openpgp');
-
-

Or as an ES6 module, from an .mjs file:

+

And import it as an ES module, from a .mjs file:

import * as openpgp from 'openpgp';
 
+

Or as a CommonJS module:

+
const openpgp = require('openpgp');
+

Deno (experimental)

Import as an ES6 module, using /dist/openpgp.mjs.

import * as openpgp from './openpgpjs/dist/openpgp.mjs';
@@ -262,12 +259,12 @@ import * as openpgp from './openpgp.min.mjs';
 

To offload cryptographic operations off the main thread, you can implement a Web Worker in your application and load OpenPGP.js from there. For an example Worker implementation, see test/worker/worker_example.js.

TypeScript

-

Since TS is not fully integrated in the library, TS-only dependencies are currently listed as devDependencies, so to compile the project you’ll need to add @openpgp/web-stream-tools manually (NB: only versions below v0.12 are compatible with OpenPGP.js v5):

-
npm install --save-dev @openpgp/web-stream-tools@0.0.11-patch-0
+

Since TS is not fully integrated in the library, TS-only dependencies are currently listed as devDependencies, so to compile the project you’ll need to add @openpgp/web-stream-tools manually:

+
npm install --save-dev @openpgp/web-stream-tools
 

If you notice missing or incorrect type definitions, feel free to open a PR.

Examples

-

Here are some examples of how to use OpenPGP.js v5. For more elaborate examples and working code, please check out the public API unit tests. If you're upgrading from v4 it might help to check out the changelog and documentation.

+

Here are some examples of how to use OpenPGP.js v6. For more elaborate examples and working code, please check out the public API unit tests. If you're upgrading from v4 it might help to check out the changelog and documentation.

Encrypt and decrypt Uint8Array data with a password

Encryption will use the algorithm specified in config.preferredSymmetricAlgorithm (defaults to aes256), and decryption will use the algorithm used for encryption.

(async () => {
@@ -503,7 +500,7 @@ can .pipe() to a Writable stream, for example.

Generate new key pair

ECC keys (smaller and faster to generate):

-

Possible values for curve are: curve25519, ed25519, p256, p384, p521, +

Possible values for curve are: curve25519, ed25519, nistP256, nistP384, nistP521, brainpoolP256r1, brainpoolP384r1, brainpoolP512r1, and secp256k1. Note that both the curve25519 and ed25519 options generate a primary key for signing using Ed25519 and a subkey for encryption using Curve25519.

@@ -708,7 +705,7 @@ and a subkey for encryption using Curve25519.


diff --git a/docs/module-config.html b/docs/module-config.html index 1c159171..f0eeb13f 100644 --- a/docs/module-config.html +++ b/docs/module-config.html @@ -89,7 +89,7 @@
Source:
@@ -247,7 +247,7 @@ as a global config setting, but can be used for specific function calls (e.g. de
Source:
@@ -365,7 +365,7 @@ Must be an integer value from 0 to 56.

Source:
@@ -390,8 +390,14 @@ Must be an integer value from 0 to 56.

Use Authenticated Encryption with Additional Data (AEAD) protection for symmetric encryption. -Note: not all OpenPGP implementations are compatible with this option. -FUTURE OPENPGP.JS VERSIONS MAY BREAK COMPATIBILITY WHEN USING THIS OPTION

+This option is applicable to:

+
    +
  • key generation (encryption key preferences),
  • +
  • password-based message encryption, and
  • +
  • private key encryption. +In the case of message encryption using public keys, the encryption key preferences are respected instead. +Note: not all OpenPGP implementations are compatible with this option.
  • +
@@ -483,7 +489,7 @@ Note: not all OpenPGP implementations are compatible with this option.
Source:
@@ -493,7 +499,7 @@ Note: not all OpenPGP implementations are compatible with this option.
See:
@@ -608,7 +614,7 @@ where key flags were ignored when selecting a key for encryption.

Source:
@@ -727,7 +733,7 @@ and have self-signature's creation date that does not match the primary key crea
Source:
@@ -848,7 +854,7 @@ This is an insecure setting:

Source:
@@ -875,7 +881,13 @@ This is an insecure setting:

Allow streaming unauthenticated data before its integrity has been checked. This would allow the application to process large streams while limiting memory usage by releasing the decrypted chunks as soon as possible and deferring checking their integrity until the decrypted stream has been read in full.

-

This setting is insecure if the partially decrypted message is processed further or displayed to the user.

+

This setting is insecure if the encrypted data has been corrupted by a malicious entity:

+
    +
  • if the partially decrypted message is processed further or displayed to the user, it opens up the possibility of attacks such as EFAIL +(see https://efail.de/).
  • +
  • an attacker with access to traces or timing info of internal processing errors could learn some info about the data.
  • +
+

NB: this setting does not apply to AEAD-encrypted data, where the AEAD data chunk is never released until integrity is confirmed.

@@ -967,119 +979,7 @@ and deferring checking their integrity until the decrypted stream has been read
Source:
- - - - - - - -
- - - - - - - - -

(static) checksumRequired

- - - - - - - - - - -
Properties:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
checksumRequired - - -Boolean - - - -

Do not throw error when armor is missing a checksum

- - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
@@ -1191,7 +1091,7 @@ and deferring checking their integrity until the decrypted stream has been read
Source:
@@ -1313,7 +1213,7 @@ See also constantTimePKCS1DecryptionSupportedSymmetricAlgorithms.Source:
@@ -1431,119 +1331,7 @@ However, the more algorithms are added, the slower the decryption procedure beco
Source:
- - - - - - - -
- - - - - - - - -

(static) deflateLevel

- - - - - - - - - - -
Properties:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
deflateLevel - - -Integer - - - -

Default zip/zlib compression level, between 1 and 9

- - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
@@ -1655,7 +1443,7 @@ However, the more algorithms are added, the slower the decryption procedure beco
Source:
@@ -1767,7 +1555,7 @@ However, the more algorithms are added, the slower the decryption procedure beco
Source:
@@ -1884,7 +1672,7 @@ validation error when the notation is marked as critical.

Source:
@@ -2000,119 +1788,7 @@ validation error when the notation is marked as critical.

Source:
- - - - - - - -
- - - - - - - - -

(static) minBytesForWebCrypto

- - - - - - - - - - -
Properties:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
minBytesForWebCrypto - - -Integer - - - -

The minimum amount of bytes for which to use native WebCrypto APIs when available

- - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
@@ -2229,7 +1905,7 @@ The default is 2047 since due to a bug, previous versions of OpenPGP.js could ge
Source:
@@ -2346,7 +2022,7 @@ The default is 2047 since due to a bug, previous versions of OpenPGP.js could ge
Source:
@@ -2463,7 +2139,7 @@ Only has an effect when aeadProtect is set to true.

Source:
@@ -2575,7 +2251,7 @@ Only has an effect when aeadProtect is set to true.

Source:
@@ -2687,7 +2363,7 @@ Only has an effect when aeadProtect is set to true.

Source:
@@ -2799,7 +2475,7 @@ Only has an effect when aeadProtect is set to true.

Source:
@@ -2915,7 +2591,7 @@ Only has an effect when aeadProtect is set to true.

Source:
@@ -3031,7 +2707,7 @@ Only has an effect when aeadProtect is set to true.

Source:
@@ -3147,7 +2823,7 @@ Only has an effect when aeadProtect is set to true.

Source:
@@ -3263,7 +2939,7 @@ Only has an effect when aeadProtect is set to true.

Source:
@@ -3281,11 +2957,20 @@ Only has an effect when aeadProtect is set to true.

-

(static) revocationsExpire

+

(static) s2kArgon2Params

+
+

draft-crypto-refresh 3.7.1.4: +Argon2 parameters for S2K (String to Key). +Only relevant if config.s2kType is set to enums.s2k.argon2. +Default settings correspond to the second recommendation from RFC9106 ("uniformly safe option"), +to ensure compatibility with memory-constrained environments. +For more details on the choice of parameters, see https://tools.ietf.org/html/rfc9106#section-4.

+
+ @@ -3318,13 +3003,13 @@ Only has an effect when aeadProtect is set to true.

- revocationsExpire + params -Boolean +Object @@ -3334,7 +3019,101 @@ Only has an effect when aeadProtect is set to true.

-

If true, expired revocation signatures are ignored

+ +
Properties
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
passes + + +Integer + + + +

number of iterations t

parallelism + + +Integer + + + +

degree of parallelism p

memoryExponent + + +Integer + + + +

one-octet exponent indicating the memory size, which will be: 2**memoryExponent kibibytes.

+ + @@ -3375,7 +3154,7 @@ Only has an effect when aeadProtect is set to true.

Source:
@@ -3399,8 +3178,10 @@ Only has an effect when aeadProtect is set to true.

-

RFC4880 3.7.1.3: -Iteration Count Byte for S2K (String to Key)

+

RFC4880 3.7.1.3: +Iteration Count Byte for Iterated and Salted S2K (String to Key). +Only relevant if config.s2kType is set to enums.s2k.iterated. +Note: this is the exponent value, not the final number of iterations (refer to specs for more details).

@@ -3492,7 +3273,129 @@ Iteration Count Byte for S2K (String to Key)

Source:
+ + + + + + + +
+ + + + + + + + +

(static) s2kType

+ + + + +
+

S2K (String to Key) type, used for key derivation in the context of secret key encryption +and password-encrypted data. Weaker s2k options are not allowed. +Note: Argon2 is the strongest option but not all OpenPGP implementations are compatible with it +(pending standardisation).

+
+ + + + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
s2kType + + +enums.s2k.argon2 +| + +enums.s2k.iterated + + + +

module:enums.s2k

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
@@ -3604,7 +3507,7 @@ Iteration Count Byte for S2K (String to Key)

Source:
@@ -3716,7 +3619,7 @@ Iteration Count Byte for S2K (String to Key)

Source:
@@ -3734,15 +3637,14 @@ Iteration Count Byte for S2K (String to Key)

-

(static) useIndutnyElliptic

+

(static) useEllipticFallback

-

Whether to use the indutny/elliptic library for curves (other than Curve25519) that are not supported by the available native crypto API. -When false, certain standard curves will not be supported (depending on the platform). -Note: the indutny/elliptic curve library is not designed to be constant time.

+

Whether to use the the noble-curves library for curves (other than Curve25519) that are not supported by the available native crypto API. +When false, certain standard curves will not be supported (depending on the platform).

@@ -3777,7 +3679,7 @@ Note: the indutny/elliptic curve library is not designed to be constant time.

- useIndutnyElliptic + useEllipticFallback @@ -3834,7 +3736,7 @@ Note: the indutny/elliptic curve library is not designed to be constant time.

Source:
@@ -3852,13 +3754,13 @@ Note: the indutny/elliptic curve library is not designed to be constant time.

(static) v5Keys +

(static) v6Keys

-

Use V5 keys. +

Use v6 keys. Note: not all OpenPGP implementations are compatible with this option. FUTURE OPENPGP.JS VERSIONS MAY BREAK COMPATIBILITY WHEN USING THIS OPTION

@@ -3895,7 +3797,7 @@ Note: not all OpenPGP implementations are compatible with this option. - v5Keys + v6Keys @@ -3952,7 +3854,7 @@ Note: not all OpenPGP implementations are compatible with this option.
Source:
@@ -4064,7 +3966,7 @@ Note: not all OpenPGP implementations are compatible with this option.
Source:
@@ -4098,7 +4000,7 @@ Note: not all OpenPGP implementations are compatible with this option.
diff --git a/docs/module-crypto.html b/docs/module-crypto.html new file mode 100644 index 00000000..447bb1b7 --- /dev/null +++ b/docs/module-crypto.html @@ -0,0 +1,184 @@ + + + + + JSDoc: Module: crypto + + + + + + + + + + +
+ +

Module: crypto

+ + + + + + +
+ +
+ + + + + +
+ + + +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-crypto_aes_kw.html b/docs/module-crypto_aes_kw.html new file mode 100644 index 00000000..b1dd518e --- /dev/null +++ b/docs/module-crypto_aes_kw.html @@ -0,0 +1,604 @@ + + + + + JSDoc: Module: crypto/aes_kw + + + + + + + + + + +
+ +

Module: crypto/aes_kw

+ + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

Implementation of RFC 3394 AES Key Wrap & Key Unwrap funcions

+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(static) unwrap(algo, key, wrappedData) → {Uint8Array}

+ + + + + + +
+

AES key unwrap

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +enums.symmetric.aes128 +| + +enums.symmetric.aes256 +| + +enums.symmetric.aes192 + + + +

AES algo

key + + +Uint8Array + + + +

wrapping key

wrappedData + + +Uint8Array + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

unwrapped data

+
+ + + +
+
+ Type +
+
+ +Uint8Array + + +
+
+ + + + + + + + + + + + + +

(static) wrap(algo, key, dataToWrap) → {Uint8Array}

+ + + + + + +
+

AES key wrap

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +enums.symmetric.aes128 +| + +enums.symmetric.aes256 +| + +enums.symmetric.aes192 + + + +

AES algo

key + + +Uint8Array + + + +

wrapping key

dataToWrap + + +Uint8Array + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

wrapped key

+
+ + + +
+
+ Type +
+
+ +Uint8Array + + +
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-crypto_cmac.html b/docs/module-crypto_cmac.html new file mode 100644 index 00000000..cc9ec4f1 --- /dev/null +++ b/docs/module-crypto_cmac.html @@ -0,0 +1,413 @@ + + + + + JSDoc: Module: crypto/cmac + + + + + + + + + + +
+ +

Module: crypto/cmac

+ + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

This module implements AES-CMAC on top of +native AES-CBC using either the WebCrypto API or Node.js' crypto API.

+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +

Members

+ + + +

(inner, constant) blockLength

+ + + + +
+

This implementation of CMAC is based on the description of OMAC in +http://web.cs.ucdavis.edu/~rogaway/papers/eax.pdf. As per that +document:

+

We have made a small modification to the OMAC algorithm as it was +originally presented, changing one of its two constants. +Specifically, the constant 4 at line 85 was the constant 1/2 (the +multiplicative inverse of 2) in the original definition of OMAC [14]. +The OMAC authors indicate that they will promulgate this modification +[15], which slightly simplifies implementations.

+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + +

Methods

+ + + + + + + +

(inner) rightXORMut(data, padding)

+ + + + + + +
+

xor padding into the end of data. This function implements "the +operation xor→ [which] xors the shorter string into the end of longer +one". Since data is always as least as long as padding, we can +simplify the implementation.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
data + + +Uint8Array + + + +
padding + + +Uint8Array + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-crypto_crypto.html b/docs/module-crypto_crypto.html new file mode 100644 index 00000000..27f56994 --- /dev/null +++ b/docs/module-crypto_crypto.html @@ -0,0 +1,3003 @@ + + + + + JSDoc: Module: crypto/crypto + + + + + + + + + + +
+ +

Module: crypto/crypto

+ + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

Provides functions for asymmetric encryption and decryption as +well as key generation and parameter handling for all public-key cryptosystems.

+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(async, static) generateParams(algo, bits, oid) → {Promise.<{publicParams: {Object}, privateParams: {Object}}>}

+ + + + + + +
+

Generate algorithm-specific key parameters

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +module:enums.publicKey + + + +

The public key algorithm

bits + + +Integer + + + +

Bit length for RSA keys

oid + + +module:type/oid + + + +

Object identifier for ECC keys

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

The parameters referenced by name.

+
+ + + +
+
+ Type +
+
+ +Promise.<{publicParams: {Object}, privateParams: {Object}}> + + +
+
+ + + + + + + + + + + + + +

(static) generateSessionKey(algo) → {Uint8Array}

+ + + + + + +
+

Generating a session key for the specified symmetric algorithm +See RFC 4880 9.2 for algorithms.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +module:enums.symmetric + + + +

Symmetric encryption algorithm

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Random bytes as a string to be used as a key.

+
+ + + +
+
+ Type +
+
+ +Uint8Array + + +
+
+ + + + + + + + + + + + + +

(static) getAEADMode(algo) → {Object}

+ + + + + + +
+

Get implementation of the given AEAD mode

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +enums.aead + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Throws:
+ + + +
+
+
+

on invalid algo

+
+
+
+
+
+
+ Type +
+
+ +Error + + +
+
+
+
+
+ + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Object + + +
+
+ + + + + + + + + + + + + +

(static) getCurvePayloadSize(algo, oidopt)

+ + + + + + +
+

Get encoded secret size for a given elliptic algo

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
algo + + +module:enums.publicKey + + + + + + + + + +

alrogithm identifier

oid + + +module:type/oid + + + + + + <optional>
+ + + + + +

curve OID if needed by algo

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) getPreferredCurveHashAlgo(algo, oidopt)

+ + + + + + +
+

Get preferred signing hash algo for a given elliptic algo

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
algo + + +module:enums.publicKey + + + + + + + + + +

alrogithm identifier

oid + + +module:type/oid + + + + + + <optional>
+ + + + + +

curve OID if needed by algo

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(async, static) getPrefixRandom(algo) → {Promise.<Uint8Array>}

+ + + + + + +
+

Generates a random byte prefix for the specified algorithm +See RFC 4880 9.2 for algorithms.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +module:enums.symmetric + + + +

Symmetric encryption algorithm

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Random bytes with length equal to the block size of the cipher, plus the last two bytes repeated.

+
+ + + +
+
+ Type +
+
+ +Promise.<Uint8Array> + + +
+
+ + + + + + + + + + + + + +

(static) parseEncSessionKeyParams(algo, bytes) → {Object}

+ + + + + + +
+

Returns the types comprising the encrypted session key of an algorithm

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +module:enums.publicKey + + + +

The key algorithm

bytes + + +Uint8Array + + + +

The key material to parse

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

The session key parameters referenced by name.

+
+ + + +
+
+ Type +
+
+ +Object + + +
+
+ + + + + + + + + + + + + +

(static) parsePrivateKeyParams(algo, bytes, publicParams) → {Object}

+ + + + + + +
+

Parse private key material in binary form to get the key parameters

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +module:enums.publicKey + + + +

The key algorithm

bytes + + +Uint8Array + + + +

The key material to parse

publicParams + + +Object + + + +

(ECC only) public params, needed to format some private params

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Number of read bytes plus the key parameters referenced by name.

+
+ + + +
+
+ Type +
+
+ +Object + + +
+
+ + + + + + + + + + + + + +

(static) parsePublicKeyParams(algo, bytes) → {Object}

+ + + + + + +
+

Parse public key material in binary form to get the key parameters

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +module:enums.publicKey + + + +

The key algorithm

bytes + + +Uint8Array + + + +

The key material to parse

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Number of read bytes plus key parameters referenced by name.

+
+ + + +
+
+ Type +
+
+ +Object + + +
+
+ + + + + + + + + + + + + +

(async, static) publicKeyDecrypt(algo, publicKeyParams, privateKeyParams, sessionKeyParams, fingerprint, randomPayloadopt) → {Promise.<Uint8Array>}

+ + + + + + +
+

Decrypts data using specified algorithm and private key parameters. +See RFC 4880 5.5.3

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
algo + + +module:enums.publicKey + + + + + + + + + +

Public key algorithm

publicKeyParams + + +Object + + + + + + + + + +

Algorithm-specific public key parameters

privateKeyParams + + +Object + + + + + + + + + +

Algorithm-specific private key parameters

sessionKeyParams + + +Object + + + + + + + + + +

Encrypted session key parameters

fingerprint + + +Uint8Array + + + + + + + + + +

Recipient fingerprint

randomPayload + + +Uint8Array + + + + + + <optional>
+ + + + + +

Data to return on decryption error, instead of throwing +(needed for constant-time processing in RSA and ElGamal)

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Throws:
+ + + +
+
+
+

on sensitive decryption error, unless randomPayload is given

+
+
+
+
+
+
+ Type +
+
+ +Error + + +
+
+
+
+
+ + + + + +
Returns:
+ + +
+

Decrypted data.

+
+ + + +
+
+ Type +
+
+ +Promise.<Uint8Array> + + +
+
+ + + + + + + + + + + + + +

(async, static) publicKeyEncrypt(keyAlgo, symmetricAlgo, publicParams, data, fingerprint) → {Promise.<Object>}

+ + + + + + +
+

Encrypts data using specified algorithm and public key parameters. +See RFC 4880 9.1 for public key algorithms.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
keyAlgo + + +module:enums.publicKey + + + +

Public key algorithm

symmetricAlgo + + +module:enums.symmetric +| + +null + + + +

Cipher algorithm (v3 only)

publicParams + + +Object + + + +

Algorithm-specific public key parameters

data + + +Uint8Array + + + +

Session key data to be encrypted

fingerprint + + +Uint8Array + + + +

Recipient fingerprint

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Encrypted session key parameters.

+
+ + + +
+
+ Type +
+
+ +Promise.<Object> + + +
+
+ + + + + + + + + + + + + +

(static) serializeParams(algo, params) → {Uint8Array}

+ + + + + + +
+

Convert params to MPI and serializes them in the proper order

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +module:enums.publicKey + + + +

The public key algorithm

params + + +Object + + + +

The key parameters indexed by name

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

The array containing the MPIs.

+
+ + + +
+
+ Type +
+
+ +Uint8Array + + +
+
+ + + + + + + + + + + + + +

(async, static) validateParams(algo, publicParams, privateParams) → {Promise.<Boolean>}

+ + + + + + +
+

Validate algorithm-specific key parameters

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +module:enums.publicKey + + + +

The public key algorithm

publicParams + + +Object + + + +

Algorithm-specific public key parameters

privateParams + + +Object + + + +

Algorithm-specific private key parameters

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Whether the parameters are valid.

+
+ + + +
+
+ Type +
+
+ +Promise.<Boolean> + + +
+
+ + + + + + + + + + + + + +

(inner) checkSupportedCurve(oid)

+ + + + + + +
+

Check whether the given curve OID is supported

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
oid + + +module:type/oid + + + +

EC object identifier

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Throws:
+ + + +
+
+
+

if curve is not supported

+
+
+
+
+
+
+ Type +
+
+ +UnsupportedError + + +
+
+
+
+
+ + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-crypto_hash.html b/docs/module-crypto_hash.html new file mode 100644 index 00000000..8c3e17e1 --- /dev/null +++ b/docs/module-crypto_hash.html @@ -0,0 +1,596 @@ + + + + + JSDoc: Module: crypto/hash + + + + + + + + + + +
+ +

Module: crypto/hash

+ + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

Provides an interface to hashing functions available in Node.js or external libraries.

+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +

Members

+ + + +

(static) md5

+ + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+
    +
  • module:md5
  • +
+
+ + + +
+ + + + + + + + + + +

Methods

+ + + + + + + +

(static) digest(algo, data) → {Promise.<Uint8Array>}

+ + + + + + +
+

Create a hash on the specified data using the specified algorithm

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +module:enums.hash + + + +

Hash algorithm type (see RFC 4880 9.4)

data + + +Uint8Array + + + +

Data to be hashed

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Hash value.

+
+ + + +
+
+ Type +
+
+ +Promise.<Uint8Array> + + +
+
+ + + + + + + + + + + + + +

(static) getHashByteLength(algo) → {Integer}

+ + + + + + +
+

Returns the hash size in bytes of the specified hash algorithm type

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +module:enums.hash + + + +

Hash algorithm type (See RFC 4880 9.4)

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Size in bytes of the resulting hash.

+
+ + + +
+
+ Type +
+
+ +Integer + + +
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-crypto_hkdf.html b/docs/module-crypto_hkdf.html new file mode 100644 index 00000000..1f5418fd --- /dev/null +++ b/docs/module-crypto_hkdf.html @@ -0,0 +1,167 @@ + + + + + JSDoc: Module: crypto/hkdf + + + + + + + + + + +
+ +

Module: crypto/hkdf

+ + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

This module implements HKDF using either the WebCrypto API or Node.js' crypto API.

+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-crypto_mode.html b/docs/module-crypto_mode.html new file mode 100644 index 00000000..3c067e26 --- /dev/null +++ b/docs/module-crypto_mode.html @@ -0,0 +1,439 @@ + + + + + JSDoc: Module: crypto/mode + + + + + + + + + + +
+ +

Module: crypto/mode

+ + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

Cipher modes

+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +

Members

+ + + +

(static) cfb

+ + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + + +

(static) eax

+ + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + + +

(static) gcm

+ + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + + +

(static) ocb

+ + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-crypto_mode_cfb.html b/docs/module-crypto_mode_cfb.html new file mode 100644 index 00000000..70dd781f --- /dev/null +++ b/docs/module-crypto_mode_cfb.html @@ -0,0 +1,548 @@ + + + + + JSDoc: Module: crypto/mode/cfb + + + + + + + + + + +
+ +

Module: crypto/mode/cfb

+ + + + + + +
+ +
+ + + +
+ +
+
+ + + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(static) decrypt(algo, key, ciphertext, iv)

+ + + + + + +
+

CFB decryption

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +enums.symmetric + + + +

block cipher algorithm

key + + +Uint8Array + + + +
ciphertext + + +MaybeStream.<Uint8Array> + + + +
iv + + +Uint8Array + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

MaybeStream

+
+ + + + + + + + + + + + + + + +

(static) encrypt(algo, key, plaintext, iv, config)

+ + + + + + +
+

CFB encryption

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +enums.symmetric + + + +

block cipher algorithm

key + + +Uint8Array + + + +
plaintext + + +MaybeStream.<Uint8Array> + + + +
iv + + +Uint8Array + + + +
config + + +Object + + + +

full configuration, defaults to openpgp.config

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

MaybeStream

+
+ + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-crypto_mode_eax.html b/docs/module-crypto_mode_eax.html new file mode 100644 index 00000000..d72c957e --- /dev/null +++ b/docs/module-crypto_mode_eax.html @@ -0,0 +1,748 @@ + + + + + JSDoc: Module: crypto/mode/eax + + + + + + + + + + +
+ +

Module: crypto/mode/eax

+ + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

This module implements AES-EAX en/decryption on top of +native AES-CTR using either the WebCrypto API or Node.js' crypto API.

+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(inner) decrypt(ciphertext, nonce, adata) → {Promise.<Uint8Array>}

+ + + + + + +
+

Decrypt ciphertext input.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
ciphertext + + +Uint8Array + + + +

The ciphertext input to be decrypted

nonce + + +Uint8Array + + + +

The nonce (16 bytes)

adata + + +Uint8Array + + + +

Associated data to verify

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

The plaintext output.

+
+ + + +
+
+ Type +
+
+ +Promise.<Uint8Array> + + +
+
+ + + + + + + + + + + + + +

(async, inner) EAX(cipher, key)

+ + + + + + +
+

Class to en/decrypt using EAX mode.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
cipher + + +enums.symmetric + + + +

The symmetric cipher algorithm to use

key + + +Uint8Array + + + +

The encryption key

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(inner) encrypt(plaintext, nonce, adata) → {Promise.<Uint8Array>}

+ + + + + + +
+

Encrypt plaintext input.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
plaintext + + +Uint8Array + + + +

The cleartext input to be encrypted

nonce + + +Uint8Array + + + +

The nonce (16 bytes)

adata + + +Uint8Array + + + +

Associated data to sign

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

The ciphertext output.

+
+ + + +
+
+ Type +
+
+ +Promise.<Uint8Array> + + +
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-crypto_mode_gcm.html b/docs/module-crypto_mode_gcm.html new file mode 100644 index 00000000..1b64afe0 --- /dev/null +++ b/docs/module-crypto_mode_gcm.html @@ -0,0 +1,334 @@ + + + + + JSDoc: Module: crypto/mode/gcm + + + + + + + + + + +
+ +

Module: crypto/mode/gcm

+ + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

This module wraps native AES-GCM en/decryption for both +the WebCrypto api as well as node.js' crypto api.

+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(async, inner) GCM(cipher, key)

+ + + + + + +
+

Class to en/decrypt using GCM mode.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
cipher + + +enums.symmetric + + + +

The symmetric cipher algorithm to use

key + + +Uint8Array + + + +

The encryption key

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-crypto_mode_ocb.html b/docs/module-crypto_mode_ocb.html new file mode 100644 index 00000000..b90df5b5 --- /dev/null +++ b/docs/module-crypto_mode_ocb.html @@ -0,0 +1,747 @@ + + + + + JSDoc: Module: crypto/mode/ocb + + + + + + + + + + +
+ +

Module: crypto/mode/ocb

+ + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

This module implements AES-OCB en/decryption.

+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(inner) decrypt(ciphertext, nonce, adata) → {Promise.<Uint8Array>}

+ + + + + + +
+

Decrypt ciphertext input.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
ciphertext + + +Uint8Array + + + +

The ciphertext input to be decrypted

nonce + + +Uint8Array + + + +

The nonce (15 bytes)

adata + + +Uint8Array + + + +

Associated data to sign

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

The ciphertext output.

+
+ + + +
+
+ Type +
+
+ +Promise.<Uint8Array> + + +
+
+ + + + + + + + + + + + + +

(inner) encrypt(plaintext, nonce, adata) → {Promise.<Uint8Array>}

+ + + + + + +
+

Encrypt plaintext input.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
plaintext + + +Uint8Array + + + +

The cleartext input to be encrypted

nonce + + +Uint8Array + + + +

The nonce (15 bytes)

adata + + +Uint8Array + + + +

Associated data to sign

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

The ciphertext output.

+
+ + + +
+
+ Type +
+
+ +Promise.<Uint8Array> + + +
+
+ + + + + + + + + + + + + +

(async, inner) OCB(cipher, key)

+ + + + + + +
+

Class to en/decrypt using OCB mode.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
cipher + + +enums.symmetric + + + +

The symmetric cipher algorithm to use

key + + +Uint8Array + + + +

The encryption key

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-crypto_pkcs1.html b/docs/module-crypto_pkcs1.html new file mode 100644 index 00000000..309d1e8d --- /dev/null +++ b/docs/module-crypto_pkcs1.html @@ -0,0 +1,882 @@ + + + + + JSDoc: Module: crypto/pkcs1 + + + + + + + + + + +
+ +

Module: crypto/pkcs1

+ + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

Provides EME-PKCS1-v1_5 encoding and decoding and EMSA-PKCS1-v1_5 encoding function

+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +

Members

+ + + +

(inner, constant) hash_headers

+ + + + +
+

ASN1 object identifiers for hashes

+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + + + + +

Methods

+ + + + + + + +

(static) emeDecode(encoded, randomPayload) → {Uint8Array}

+ + + + + + +
+

Decode a EME-PKCS1-v1_5 padded message

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
encoded + + +Uint8Array + + + +

Encoded message bytes

randomPayload + + +Uint8Array + + + +

Data to return in case of decoding error (needed for constant-time processing)

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + + + + + + + +
Throws:
+ + + +
+
+
+

on decoding failure, unless randomPayload is provided

+
+
+
+
+
+
+ Type +
+
+ +Error + + +
+
+
+
+
+ + + + + +
Returns:
+ + +
+

decoded data or randomPayload (on error, if given)

+
+ + + +
+
+ Type +
+
+ +Uint8Array + + +
+
+ + + + + + + + + + + + + +

(static) emeEncode(message, keyLength) → {Uint8Array}

+ + + + + + +
+

Create a EME-PKCS1-v1_5 padded message

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
message + + +Uint8Array + + + +

Message to be encoded

keyLength + + +Integer + + + +

The length in octets of the key modulus

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

EME-PKCS1 padded message.

+
+ + + +
+
+ Type +
+
+ +Uint8Array + + +
+
+ + + + + + + + + + + + + +

(static) emsaEncode(algo, hashed, emLen) → {Uint8Array}

+ + + + + + +
+

Create a EMSA-PKCS1-v1_5 padded message

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +Integer + + + +

Hash algorithm type used

hashed + + +Uint8Array + + + +

Message to be encoded

emLen + + +Integer + + + +

Intended length in octets of the encoded message

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Encoded message.

+
+ + + +
+
+ Type +
+
+ +Uint8Array + + +
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-crypto_public_key.html b/docs/module-crypto_public_key.html new file mode 100644 index 00000000..123b2b83 --- /dev/null +++ b/docs/module-crypto_public_key.html @@ -0,0 +1,439 @@ + + + + + JSDoc: Module: crypto/public_key + + + + + + + + + + +
+ +

Module: crypto/public_key

+ + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

Asymmetric cryptography functions

+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +

Members

+ + + +

(static) dsa

+ + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + + +

(static) elgamal

+ + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + + +

(static) elliptic

+ + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + + +

(static) rsa

+ + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-crypto_public_key_dsa.html b/docs/module-crypto_public_key_dsa.html new file mode 100644 index 00000000..52c051a3 --- /dev/null +++ b/docs/module-crypto_public_key_dsa.html @@ -0,0 +1,1084 @@ + + + + + JSDoc: Module: crypto/public_key/dsa + + + + + + + + + + +
+ +

Module: crypto/public_key/dsa

+ + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

A Digital signature algorithm implementation

+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +

Members

+ + + +

(inner) x

+ + + + +
+

Re-derive public key y' = g ** x mod p +Expect y == y'

+

Blinded exponentiation computes g**{rq + x} to compare to y

+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + +

Methods

+ + + + + + + +

(async, static) sign(hashAlgo, hashed, g, p, q, x) → {Promise.<{r: Uint8Array, s: Uint8Array}>}

+ + + + + + +
+

DSA Sign function

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
hashAlgo + + +Integer + + + +
hashed + + +Uint8Array + + + +
g + + +Uint8Array + + + +
p + + +Uint8Array + + + +
q + + +Uint8Array + + + +
x + + +Uint8Array + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Promise.<{r: Uint8Array, s: Uint8Array}> + + +
+
+ + + + + + + + + + + + + +

(async, static) validateParams(p, q, g, y, x) → {Promise.<Boolean>}

+ + + + + + +
+

Validate DSA parameters

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
p + + +Uint8Array + + + +

DSA prime

q + + +Uint8Array + + + +

DSA group order

g + + +Uint8Array + + + +

DSA sub-group generator

y + + +Uint8Array + + + +

DSA public key

x + + +Uint8Array + + + +

DSA private key

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Whether params are valid.

+
+ + + +
+
+ Type +
+
+ +Promise.<Boolean> + + +
+
+ + + + + + + + + + + + + +

(async, static) verify(hashAlgo, r, s, hashed, g, p, q, y) → {boolean}

+ + + + + + +
+

DSA Verify function

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
hashAlgo + + +Integer + + + +
r + + +Uint8Array + + + +
s + + +Uint8Array + + + +
hashed + + +Uint8Array + + + +
g + + +Uint8Array + + + +
p + + +Uint8Array + + + +
q + + +Uint8Array + + + +
y + + +Uint8Array + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-crypto_public_key_elgamal.html b/docs/module-crypto_public_key_elgamal.html new file mode 100644 index 00000000..44ed8b38 --- /dev/null +++ b/docs/module-crypto_public_key_elgamal.html @@ -0,0 +1,981 @@ + + + + + JSDoc: Module: crypto/public_key/elgamal + + + + + + + + + + +
+ +

Module: crypto/public_key/elgamal

+ + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

ElGamal implementation

+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +

Members

+ + + +

(inner) x

+ + + + +
+

Re-derive public key y' = g ** x mod p +Expect y == y'

+

Blinded exponentiation computes g**{r(p-1) + x} to compare to y

+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + +

Methods

+ + + + + + + +

(async, static) decrypt(c1, c2, p, x, randomPayload) → {Promise.<Uint8Array>}

+ + + + + + +
+

ElGamal Encryption function

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
c1 + + +Uint8Array + + + +
c2 + + +Uint8Array + + + +
p + + +Uint8Array + + + +
x + + +Uint8Array + + + +
randomPayload + + +Uint8Array + + + +

Data to return on unpadding error, instead of throwing +(needed for constant-time processing)

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Throws:
+ + + +
+
+
+

on decryption error, unless randomPayload is given

+
+
+
+
+
+
+ Type +
+
+ +Error + + +
+
+
+
+
+ + + + + +
Returns:
+ + +
+

Unpadded message.

+
+ + + +
+
+ Type +
+
+ +Promise.<Uint8Array> + + +
+
+ + + + + + + + + + + + + +

(async, static) encrypt(data, p, g, y) → {Promise.<{c1: Uint8Array, c2: Uint8Array}>}

+ + + + + + +
+

ElGamal Encryption function +Note that in OpenPGP, the message needs to be padded with PKCS#1 (same as RSA)

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
data + + +Uint8Array + + + +

To be padded and encrypted

p + + +Uint8Array + + + +
g + + +Uint8Array + + + +
y + + +Uint8Array + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Promise.<{c1: Uint8Array, c2: Uint8Array}> + + +
+
+ + + + + + + + + + + + + +

(async, static) validateParams(p, g, y, x) → {Promise.<Boolean>}

+ + + + + + +
+

Validate ElGamal parameters

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
p + + +Uint8Array + + + +

ElGamal prime

g + + +Uint8Array + + + +

ElGamal group generator

y + + +Uint8Array + + + +

ElGamal public key

x + + +Uint8Array + + + +

ElGamal private exponent

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Whether params are valid.

+
+ + + +
+
+ Type +
+
+ +Promise.<Boolean> + + +
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-crypto_public_key_elliptic.html b/docs/module-crypto_public_key_elliptic.html new file mode 100644 index 00000000..37f56eda --- /dev/null +++ b/docs/module-crypto_public_key_elliptic.html @@ -0,0 +1,180 @@ + + + + + JSDoc: Module: crypto/public_key/elliptic + + + + + + + + + + +
+ +

Module: crypto/public_key/elliptic

+ + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

Functions to access Elliptic Curve Cryptography

+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-crypto_public_key_elliptic_curve.html b/docs/module-crypto_public_key_elliptic_curve.html new file mode 100644 index 00000000..ea23377b --- /dev/null +++ b/docs/module-crypto_public_key_elliptic_curve.html @@ -0,0 +1,1240 @@ + + + + + JSDoc: Module: crypto/public_key/elliptic/curve + + + + + + + + + + +
+ +

Module: crypto/public_key/elliptic/curve

+ + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

Wrapper of an instance of an Elliptic Curve

+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(inner) checkPublicPointEnconding()

+ + + + + + +
+

Check whether the public point has a valid encoding. +NB: this function does not check e.g. whether the point belongs to the curve.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(inner) getPreferredHashAlgo(oid) → {enums.hash}

+ + + + + + +
+

Get preferred hash algo to use with the given curve

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
oid + + +module:type/oid + + + +

curve oid

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

hash algorithm

+
+ + + +
+
+ Type +
+
+ +enums.hash + + +
+
+ + + + + + + + + + + + + +

(inner) jwkToRawPublic(jwk) → {Uint8Array}

+ + + + + + + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
jwk + + +JsonWebKey + + + +

key for conversion

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Raw public key.

+
+ + + +
+
+ Type +
+
+ +Uint8Array + + +
+
+ + + + + + + + + + + + + +

(inner) privateToJWK(payloadSize, name, publicKey, privateKey) → {JsonWebKey}

+ + + + + + + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
payloadSize + + +Integer + + + +

ec payload size

name + + +String + + + +

curve name

publicKey + + +Uint8Array + + + +

public key

privateKey + + +Uint8Array + + + +

private key

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Private key in jwk format.

+
+ + + +
+
+ Type +
+
+ +JsonWebKey + + +
+
+ + + + + + + + + + + + + +

(inner) rawPublicToJWK(payloadSize, name, publicKey) → {JsonWebKey}

+ + + + + + + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
payloadSize + + +Integer + + + +

ec payload size

name + + +String + + + +

curve name

publicKey + + +Uint8Array + + + +

public key

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Public key in jwk format.

+
+ + + +
+
+ Type +
+
+ +JsonWebKey + + +
+
+ + + + + + + + + + + + + +

(async, inner) validateStandardParams(algo, oid, Q, d) → {Promise.<Boolean>}

+ + + + + + +
+

Validate ECDH and ECDSA parameters +Not suitable for EdDSA (different secret key format)

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +module:enums.publicKey + + + +

EC algorithm, to filter supported curves

oid + + +module:type/oid + + + +

EC object identifier

Q + + +Uint8Array + + + +

EC public point

d + + +Uint8Array + + + +

EC secret scalar

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Whether params are valid.

+
+ + + +
+
+ Type +
+
+ +Promise.<Boolean> + + +
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-crypto_public_key_elliptic_ecdh.html b/docs/module-crypto_public_key_elliptic_ecdh.html new file mode 100644 index 00000000..e2ae3b7f --- /dev/null +++ b/docs/module-crypto_public_key_elliptic_ecdh.html @@ -0,0 +1,6235 @@ + + + + + JSDoc: Module: crypto/public_key/elliptic/ecdh + + + + + + + + + + +
+ +

Module: crypto/public_key/elliptic/ecdh

+ + + + + + +
+ +
+ + + + + + + +
+ +
+
+ + +

Key encryption and decryption for RFC 6637 ECDH

+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(async, static) decrypt(oid, kdfParams, V, C, Q, d, fingerprint) → {Promise.<Uint8Array>}

+ + + + + + +
+

Decrypt and unwrap the value derived from session key

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
oid + + +module:type/oid + + + +

Elliptic curve object identifier

kdfParams + + +module:type/kdf_params + + + +

KDF params including cipher and algorithm to use

V + + +Uint8Array + + + +

Public part of ephemeral key

C + + +Uint8Array + + + +

Encrypted and wrapped value derived from session key

Q + + +Uint8Array + + + +

Recipient public key

d + + +Uint8Array + + + +

Recipient private key

fingerprint + + +Uint8Array + + + +

Recipient fingerprint, already truncated depending on the key version

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Value derived from session key.

+
+ + + +
+
+ Type +
+
+ +Promise.<Uint8Array> + + +
+
+ + + + + + + + + + + + + +

(async, static) decrypt(algo, ephemeralPublicKey, wrappedKey,, A, k) → {Promise.<Uint8Array>}

+ + + + + + +
+

Decrypt and unwrap the session key

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +module:enums.publicKey + + + +

Algorithm identifier

ephemeralPublicKey + + +Uint8Array + + + +

(K_A)

wrappedKey, + + +Uint8Array + + + +
A + + +Uint8Array + + + +

Recipient public key (K_b), needed for KDF

k + + +Uint8Array + + + +

Recipient secret key (b)

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

decrypted session key data

+
+ + + +
+
+ Type +
+
+ +Promise.<Uint8Array> + + +
+
+ + + + + + + + + + + + + +

(async, static) encrypt(oid, kdfParams, data, Q, fingerprint) → {Promise.<{publicKey: Uint8Array, wrappedKey: Uint8Array}>}

+ + + + + + +
+

Encrypt and wrap a session key

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
oid + + +module:type/oid + + + +

Elliptic curve object identifier

kdfParams + + +module:type/kdf_params + + + +

KDF params including cipher and algorithm to use

data + + +Uint8Array + + + +

Unpadded session key data

Q + + +Uint8Array + + + +

Recipient public key

fingerprint + + +Uint8Array + + + +

Recipient fingerprint, already truncated depending on the key version

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Promise.<{publicKey: Uint8Array, wrappedKey: Uint8Array}> + + +
+
+ + + + + + + + + + + + + +

(async, static) encrypt(algo, data, recipientA) → {Promise.<{ephemeralPublicKey: Uint8Array, wrappedKey: Uint8Array}>}

+ + + + + + +
+

Wrap and encrypt a session key

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +module:enums.publicKey + + + +

Algorithm identifier

data + + +Uint8Array + + + +

session key data to be encrypted

recipientA + + +Uint8Array + + + +

Recipient public key (K_B)

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

ephemeral public key (K_A) and encrypted key

+
+ + + +
+
+ Type +
+
+ +Promise.<{ephemeralPublicKey: Uint8Array, wrappedKey: Uint8Array}> + + +
+
+ + + + + + + + + + + + + +

(static) generate(algo) → {Promise.<{A: Uint8Array, k: Uint8Array}>}

+ + + + + + +
+

Generate ECDH key for Montgomery curves

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +module:enums.publicKey + + + +

Algorithm identifier

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Promise.<{A: Uint8Array, k: Uint8Array}> + + +
+
+ + + + + + + + + + + + + +

(async, static) generateEphemeralEncryptionMaterial() → {Promise.<{ephemeralPublicKey: Uint8Array, sharedSecret: Uint8Array}>}

+ + + + + + +
+

Generate shared secret and ephemeral public key for encryption

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

ephemeral public key (K_A) and shared secret

+
+ + + +
+
+ Type +
+
+ +Promise.<{ephemeralPublicKey: Uint8Array, sharedSecret: Uint8Array}> + + +
+
+ + + + + + + + + + + + + +

(async, static) validateParams(oid, Q, d) → {Promise.<Boolean>}

+ + + + + + +
+

Validate ECDH parameters

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
oid + + +module:type/oid + + + +

Elliptic curve object identifier

Q + + +Uint8Array + + + +

ECDH public point

d + + +Uint8Array + + + +

ECDH secret scalar

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Whether params are valid.

+
+ + + +
+
+ Type +
+
+ +Promise.<Boolean> + + +
+
+ + + + + + + + + + + + + +

(async, static) validateParams(algo, A, k) → {Promise.<Boolean>}

+ + + + + + +
+

Validate ECDH parameters

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +module:enums.publicKey + + + +

Algorithm identifier

A + + +Uint8Array + + + +

ECDH public point

k + + +Uint8Array + + + +

ECDH secret scalar

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Whether params are valid.

+
+ + + +
+
+ Type +
+
+ +Promise.<Boolean> + + +
+
+ + + + + + + + + + + + + +

(async, inner) genPrivateEphemeralKey(curve, V, Q, d) → {Promise.<{secretKey: Uint8Array, sharedKey: Uint8Array}>}

+ + + + + + +
+

Generate ECDHE secret from private key and public part of ephemeral key

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
curve + + +CurveWithOID + + + +

Elliptic curve object

V + + +Uint8Array + + + +

Public part of ephemeral key

Q + + +Uint8Array + + + +

Recipient public key

d + + +Uint8Array + + + +

Recipient private key

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Promise.<{secretKey: Uint8Array, sharedKey: Uint8Array}> + + +
+
+ + + + + + + + + + + + + +

(async, inner) genPublicEphemeralKey(curve, Q) → {Promise.<{publicKey: Uint8Array, sharedKey: Uint8Array}>}

+ + + + + + +
+

Generate ECDHE ephemeral key and secret from public key

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
curve + + +CurveWithOID + + + +

Elliptic curve object

Q + + +Uint8Array + + + +

Recipient public key

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Promise.<{publicKey: Uint8Array, sharedKey: Uint8Array}> + + +
+
+ + + + + + + + + + + + + +

(async, inner) nodePrivateEphemeralKey(curve, V, d) → {Promise.<{secretKey: Uint8Array, sharedKey: Uint8Array}>}

+ + + + + + +
+

Generate ECDHE secret from private key and public part of ephemeral key using nodeCrypto

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
curve + + +CurveWithOID + + + +

Elliptic curve object

V + + +Uint8Array + + + +

Public part of ephemeral key

d + + +Uint8Array + + + +

Recipient private key

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Promise.<{secretKey: Uint8Array, sharedKey: Uint8Array}> + + +
+
+ + + + + + + + + + + + + +

(async, inner) nodePublicEphemeralKey(curve, Q) → {Promise.<{publicKey: Uint8Array, sharedKey: Uint8Array}>}

+ + + + + + +
+

Generate ECDHE ephemeral key and secret from public key using nodeCrypto

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
curve + + +CurveWithOID + + + +

Elliptic curve object

Q + + +Uint8Array + + + +

Recipient public key

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Promise.<{publicKey: Uint8Array, sharedKey: Uint8Array}> + + +
+
+ + + + + + + + + + + + + +

(async, inner) webPrivateEphemeralKey(curve, V, Q, d) → {Promise.<{secretKey: Uint8Array, sharedKey: Uint8Array}>}

+ + + + + + +
+

Generate ECDHE secret from private key and public part of ephemeral key using webCrypto

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
curve + + +CurveWithOID + + + +

Elliptic curve object

V + + +Uint8Array + + + +

Public part of ephemeral key

Q + + +Uint8Array + + + +

Recipient public key

d + + +Uint8Array + + + +

Recipient private key

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Promise.<{secretKey: Uint8Array, sharedKey: Uint8Array}> + + +
+
+ + + + + + + + + + + + + +

(async, inner) webPublicEphemeralKey(curve, Q) → {Promise.<{publicKey: Uint8Array, sharedKey: Uint8Array}>}

+ + + + + + +
+

Generate ECDHE ephemeral key and secret from public key using webCrypto

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
curve + + +CurveWithOID + + + +

Elliptic curve object

Q + + +Uint8Array + + + +

Recipient public key

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Promise.<{publicKey: Uint8Array, sharedKey: Uint8Array}> + + +
+
+ + + + + + + + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+
+ + +

Key encryption and decryption for RFC 6637 ECDH

+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(async, static) decrypt(oid, kdfParams, V, C, Q, d, fingerprint) → {Promise.<Uint8Array>}

+ + + + + + +
+

Decrypt and unwrap the value derived from session key

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
oid + + +module:type/oid + + + +

Elliptic curve object identifier

kdfParams + + +module:type/kdf_params + + + +

KDF params including cipher and algorithm to use

V + + +Uint8Array + + + +

Public part of ephemeral key

C + + +Uint8Array + + + +

Encrypted and wrapped value derived from session key

Q + + +Uint8Array + + + +

Recipient public key

d + + +Uint8Array + + + +

Recipient private key

fingerprint + + +Uint8Array + + + +

Recipient fingerprint, already truncated depending on the key version

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Value derived from session key.

+
+ + + +
+
+ Type +
+
+ +Promise.<Uint8Array> + + +
+
+ + + + + + + + + + + + + +

(async, static) decrypt(algo, ephemeralPublicKey, wrappedKey,, A, k) → {Promise.<Uint8Array>}

+ + + + + + +
+

Decrypt and unwrap the session key

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +module:enums.publicKey + + + +

Algorithm identifier

ephemeralPublicKey + + +Uint8Array + + + +

(K_A)

wrappedKey, + + +Uint8Array + + + +
A + + +Uint8Array + + + +

Recipient public key (K_b), needed for KDF

k + + +Uint8Array + + + +

Recipient secret key (b)

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

decrypted session key data

+
+ + + +
+
+ Type +
+
+ +Promise.<Uint8Array> + + +
+
+ + + + + + + + + + + + + +

(async, static) encrypt(oid, kdfParams, data, Q, fingerprint) → {Promise.<{publicKey: Uint8Array, wrappedKey: Uint8Array}>}

+ + + + + + +
+

Encrypt and wrap a session key

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
oid + + +module:type/oid + + + +

Elliptic curve object identifier

kdfParams + + +module:type/kdf_params + + + +

KDF params including cipher and algorithm to use

data + + +Uint8Array + + + +

Unpadded session key data

Q + + +Uint8Array + + + +

Recipient public key

fingerprint + + +Uint8Array + + + +

Recipient fingerprint, already truncated depending on the key version

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Promise.<{publicKey: Uint8Array, wrappedKey: Uint8Array}> + + +
+
+ + + + + + + + + + + + + +

(async, static) encrypt(algo, data, recipientA) → {Promise.<{ephemeralPublicKey: Uint8Array, wrappedKey: Uint8Array}>}

+ + + + + + +
+

Wrap and encrypt a session key

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +module:enums.publicKey + + + +

Algorithm identifier

data + + +Uint8Array + + + +

session key data to be encrypted

recipientA + + +Uint8Array + + + +

Recipient public key (K_B)

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

ephemeral public key (K_A) and encrypted key

+
+ + + +
+
+ Type +
+
+ +Promise.<{ephemeralPublicKey: Uint8Array, wrappedKey: Uint8Array}> + + +
+
+ + + + + + + + + + + + + +

(static) generate(algo) → {Promise.<{A: Uint8Array, k: Uint8Array}>}

+ + + + + + +
+

Generate ECDH key for Montgomery curves

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +module:enums.publicKey + + + +

Algorithm identifier

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Promise.<{A: Uint8Array, k: Uint8Array}> + + +
+
+ + + + + + + + + + + + + +

(async, static) generateEphemeralEncryptionMaterial() → {Promise.<{ephemeralPublicKey: Uint8Array, sharedSecret: Uint8Array}>}

+ + + + + + +
+

Generate shared secret and ephemeral public key for encryption

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

ephemeral public key (K_A) and shared secret

+
+ + + +
+
+ Type +
+
+ +Promise.<{ephemeralPublicKey: Uint8Array, sharedSecret: Uint8Array}> + + +
+
+ + + + + + + + + + + + + +

(async, static) validateParams(oid, Q, d) → {Promise.<Boolean>}

+ + + + + + +
+

Validate ECDH parameters

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
oid + + +module:type/oid + + + +

Elliptic curve object identifier

Q + + +Uint8Array + + + +

ECDH public point

d + + +Uint8Array + + + +

ECDH secret scalar

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Whether params are valid.

+
+ + + +
+
+ Type +
+
+ +Promise.<Boolean> + + +
+
+ + + + + + + + + + + + + +

(async, static) validateParams(algo, A, k) → {Promise.<Boolean>}

+ + + + + + +
+

Validate ECDH parameters

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +module:enums.publicKey + + + +

Algorithm identifier

A + + +Uint8Array + + + +

ECDH public point

k + + +Uint8Array + + + +

ECDH secret scalar

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Whether params are valid.

+
+ + + +
+
+ Type +
+
+ +Promise.<Boolean> + + +
+
+ + + + + + + + + + + + + +

(async, inner) genPrivateEphemeralKey(curve, V, Q, d) → {Promise.<{secretKey: Uint8Array, sharedKey: Uint8Array}>}

+ + + + + + +
+

Generate ECDHE secret from private key and public part of ephemeral key

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
curve + + +CurveWithOID + + + +

Elliptic curve object

V + + +Uint8Array + + + +

Public part of ephemeral key

Q + + +Uint8Array + + + +

Recipient public key

d + + +Uint8Array + + + +

Recipient private key

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Promise.<{secretKey: Uint8Array, sharedKey: Uint8Array}> + + +
+
+ + + + + + + + + + + + + +

(async, inner) genPublicEphemeralKey(curve, Q) → {Promise.<{publicKey: Uint8Array, sharedKey: Uint8Array}>}

+ + + + + + +
+

Generate ECDHE ephemeral key and secret from public key

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
curve + + +CurveWithOID + + + +

Elliptic curve object

Q + + +Uint8Array + + + +

Recipient public key

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Promise.<{publicKey: Uint8Array, sharedKey: Uint8Array}> + + +
+
+ + + + + + + + + + + + + +

(async, inner) nodePrivateEphemeralKey(curve, V, d) → {Promise.<{secretKey: Uint8Array, sharedKey: Uint8Array}>}

+ + + + + + +
+

Generate ECDHE secret from private key and public part of ephemeral key using nodeCrypto

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
curve + + +CurveWithOID + + + +

Elliptic curve object

V + + +Uint8Array + + + +

Public part of ephemeral key

d + + +Uint8Array + + + +

Recipient private key

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Promise.<{secretKey: Uint8Array, sharedKey: Uint8Array}> + + +
+
+ + + + + + + + + + + + + +

(async, inner) nodePublicEphemeralKey(curve, Q) → {Promise.<{publicKey: Uint8Array, sharedKey: Uint8Array}>}

+ + + + + + +
+

Generate ECDHE ephemeral key and secret from public key using nodeCrypto

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
curve + + +CurveWithOID + + + +

Elliptic curve object

Q + + +Uint8Array + + + +

Recipient public key

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Promise.<{publicKey: Uint8Array, sharedKey: Uint8Array}> + + +
+
+ + + + + + + + + + + + + +

(async, inner) webPrivateEphemeralKey(curve, V, Q, d) → {Promise.<{secretKey: Uint8Array, sharedKey: Uint8Array}>}

+ + + + + + +
+

Generate ECDHE secret from private key and public part of ephemeral key using webCrypto

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
curve + + +CurveWithOID + + + +

Elliptic curve object

V + + +Uint8Array + + + +

Public part of ephemeral key

Q + + +Uint8Array + + + +

Recipient public key

d + + +Uint8Array + + + +

Recipient private key

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Promise.<{secretKey: Uint8Array, sharedKey: Uint8Array}> + + +
+
+ + + + + + + + + + + + + +

(async, inner) webPublicEphemeralKey(curve, Q) → {Promise.<{publicKey: Uint8Array, sharedKey: Uint8Array}>}

+ + + + + + +
+

Generate ECDHE ephemeral key and secret from public key using webCrypto

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
curve + + +CurveWithOID + + + +

Elliptic curve object

Q + + +Uint8Array + + + +

Recipient public key

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Promise.<{publicKey: Uint8Array, sharedKey: Uint8Array}> + + +
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-crypto_public_key_elliptic_ecdsa.html b/docs/module-crypto_public_key_elliptic_ecdsa.html new file mode 100644 index 00000000..e3fa7950 --- /dev/null +++ b/docs/module-crypto_public_key_elliptic_ecdsa.html @@ -0,0 +1,1017 @@ + + + + + JSDoc: Module: crypto/public_key/elliptic/ecdsa + + + + + + + + + + +
+ +

Module: crypto/public_key/elliptic/ecdsa

+ + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

Implementation of ECDSA following RFC6637 for Openpgpjs

+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(async, static) sign(oid, hashAlgo, message, publicKey, privateKey, hashed) → {Promise.<{r: Uint8Array, s: Uint8Array}>}

+ + + + + + +
+

Sign a message using the provided key

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
oid + + +module:type/oid + + + +

Elliptic curve object identifier

hashAlgo + + +module:enums.hash + + + +

Hash algorithm used to sign

message + + +Uint8Array + + + +

Message to sign

publicKey + + +Uint8Array + + + +

Public key

privateKey + + +Uint8Array + + + +

Private key used to sign the message

hashed + + +Uint8Array + + + +

The hashed message

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Signature of the message

+
+ + + +
+
+ Type +
+
+ +Promise.<{r: Uint8Array, s: Uint8Array}> + + +
+
+ + + + + + + + + + + + + +

(async, static) validateParams(oid, Q, d) → {Promise.<Boolean>}

+ + + + + + +
+

Validate ECDSA parameters

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
oid + + +module:type/oid + + + +

Elliptic curve object identifier

Q + + +Uint8Array + + + +

ECDSA public point

d + + +Uint8Array + + + +

ECDSA secret scalar

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Whether params are valid.

+
+ + + +
+
+ Type +
+
+ +Promise.<Boolean> + + +
+
+ + + + + + + + + + + + + +

(async, static) verify(oid, hashAlgo, signature, message, publicKey, hashed) → {Boolean}

+ + + + + + +
+

Verifies if a signature is valid for a message

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
oid + + +module:type/oid + + + +

Elliptic curve object identifier

hashAlgo + + +module:enums.hash + + + +

Hash algorithm used in the signature

signature + + +Object + + + +

Signature to verify

message + + +Uint8Array + + + +

Message to verify

publicKey + + +Uint8Array + + + +

Public key used to verify the message

hashed + + +Uint8Array + + + +

The hashed message

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Boolean + + +
+
+ + + + + + + + + + + + + +

(async, inner) jsVerify()

+ + + + + + +
+

Fallback javascript implementation of ECDSA verification. +To be used if no native implementation is available for the given curve/operation.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-crypto_public_key_elliptic_eddsa.html b/docs/module-crypto_public_key_elliptic_eddsa.html new file mode 100644 index 00000000..8656469e --- /dev/null +++ b/docs/module-crypto_public_key_elliptic_eddsa.html @@ -0,0 +1,1106 @@ + + + + + JSDoc: Module: crypto/public_key/elliptic/eddsa + + + + + + + + + + +
+ +

Module: crypto/public_key/elliptic/eddsa

+ + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

Implementation of EdDSA following RFC4880bis-03 for OpenPGP

+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(static) generate(algo) → {Promise.<{A: Uint8Array, seed: Uint8Array}>}

+ + + + + + +
+

Generate (non-legacy) EdDSA key

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +module:enums.publicKey + + + +

Algorithm identifier

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Promise.<{A: Uint8Array, seed: Uint8Array}> + + +
+
+ + + + + + + + + + + + + +

(async, static) sign(algo, hashAlgo, message, publicKey, privateKey, hashed) → {Promise.<{RS: Uint8Array}>}

+ + + + + + +
+

Sign a message using the provided key

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +module:enums.publicKey + + + +

Algorithm identifier

hashAlgo + + +module:enums.hash + + + +

Hash algorithm used to sign (must be sha256 or stronger)

message + + +Uint8Array + + + +

Message to sign

publicKey + + +Uint8Array + + + +

Public key

privateKey + + +Uint8Array + + + +

Private key used to sign the message

hashed + + +Uint8Array + + + +

The hashed message

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Signature of the message

+
+ + + +
+
+ Type +
+
+ +Promise.<{RS: Uint8Array}> + + +
+
+ + + + + + + + + + + + + +

(async, static) validateParams(algo, A, seed, oid) → {Promise.<Boolean>}

+ + + + + + +
+

Validate (non-legacy) EdDSA parameters

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +module:enums.publicKey + + + +

Algorithm identifier

A + + +Uint8Array + + + +

EdDSA public point

seed + + +Uint8Array + + + +

EdDSA secret seed

oid + + +Uint8Array + + + +

(legacy only) EdDSA OID

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Whether params are valid.

+
+ + + +
+
+ Type +
+
+ +Promise.<Boolean> + + +
+
+ + + + + + + + + + + + + +

(async, static) verify(algo, hashAlgo, signature, m, publicKey, hashed) → {Boolean}

+ + + + + + +
+

Verifies if a signature is valid for a message

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +module:enums.publicKey + + + +

Algorithm identifier

hashAlgo + + +module:enums.hash + + + +

Hash algorithm used in the signature

signature + + +Object + + + +

Signature to verify the message

m + + +Uint8Array + + + +

Message to verify

publicKey + + +Uint8Array + + + +

Public key used to verify the message

hashed + + +Uint8Array + + + +

The hashed message

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Boolean + + +
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-crypto_public_key_elliptic_eddsa_legacy.html b/docs/module-crypto_public_key_elliptic_eddsa_legacy.html new file mode 100644 index 00000000..ccd81de7 --- /dev/null +++ b/docs/module-crypto_public_key_elliptic_eddsa_legacy.html @@ -0,0 +1,927 @@ + + + + + JSDoc: Module: crypto/public_key/elliptic/eddsa_legacy + + + + + + + + + + +
+ +

Module: crypto/public_key/elliptic/eddsa_legacy

+ + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

Implementation of legacy EdDSA following RFC4880bis-03 for OpenPGP. +This key type has been deprecated by the crypto-refresh RFC.

+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(async, static) sign(oid, hashAlgo, message, publicKey, privateKey, hashed) → {Promise.<{r: Uint8Array, s: Uint8Array}>}

+ + + + + + +
+

Sign a message using the provided legacy EdDSA key

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
oid + + +module:type/oid + + + +

Elliptic curve object identifier

hashAlgo + + +module:enums.hash + + + +

Hash algorithm used to sign (must be sha256 or stronger)

message + + +Uint8Array + + + +

Message to sign

publicKey + + +Uint8Array + + + +

Public key

privateKey + + +Uint8Array + + + +

Private key used to sign the message

hashed + + +Uint8Array + + + +

The hashed message

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Signature of the message

+
+ + + +
+
+ Type +
+
+ +Promise.<{r: Uint8Array, s: Uint8Array}> + + +
+
+ + + + + + + + + + + + + +

(async, static) validateParams(oid, Q, k) → {Promise.<Boolean>}

+ + + + + + +
+

Validate legacy EdDSA parameters

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
oid + + +module:type/oid + + + +

Elliptic curve object identifier

Q + + +Uint8Array + + + +

EdDSA public point

k + + +Uint8Array + + + +

EdDSA secret seed

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Whether params are valid.

+
+ + + +
+
+ Type +
+
+ +Promise.<Boolean> + + +
+
+ + + + + + + + + + + + + +

(async, static) verify(oid, hashAlgo, signature, m, publicKey, hashed) → {Boolean}

+ + + + + + +
+

Verifies if a legacy EdDSA signature is valid for a message

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
oid + + +module:type/oid + + + +

Elliptic curve object identifier

hashAlgo + + +module:enums.hash + + + +

Hash algorithm used in the signature

signature + + +Object + + + +

Signature to verify the message

m + + +Uint8Array + + + +

Message to verify

publicKey + + +Uint8Array + + + +

Public key used to verify the message

hashed + + +Uint8Array + + + +

The hashed message

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Boolean + + +
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-crypto_public_key_rsa.html b/docs/module-crypto_public_key_rsa.html new file mode 100644 index 00000000..c11c76a5 --- /dev/null +++ b/docs/module-crypto_public_key_rsa.html @@ -0,0 +1,2369 @@ + + + + + JSDoc: Module: crypto/public_key/rsa + + + + + + + + + + +
+ +

Module: crypto/public_key/rsa

+ + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

RSA implementation

+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(async, static) decrypt(m, n, e, d, p, q, u, randomPayload) → {Promise.<String>}

+ + + + + + +
+

Decrypt RSA message

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
m + + +Uint8Array + + + +

Message

n + + +Uint8Array + + + +

RSA public modulus

e + + +Uint8Array + + + +

RSA public exponent

d + + +Uint8Array + + + +

RSA private exponent

p + + +Uint8Array + + + +

RSA private prime p

q + + +Uint8Array + + + +

RSA private prime q

u + + +Uint8Array + + + +

RSA private coefficient

randomPayload + + +Uint8Array + + + +

Data to return on decryption error, instead of throwing +(needed for constant-time processing)

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Throws:
+ + + +
+
+
+

on decryption error, unless randomPayload is given

+
+
+
+
+
+
+ Type +
+
+ +Error + + +
+
+
+
+
+ + + + + +
Returns:
+ + +
+

RSA Plaintext.

+
+ + + +
+
+ Type +
+
+ +Promise.<String> + + +
+
+ + + + + + + + + + + + + +

(async, static) encrypt(data, n, e) → {Promise.<Uint8Array>}

+ + + + + + +
+

Encrypt message

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
data + + +Uint8Array + + + +

Message

n + + +Uint8Array + + + +

RSA public modulus

e + + +Uint8Array + + + +

RSA public exponent

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

RSA Ciphertext.

+
+ + + +
+
+ Type +
+
+ +Promise.<Uint8Array> + + +
+
+ + + + + + + + + + + + + +

(async, static) generate(bits, e) → {Object}

+ + + + + + +
+

Generate a new random private key B bits long with public exponent E.

+

When possible, webCrypto or nodeCrypto is used. Otherwise, primes are generated using +40 rounds of the Miller-Rabin probabilistic random prime generation algorithm.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
bits + + +Integer + + + +

RSA bit length

e + + +Integer + + + +

RSA public exponent

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+
    +
  • module:crypto/public_key/prime
  • +
+
+ + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

RSA public modulus, RSA public exponent, RSA private exponent, +RSA private prime p, RSA private prime q, u = p ** -1 mod q

+
+ + + +
+
+ Type +
+
+ +Object + + +
+
+ + + + + + + + + + + + + +

(async, static) sign(hashAlgo, data, n, e, d, p, q, u, hashed) → {Promise.<Uint8Array>}

+ + + + + + +
+

Create signature

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
hashAlgo + + +module:enums.hash + + + +

Hash algorithm

data + + +Uint8Array + + + +

Message

n + + +Uint8Array + + + +

RSA public modulus

e + + +Uint8Array + + + +

RSA public exponent

d + + +Uint8Array + + + +

RSA private exponent

p + + +Uint8Array + + + +

RSA private prime p

q + + +Uint8Array + + + +

RSA private prime q

u + + +Uint8Array + + + +

RSA private coefficient

hashed + + +Uint8Array + + + +

Hashed message

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

RSA Signature.

+
+ + + +
+
+ Type +
+
+ +Promise.<Uint8Array> + + +
+
+ + + + + + + + + + + + + +

(async, static) validateParams(n, e, d, p, q, u) → {Promise.<Boolean>}

+ + + + + + +
+

Validate RSA parameters

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
n + + +Uint8Array + + + +

RSA public modulus

e + + +Uint8Array + + + +

RSA public exponent

d + + +Uint8Array + + + +

RSA private exponent

p + + +Uint8Array + + + +

RSA private prime p

q + + +Uint8Array + + + +

RSA private prime q

u + + +Uint8Array + + + +

RSA inverse of p w.r.t. q

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Whether params are valid.

+
+ + + +
+
+ Type +
+
+ +Promise.<Boolean> + + +
+
+ + + + + + + + + + + + + +

(async, static) verify(hashAlgo, data, s, n, e, hashed) → {Boolean}

+ + + + + + +
+

Verify signature

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
hashAlgo + + +module:enums.hash + + + +

Hash algorithm

data + + +Uint8Array + + + +

Message

s + + +Uint8Array + + + +

Signature

n + + +Uint8Array + + + +

RSA public modulus

e + + +Uint8Array + + + +

RSA public exponent

hashed + + +Uint8Array + + + +

Hashed message

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Boolean + + +
+
+ + + + + + + + + + + + + +

(inner) jwkToPrivate()

+ + + + + + +
+

Convert JWK private key to OpenPGP private key params

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(async, inner) privateToJWK(hashAlgo, n, e, d, p, q, u)

+ + + + + + +
+

Convert Openpgp private key params to jwk key according to

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
hashAlgo + + +String + + + +
n + + +Uint8Array + + + +
e + + +Uint8Array + + + +
d + + +Uint8Array + + + +
p + + +Uint8Array + + + +
q + + +Uint8Array + + + +
u + + +Uint8Array + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(inner) publicToJWK(hashAlgo, n, e)

+ + + + + + +
+

Convert Openpgp key public params to jwk key according to

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
hashAlgo + + +String + + + +
n + + +Uint8Array + + + +
e + + +Uint8Array + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-crypto_random.html b/docs/module-crypto_random.html new file mode 100644 index 00000000..f0074018 --- /dev/null +++ b/docs/module-crypto_random.html @@ -0,0 +1,516 @@ + + + + + JSDoc: Module: crypto/random + + + + + + + + + + +
+ +

Module: crypto/random

+ + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

Provides tools for retrieving secure randomness from browsers or Node.js

+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(async, static) getRandomBigInteger(min, max) → {bigint}

+ + + + + + +
+

Create a secure random BigInt that is greater than or equal to min and less than max.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
min + + +bigint + + + +

Lower bound, included

max + + +bigint + + + +

Upper bound, excluded

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Random BigInt.

+
+ + + +
+
+ Type +
+
+ +bigint + + +
+
+ + + + + + + + + + + + + +

(static) getRandomBytes(length) → {Uint8Array}

+ + + + + + +
+

Retrieve secure random byte array of the specified length

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
length + + +Integer + + + +

Length in bytes to generate

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Random byte array.

+
+ + + +
+
+ Type +
+
+ +Uint8Array + + +
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-crypto_signature.html b/docs/module-crypto_signature.html new file mode 100644 index 00000000..74e33e53 --- /dev/null +++ b/docs/module-crypto_signature.html @@ -0,0 +1,917 @@ + + + + + JSDoc: Module: crypto/signature + + + + + + + + + + +
+ +

Module: crypto/signature

+ + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

Provides functions for asymmetric signing and signature verification

+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(async, static) parseSignatureParams(algo, signature) → {Promise.<Object>}

+ + + + + + +
+

Parse signature in binary form to get the parameters. +The returned values are only padded for EdDSA, since in the other cases their expected length +depends on the key params, hence we delegate the padding to the signature verification function. +See RFC 4880 9.1 +See RFC 4880 5.2.2.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +module:enums.publicKey + + + +

Public key algorithm

signature + + +Uint8Array + + + +

Data for which the signature was created

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

True if signature is valid.

+
+ + + +
+
+ Type +
+
+ +Promise.<Object> + + +
+
+ + + + + + + + + + + + + +

(async, static) sign(algo, hashAlgo, publicKeyParams, privateKeyParams, data, hashed) → {Promise.<Object>}

+ + + + + + +
+

Creates a signature on data using specified algorithms and private key parameters. +See RFC 4880 9.1 +and RFC 4880 9.4 +for public key and hash algorithms.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +module:enums.publicKey + + + +

Public key algorithm

hashAlgo + + +module:enums.hash + + + +

Hash algorithm

publicKeyParams + + +Object + + + +

Algorithm-specific public and private key parameters

privateKeyParams + + +Object + + + +

Algorithm-specific public and private key parameters

data + + +Uint8Array + + + +

Data to be signed

hashed + + +Uint8Array + + + +

The hashed data

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Signature Object containing named signature parameters.

+
+ + + +
+
+ Type +
+
+ +Promise.<Object> + + +
+
+ + + + + + + + + + + + + +

(async, static) verify(algo, hashAlgo, signature, publicParams, data, hashed) → {Promise.<Boolean>}

+ + + + + + +
+

Verifies the signature provided for data using specified algorithms and public key parameters. +See RFC 4880 9.1 +and RFC 4880 9.4 +for public key and hash algorithms.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
algo + + +module:enums.publicKey + + + +

Public key algorithm

hashAlgo + + +module:enums.hash + + + +

Hash algorithm

signature + + +Object + + + +

Named algorithm-specific signature parameters

publicParams + + +Object + + + +

Algorithm-specific public key parameters

data + + +Uint8Array + + + +

Data for which the signature was created

hashed + + +Uint8Array + + + +

The hashed data

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

True if signature is valid.

+
+ + + +
+
+ Type +
+
+ +Promise.<Boolean> + + +
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-encoding_base64.html b/docs/module-encoding_base64.html new file mode 100644 index 00000000..ae26bfde --- /dev/null +++ b/docs/module-encoding_base64.html @@ -0,0 +1,769 @@ + + + + + JSDoc: Module: encoding/base64 + + + + + + + + + + +
+ +

Module: encoding/base64

+ + + + + + +
+ +
+ + + +
+ +
+
+ + + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(static) b64ToUint8Array(base64) → {Uint8Array}

+ + + + + + +
+

Convert a Base-64 encoded string an array of 8-bit integer

+

Note: accepts both Radix-64 and URL-safe strings

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
base64 + + +String + + + +

Base-64 encoded string to convert

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

An array of 8-bit integers.

+
+ + + +
+
+ Type +
+
+ +Uint8Array + + +
+
+ + + + + + + + + + + + + +

(static) decode(data) → {Uint8Array|ReadableStream.<Uint8Array>}

+ + + + + + +
+

Convert radix-64 to binary array

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
data + + +String +| + +ReadableStream.<String> + + + +

Radix-64 string to convert

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Binary array version of input string.

+
+ + + +
+
+ Type +
+
+ +Uint8Array +| + +ReadableStream.<Uint8Array> + + +
+
+ + + + + + + + + + + + + +

(static) encode(data) → {String|ReadableStream.<String>}

+ + + + + + +
+

Convert binary array to radix-64

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
data + + +Uint8Array +| + +ReadableStream.<Uint8Array> + + + +

Uint8Array to convert

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Radix-64 version of input string.

+
+ + + +
+
+ Type +
+
+ +String +| + +ReadableStream.<String> + + +
+
+ + + + + + + + + + + + + +

(static) uint8ArrayToB64(bytes, url) → {String}

+ + + + + + +
+

Convert an array of 8-bit integer to a Base-64 encoded string

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
bytes + + +Uint8Array + + + +

An array of 8-bit integers to convert

url + + +bool + + + +

If true, output is URL-safe

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Base-64 encoded string.

+
+ + + +
+
+ Type +
+
+ +String + + +
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-enums.html b/docs/module-enums.html index f8640127..65f76fb0 100644 --- a/docs/module-enums.html +++ b/docs/module-enums.html @@ -148,6 +148,29 @@ + + + + + + + + gcm + + + + + +Integer + + + + + + + + + @@ -212,7 +235,7 @@
Source:
@@ -476,7 +499,7 @@
Source:
@@ -671,7 +694,7 @@
Source:
@@ -740,7 +763,7 @@ - p256 + nistP256 @@ -763,7 +786,7 @@ - "P-256" + p256 @@ -786,122 +809,7 @@ - secp256r1 - - - - - -String - - - - - - - - - - - - - - - - - prime256v1 - - - - - -String - - - - - - - - - - - - - - - - - "1.2.840.10045.3.1.7" - - - - - -String - - - - - - - - - - - - - - - - - 2a8648ce3d030107 - - - - - -String - - - - - - - - - - - - - - - - - 2A8648CE3D030107 - - - - - -String - - - - - - - - - - - - - - - - - p384 + nistP384 @@ -924,7 +832,7 @@ - "P-384" + p384 @@ -947,99 +855,7 @@ - secp384r1 - - - - - -String - - - - - - - - - - - - - - - - - "1.3.132.0.34" - - - - - -String - - - - - - - - - - - - - - - - - 2b81040022 - - - - - -String - - - - - - - - - - - - - - - - - 2B81040022 - - - - - -String - - - - - - - - - - - - - - - - - p521 + nistP521 @@ -1062,99 +878,7 @@ - "P-521" - - - - - -String - - - - - - - - - - - - - - - - - secp521r1 - - - - - -String - - - - - - - - - - - - - - - - - "1.3.132.0.35" - - - - - -String - - - - - - - - - - - - - - - - - 2b81040023 - - - - - -String - - - - - - - - - - - - - - - - - 2B81040023 + p521 @@ -1198,75 +922,6 @@ - - - "1.3.132.0.10" - - - - - -String - - - - - - - - - - - - - - - - - 2b8104000a - - - - - -String - - - - - - - - - - - - - - - - - 2B8104000A - - - - - -String - - - - - - - - - - - - - - ed25519Legacy @@ -1290,29 +945,6 @@ - - - ED25519 - - - - - -String - - - - - - - - - - - - - - ed25519 @@ -1331,98 +963,6 @@ - - - - - - - - Ed25519 - - - - - -String - - - - - - - - - - - - - - - - - "1.3.6.1.4.1.11591.15.1" - - - - - -String - - - - - - - - - - - - - - - - - 2b06010401da470f01 - - - - - -String - - - - - - - - - - - - - - - - - 2B06010401DA470F01 - - - - - -String - - - - - - - - - @@ -1451,52 +991,6 @@ - - - X25519 - - - - - -String - - - - - - - - - - - - - - - - - cv25519 - - - - - -String - - - - - - - - - - - - - - curve25519 @@ -1515,98 +1009,6 @@ - - - - - - - - Curve25519 - - - - - -String - - - - - - - - - - - - - - - - - "1.3.6.1.4.1.3029.1.5.1" - - - - - -String - - - - - - - - - - - - - - - - - 2b060104019755010501 - - - - - -String - - - - - - - - - - - - - - - - - 2B060104019755010501 - - - - - -String - - - - - - - - - @@ -1635,75 +1037,6 @@ - - - "1.3.36.3.3.2.8.1.1.7" - - - - - -String - - - - - - - - - - - - - - - - - 2b2403030208010107 - - - - - -String - - - - - - - - - - - - - - - - - 2B2403030208010107 - - - - - -String - - - - - - - - - - - - - - brainpoolP384r1 @@ -1727,75 +1060,6 @@ - - - "1.3.36.3.3.2.8.1.1.11" - - - - - -String - - - - - - - - - - - - - - - - - 2b240303020801010b - - - - - -String - - - - - - - - - - - - - - - - - 2B240303020801010B - - - - - -String - - - - - - - - - - - - - - brainpoolP512r1 @@ -1818,75 +1082,6 @@ - - - - "1.3.36.3.3.2.8.1.1.13" - - - - - -String - - - - - - - - - - - - - - - - - 2b240303020801010d - - - - - -String - - - - - - - - - - - - - - - - - 2B240303020801010D - - - - - -String - - - - - - - - - - - - - @@ -1924,7 +1119,7 @@
Source:
@@ -2068,6 +1263,29 @@ fingerprint format

+ + + + seipdv2 + + + + + +Integer + + + + + + + + + + + + + @@ -2105,7 +1323,7 @@ fingerprint format

Source:
@@ -2328,6 +1546,52 @@ fingerprint format

+ + + + + + + + sha3_256 + + + + + +Integer + + + + + + + + + + + + + + + + + sha3_512 + + + + + +Integer + + + + + + + + + @@ -2369,7 +1633,7 @@ fingerprint format

Source:
@@ -2635,7 +1899,7 @@ possession of more than one person.

Source:
@@ -2830,7 +2094,7 @@ possession of more than one person.

Source:
@@ -3306,6 +2570,29 @@ possession of more than one person.

+ + + + + + + + padding + + + + + +Integer + + + + + + + + + @@ -3347,7 +2634,7 @@ possession of more than one person.

Source:
@@ -3599,52 +2886,6 @@ possession of more than one person.

- - - ed25519Legacy - - - - - -Integer - - - - - - - - - - - - - - - - - eddsa - - - - - -Integer - - - - - - - - - - - - - - aedh @@ -3819,7 +3060,7 @@ possession of more than one person.

Source:
@@ -4037,7 +3278,7 @@ possession of more than one person.

Source:
@@ -4168,6 +3409,29 @@ possession of more than one person.

+ + + + + + + + argon2 + + + + + +Integer + + + + + + + + + @@ -4232,7 +3496,7 @@ possession of more than one person.

Source:
@@ -4749,7 +4013,7 @@ document) that cannot include a target subpacket.

Source:
@@ -5048,7 +4312,7 @@ document) that cannot include a target subpacket.

- issuer + issuerKeyID @@ -5409,6 +4673,29 @@ document) that cannot include a target subpacket.

+ + + + + + + + preferredCipherSuites + + + + + +Integer + + + + + + + + + @@ -5450,7 +4737,7 @@ document) that cannot include a target subpacket.

Source:
@@ -5517,29 +4804,6 @@ document) that cannot include a target subpacket.

- - - plaintext - - - - - -Integer - - - - - - - - - - - - - - idea @@ -5760,7 +5024,7 @@ document) that cannot include a target subpacket.

Source:
@@ -5956,7 +5220,7 @@ document) that cannot include a target subpacket.

Source:
@@ -6110,7 +5374,7 @@ document) that cannot include a target subpacket.

Source:
@@ -6326,7 +5590,7 @@ document) that cannot include a target subpacket.

Source:
@@ -6423,7 +5687,7 @@ document) that cannot include a target subpacket.


diff --git a/docs/module-key_Subkey-Subkey.html b/docs/module-key_Subkey-Subkey.html index bbb8c77c..bed9f3e6 100644 --- a/docs/module-key_Subkey-Subkey.html +++ b/docs/module-key_Subkey-Subkey.html @@ -28,7 +28,8 @@
-

Subkey(subkeyPacket, mainKey)

+

+ key/Subkey~Subkey(subkeyPacket, mainKey)

Class that represents a subkey packet and the relevant signatures.

@@ -170,7 +171,7 @@
Source:
@@ -280,7 +281,7 @@
Source:
@@ -393,7 +394,7 @@
Source:
@@ -510,7 +511,7 @@
Source:
@@ -627,7 +628,7 @@
Source:
@@ -740,7 +741,7 @@
Source:
@@ -941,7 +942,7 @@ Returns null if the subkey is invalid.

Source:
@@ -1054,7 +1055,7 @@ Returns null if the subkey is invalid.

Source:
@@ -1171,7 +1172,7 @@ Returns null if the subkey is invalid.

Source:
@@ -1288,7 +1289,7 @@ Returns null if the subkey is invalid.

Source:
@@ -1405,7 +1406,7 @@ Returns null if the subkey is invalid.

Source:
@@ -1522,7 +1523,7 @@ Returns null if the subkey is invalid.

Source:
@@ -1639,7 +1640,7 @@ Returns null if the subkey is invalid.

Source:
@@ -1756,7 +1757,7 @@ Returns null if the subkey is invalid.

Source:
@@ -1872,7 +1873,7 @@ Returns null if the subkey is invalid.

Source:
@@ -2148,7 +2149,7 @@ Returns null if the subkey is invalid.

Source:
@@ -2486,7 +2487,7 @@ Returns null if the subkey is invalid.

Source:
@@ -2598,7 +2599,7 @@ Returns null if the subkey is invalid.

Source:
@@ -2831,7 +2832,7 @@ Returns null if the subkey is invalid.

Source:
@@ -3043,7 +3044,7 @@ and valid binding signature.

Source:
@@ -3136,7 +3137,7 @@ and valid binding signature.


diff --git a/docs/module-key_Subkey.html b/docs/module-key_Subkey.html new file mode 100644 index 00000000..e0d39f96 --- /dev/null +++ b/docs/module-key_Subkey.html @@ -0,0 +1,92 @@ + + + + + JSDoc: Module: key/Subkey + + + + + + + + + + +
+ +

Module: key/Subkey

+ + + + + + +
+ +
+ + + +
+ +
+
+ + + + + +
+ + + + + + +

Classes

+ +
+
Subkey
+
+
+ + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-key_User-User.html b/docs/module-key_User-User.html index 3d50a5c0..c34cc575 100644 --- a/docs/module-key_User-User.html +++ b/docs/module-key_User-User.html @@ -28,7 +28,8 @@
-

User(userPacket, mainKey)

+

+ key/User~User(userPacket, mainKey)

Class that represents an user ID or attribute packet and the relevant signatures.

@@ -170,7 +171,7 @@
Source:
@@ -403,7 +404,7 @@
Source:
@@ -515,7 +516,7 @@
Source:
@@ -788,7 +789,7 @@
Source:
@@ -1126,7 +1127,7 @@
Source:
@@ -1238,7 +1239,7 @@
Source:
@@ -1441,7 +1442,7 @@
Source:
@@ -1622,7 +1623,7 @@ and validity of self signature.

Source:
@@ -1886,7 +1887,7 @@ and validity of self signature.

Source:
@@ -2153,7 +2154,7 @@ Signature validity is null if the verification keys do not correspond to the cer
Source:
@@ -2233,7 +2234,7 @@ Signature validity is null if the verification keys do not correspond to the cer
diff --git a/docs/module-key_User.html b/docs/module-key_User.html new file mode 100644 index 00000000..f915884a --- /dev/null +++ b/docs/module-key_User.html @@ -0,0 +1,92 @@ + + + + + JSDoc: Module: key/User + + + + + + + + + + +
+ +

Module: key/User

+ + + + + + +
+ +
+ + + +
+ +
+
+ + + + + +
+ + + + + + +

Classes

+ +
+
User
+
+
+ + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-key_helper.html b/docs/module-key_helper.html new file mode 100644 index 00000000..b12b096d --- /dev/null +++ b/docs/module-key_helper.html @@ -0,0 +1,2891 @@ + + + + + JSDoc: Module: key/helper + + + + + + + + + + +
+ +

Module: key/helper

+ + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

Provides helpers methods for key module

+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(static) checkKeyRequirements(keyPacket, config)

+ + + + + + +
+

Check key against blacklisted algorithms and minimum strength requirements.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
keyPacket + + +SecretKeyPacket +| + +PublicKeyPacket +| + +SecretSubkeyPacket +| + +PublicSubkeyPacket + + + +
config + + +Config + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Throws:
+ + + +
+
+
+

if the key packet does not meet the requirements

+
+
+
+
+
+
+ Type +
+
+ +Error + + +
+
+
+
+
+ + + + + + + + + + + + + + + + +

(static) createBindingSignature(subkey, primaryKey, options, config)

+ + + + + + +
+

Create Binding signature to the key according to the https://tools.ietf.org/html/rfc4880#section-5.2.1

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
subkey + + +SecretSubkeyPacket + + + +

Subkey key packet

primaryKey + + +SecretKeyPacket + + + +

Primary key packet

options + + +Object + + + +
config + + +Object + + + +

Full configuration

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(static) createSignaturePacket(dataToSign, privateKey, signingKeyPacket, signaturePropertiesopt, dateopt, userIDopt, notationsopt, detachedopt, config) → {Promise.<SignaturePacket>}

+ + + + + + +
+

Create signature packet

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
dataToSign + + +Object + + + + + + + + + +

Contains packets to be signed

privateKey + + +PrivateKey + + + + + + + + + +

key to get preferences from

signingKeyPacket + + +SecretKeyPacket +| + +SecretSubkeyPacket + + + + + + + + + +

secret key packet for signing

signatureProperties + + +Object + + + + + + <optional>
+ + + + + +

Properties to write on the signature packet before signing

date + + +Date + + + + + + <optional>
+ + + + + +

Override the creationtime of the signature

userID + + +Object + + + + + + <optional>
+ + + + + +

User ID

notations + + +Array + + + + + + <optional>
+ + + + + +

Notation Data to add to the signature, e.g. [{ name: 'test@example.org', value: new TextEncoder().encode('test'), humanReadable: true, critical: false }]

detached + + +Object + + + + + + <optional>
+ + + + + +

Whether to create a detached signature packet

config + + +Object + + + + + + + + + +

full configuration

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Signature packet.

+
+ + + +
+
+ Type +
+
+ +Promise.<SignaturePacket> + + +
+
+ + + + + + + + + + + + + +

(static) getKeyExpirationTime(keyPacket, signature) → {Date|Infinity}

+ + + + + + +
+

Returns key expiration time based on the given certification signature. +The expiration time of the signature is ignored.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
keyPacket + + +PublicSubkeyPacket +| + +PublicKeyPacket + + + +

key to check

signature + + +SignaturePacket + + + +

signature to process

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

expiration time or infinity if the key does not expire

+
+ + + +
+
+ Type +
+
+ +Date +| + +Infinity + + +
+
+ + + + + + + + + + + + + +

(async, static) getLatestValidSignature(signatures, publicKey, signatureType, date, config) → {Promise.<SignaturePacket>}

+ + + + + + +
+

Returns the valid and non-expired signature that has the latest creation date, while ignoring signatures created in the future.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
signatures + + +Array.<SignaturePacket> + + + +

List of signatures

publicKey + + +PublicKeyPacket +| + +PublicSubkeyPacket + + + +

Public key packet to verify the signature

signatureType + + +module:enums.signature + + + +

Signature type to determine how to hash the data (NB: for userID signatures, +enums.signatures.certGeneric should be given regardless of the actual trust level)

date + + +Date + + + +

Use the given date instead of the current time

config + + +Object + + + +

full configuration

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

The latest valid signature.

+
+ + + +
+
+ Type +
+
+ +Promise.<SignaturePacket> + + +
+
+ + + + + + + + + + + + + +

(async, static) getPreferredCipherSuite(keysopt, dateopt, userIDsopt, configopt) → {Promise.<{symmetricAlgo: module:enums.symmetric, aeadAlgo: (module:enums.aead|undefined)}>}

+ + + + + + +
+

Returns the preferred symmetric and AEAD algorithm (if any) for a set of keys

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
keys + + +Array.<Key> + + + + + + <optional>
+ + + + + +

Set of keys

date + + +Date + + + + + + <optional>
+ + + + + +

Use the given date for verification instead of the current time

userIDs + + +Array + + + + + + <optional>
+ + + + + +

User IDs

config + + +Object + + + + + + <optional>
+ + + + + +

Full configuration, defaults to openpgp.config

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Object containing the preferred symmetric algorithm, and the preferred AEAD algorithm, or undefined if CFB is preferred

+
+ + + +
+
+ Type +
+
+ +Promise.<{symmetricAlgo: module:enums.symmetric, aeadAlgo: (module:enums.aead|undefined)}> + + +
+
+ + + + + + + + + + + + + +

(async, static) getPreferredCompressionAlgo(keysopt, dateopt, userIDsopt, configopt) → {Promise.<module:enums.compression>}

+ + + + + + +
+

Returns the preferred compression algorithm for a set of keys

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
keys + + +Array.<Key> + + + + + + <optional>
+ + + + + +

Set of keys

date + + +Date + + + + + + <optional>
+ + + + + +

Use the given date for verification instead of the current time

userIDs + + +Array + + + + + + <optional>
+ + + + + +

User IDs

config + + +Object + + + + + + <optional>
+ + + + + +

Full configuration, defaults to openpgp.config

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Preferred compression algorithm

+
+ + + +
+
+ Type +
+
+ +Promise.<module:enums.compression> + + +
+
+ + + + + + + + + + + + + +

(async, static) getPreferredHashAlgo(keyopt, keyPacket, dateopt, userIDopt, config) → {Promise.<enums.hash>}

+ + + + + + +
+

Returns the preferred signature hash algorithm of a key

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
key + + +Key + + + + + + <optional>
+ + + + + +

The key to get preferences from

keyPacket + + +SecretKeyPacket +| + +SecretSubkeyPacket + + + + + + + + + +

key packet used for signing

date + + +Date + + + + + + <optional>
+ + + + + +

Use the given date for verification instead of the current time

userID + + +Object + + + + + + <optional>
+ + + + + +

User ID

config + + +Object + + + + + + + + + +

full configuration

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Promise.<enums.hash> + + +
+
+ + + + + + + + + + + + + +

(async, static) isDataRevoked(primaryKey, dataToVerify, revocations, signature, key,, date, config) → {Promise.<Boolean>}

+ + + + + + +
+

Checks if a given certificate or binding signature is revoked

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
primaryKey + + +SecretKeyPacket +| + +PublicKeyPacket + + + +

The primary key packet

dataToVerify + + +Object + + + +

The data to check

revocations + + +Array.<SignaturePacket> + + + +

The revocation signatures to check

signature + + +SignaturePacket + + + +

The certificate or signature to check

key, + + +PublicSubkeyPacket +| + +SecretSubkeyPacket +| + +PublicKeyPacket +| + +SecretKeyPacket + + + +

optional The key packet to verify the signature, instead of the primary key

date + + +Date + + + +

Use the given date instead of the current time

config + + +Object + + + +

Full configuration

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

True if the signature revokes the data.

+
+ + + +
+
+ Type +
+
+ +Promise.<Boolean> + + +
+
+ + + + + + + + + + + + + +

(static) mergeSignatures(source, dest, attr, dateopt, checkFnopt)

+ + + + + + +
+

Merges signatures from source[attr] to dest[attr]

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
source + + +Object + + + + + + + + + +
dest + + +Object + + + + + + + + + +
attr + + +String + + + + + + + + + +
date + + +Date + + + + + + <optional>
+ + + + + +

date to use for signature expiration check, instead of the current time

checkFn + + +function + + + + + + <optional>
+ + + + + +

signature only merged if true

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-packet_packet.html b/docs/module-packet_packet.html new file mode 100644 index 00000000..b44b87f0 --- /dev/null +++ b/docs/module-packet_packet.html @@ -0,0 +1,866 @@ + + + + + JSDoc: Module: packet/packet + + + + + + + + + + +
+ +

Module: packet/packet

+ + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

Functions for reading and writing packets

+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(static) readPackets(input, callback) → {Boolean}

+ + + + + + +
+

Generic static Packet Parser function

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
input + + +Uint8Array +| + +ReadableStream.<Uint8Array> + + + +

Input stream as string

callback + + +function + + + +

Function to call with the parsed packet

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Returns false if the stream was empty and parsing is done, and true otherwise.

+
+ + + +
+
+ Type +
+
+ +Boolean + + +
+
+ + + + + + + + + + + + + +

(static) supportsStreaming(tag) → {Boolean}

+ + + + + + +
+

Whether the packet type supports partial lengths per RFC4880

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
tag + + +Integer + + + +

Tag type

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

String of the header.

+
+ + + +
+
+ Type +
+
+ +Boolean + + +
+
+ + + + + + + + + + + + + +

(static) writeHeader(tag_type, length) → {String}

+ + + + + + +
+

Writes a packet header version 4 with the given tag_type and length to a +string

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
tag_type + + +Integer + + + +

Tag type

length + + +Integer + + + +

Length of the payload

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

String of the header.

+
+ + + +
+
+ Type +
+
+ +String + + +
+
+ + + + + + + + + + + + + +

(static) writeSimpleLength(length) → {Uint8Array}

+ + + + + + +
+

Encodes a given integer of length to the openpgp length specifier to a +string

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
length + + +Integer + + + +

The length to encode

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

String with openpgp length representation.

+
+ + + +
+
+ Type +
+
+ +Uint8Array + + +
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-type_ecdh_symkey.html b/docs/module-type_ecdh_symkey.html new file mode 100644 index 00000000..02b27bc6 --- /dev/null +++ b/docs/module-type_ecdh_symkey.html @@ -0,0 +1,167 @@ + + + + + JSDoc: Module: type/ecdh_symkey + + + + + + + + + + +
+ +

Module: type/ecdh_symkey

+ + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

Encoded symmetric key for ECDH (incl. legacy x25519)

+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-type_kdf_params-KDFParams.html b/docs/module-type_kdf_params-KDFParams.html index ffec6bcf..9dabe344 100644 --- a/docs/module-type_kdf_params-KDFParams.html +++ b/docs/module-type_kdf_params-KDFParams.html @@ -163,7 +163,7 @@
Source:
@@ -322,7 +322,7 @@
Source:
@@ -434,7 +434,7 @@
Source:
@@ -502,7 +502,7 @@
diff --git a/docs/module-type_keyid-KeyID.html b/docs/module-type_keyid-KeyID.html index 2962d2a4..470acc8a 100644 --- a/docs/module-type_keyid-KeyID.html +++ b/docs/module-type_keyid-KeyID.html @@ -28,7 +28,8 @@
-

KeyID()

+

+ type/keyid~KeyID()

Implementation of type key id

RFC4880 3.3: @@ -100,7 +101,7 @@ formed.

Source:
@@ -294,7 +295,7 @@ formed.

Source:
@@ -384,7 +385,7 @@ formed.

Source:
@@ -496,7 +497,7 @@ formed.

Source:
@@ -657,7 +658,7 @@ formed.

Source:
@@ -747,7 +748,7 @@ formed.

Source:
@@ -859,7 +860,7 @@ formed.

Source:
@@ -927,7 +928,7 @@ formed.


diff --git a/docs/module-type_keyid.html b/docs/module-type_keyid.html new file mode 100644 index 00000000..e953b456 --- /dev/null +++ b/docs/module-type_keyid.html @@ -0,0 +1,92 @@ + + + + + JSDoc: Module: type/keyid + + + + + + + + + + +
+ +

Module: type/keyid

+ + + + + + +
+ +
+ + + +
+ +
+
+ + + + + +
+ + + + + + +

Classes

+ +
+
KeyID
+
+
+ + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-type_oid.html b/docs/module-type_oid.html new file mode 100644 index 00000000..524d78c1 --- /dev/null +++ b/docs/module-type_oid.html @@ -0,0 +1,178 @@ + + + + + JSDoc: Module: type/oid + + + + + + + + + + +
+ +

Module: type/oid

+ + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

Wrapper to an OID value

+

RFC6637, section 11: +The sequence of octets in the third column is the result of applying +the Distinguished Encoding Rules (DER) to the ASN.1 Object Identifier +with subsequent truncation. The truncation removes the two fields of +encoded Object Identifier. The first omitted field is one octet +representing the Object Identifier tag, and the second omitted field +is the length of the Object Identifier body. For example, the +complete ASN.1 DER encoding for the NIST P-256 curve OID is "06 08 2A +86 48 CE 3D 03 01 07", from which the first entry in the table above +is constructed by omitting the first two octets. Only the truncated +sequence of octets is the valid representation of a curve OID.

+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-type_s2k-GenericS2K.html b/docs/module-type_s2k-GenericS2K.html new file mode 100644 index 00000000..c557dd41 --- /dev/null +++ b/docs/module-type_s2k-GenericS2K.html @@ -0,0 +1,969 @@ + + + + + JSDoc: Class: GenericS2K + + + + + + + + + + +
+ +

Class: GenericS2K

+ + + + + + +
+ +
+ +

+ type/s2k~GenericS2K(configopt)

+ + +
+ +
+
+ + + + + + +

new GenericS2K(configopt)

+ + + + + + + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
config + + +Object + + + + + + <optional>
+ + + + + +

Full configuration, defaults to openpgp.config

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +

Members

+ + + +

algorithm :module:enums.hash|0

+ + + + +
+

Hash function identifier, or 0 for gnu-dummy keys

+
+ + + +
Type:
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

c :Integer

+ + + + + + +
Type:
+
    +
  • + +Integer + + +
  • +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

salt :Uint8Array

+ + + + +
+

Eight bytes of salt in a binary string.

+
+ + + +
Type:
+
    +
  • + +Uint8Array + + +
  • +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

type :String

+ + + + +
+

enums.s2k identifier or 'gnu-dummy'

+
+ + + +
Type:
+
    +
  • + +String + + +
  • +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + +

Methods

+ + + + + + + +

(async) produceKey(passphrase) → {Promise.<Uint8Array>}

+ + + + + + +
+

Produces a key using the specified passphrase and the defined +hashAlgorithm

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
passphrase + + +String + + + +

Passphrase containing user input

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Produced key with a length corresponding to. +hashAlgorithm hash length

+
+ + + +
+
+ Type +
+
+ +Promise.<Uint8Array> + + +
+
+ + + + + + + + + + + + + +

read(bytes) → {Integer}

+ + + + + + +
+

Parsing function for a string-to-key specifier (RFC 4880 3.7).

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
bytes + + +Uint8Array + + + +

Payload of string-to-key specifier

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Actual length of the object.

+
+ + + +
+
+ Type +
+
+ +Integer + + +
+
+ + + + + + + + + + + + + +

write() → {Uint8Array}

+ + + + + + +
+

Serializes s2k information

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+

Binary representation of s2k.

+
+ + + +
+
+ Type +
+
+ +Uint8Array + + +
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-type_s2k-S2K.html b/docs/module-type_s2k-S2K.html deleted file mode 100644 index 8c02d700..00000000 --- a/docs/module-type_s2k-S2K.html +++ /dev/null @@ -1,968 +0,0 @@ - - - - - JSDoc: Class: S2K - - - - - - - - - - -
- -

Class: S2K

- - - - - - -
- -
- -

S2K(configopt)

- - -
- -
-
- - - - - - -

new S2K(configopt)

- - - - - - - - - - - - - - -
Parameters:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeAttributesDescription
config - - -Object - - - - - - <optional>
- - - - - -

Full configuration, defaults to openpgp.config

- - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - -

Members

- - - -

algorithm :module:enums.hash|0

- - - - -
-

Hash function identifier, or 0 for gnu-dummy keys

-
- - - -
Type:
- - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - -

c :Integer

- - - - - - -
Type:
-
    -
  • - -Integer - - -
  • -
- - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - -

salt :Uint8Array

- - - - -
-

Eight bytes of salt in a binary string.

-
- - - -
Type:
-
    -
  • - -Uint8Array - - -
  • -
- - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - -

type :String

- - - - -
-

enums.s2k identifier or 'gnu-dummy'

-
- - - -
Type:
-
    -
  • - -String - - -
  • -
- - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - -

Methods

- - - - - - - -

(async) produceKey(passphrase) → {Promise.<Uint8Array>}

- - - - - - -
-

Produces a key using the specified passphrase and the defined -hashAlgorithm

-
- - - - - - - - - -
Parameters:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
passphrase - - -String - - - -

Passphrase containing user input

- - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - - - - - -
Returns:
- - -
-

Produced key with a length corresponding to. -hashAlgorithm hash length

-
- - - -
-
- Type -
-
- -Promise.<Uint8Array> - - -
-
- - - - - - - - - - - - - -

read(bytes) → {Integer}

- - - - - - -
-

Parsing function for a string-to-key specifier (RFC 4880 3.7).

-
- - - - - - - - - -
Parameters:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
bytes - - -Uint8Array - - - -

Payload of string-to-key specifier

- - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - - - - - -
Returns:
- - -
-

Actual length of the object.

-
- - - -
-
- Type -
-
- -Integer - - -
-
- - - - - - - - - - - - - -

write() → {Uint8Array}

- - - - - - -
-

Serializes s2k information

-
- - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - - - - - -
Returns:
- - -
-

Binary representation of s2k.

-
- - - -
-
- Type -
-
- -Uint8Array - - -
-
- - - - - - - - - - - - - -
- -
- - - - -
- - - -
- - - - - - - \ No newline at end of file diff --git a/docs/module-type_s2k.html b/docs/module-type_s2k.html new file mode 100644 index 00000000..9121ad81 --- /dev/null +++ b/docs/module-type_s2k.html @@ -0,0 +1,180 @@ + + + + + JSDoc: Module: type/s2k + + + + + + + + + + +
+ +

Module: type/s2k

+ + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

Implementation of the String-to-key specifier

+

RFC4880 3.7: +String-to-key (S2K) specifiers are used to convert passphrase strings +into symmetric-key encryption/decryption keys. They are used in two +places, currently: to encrypt the secret part of private keys in the +private keyring, and to convert passphrases to encryption keys for +symmetrically encrypted messages.

+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + +

Classes

+ +
+
GenericS2K
+
+
+ + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-type_x25519x448_symkey.html b/docs/module-type_x25519x448_symkey.html index def7fac8..958650bd 100644 --- a/docs/module-type_x25519x448_symkey.html +++ b/docs/module-type_x25519x448_symkey.html @@ -91,7 +91,7 @@ the former includes an algorithm byte preceeding the encrypted session key.

<
Source:
@@ -154,7 +154,7 @@ the former includes an algorithm byte preceeding the encrypted session key.

<
diff --git a/docs/module-util.html b/docs/module-util.html new file mode 100644 index 00000000..7675f6db --- /dev/null +++ b/docs/module-util.html @@ -0,0 +1,167 @@ + + + + + JSDoc: Module: util + + + + + + + + + + +
+ +

Module: util

+ + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

This object contains utility functions

+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/lightweight/package.json b/lightweight/package.json index 134e03c9..c141a35d 100644 --- a/lightweight/package.json +++ b/lightweight/package.json @@ -1,5 +1,5 @@ { "name": "openpgp-lightweight", - "main": "../dist/lightweight/openpgp.min.mjs", + "browser": "../dist/lightweight/openpgp.min.mjs", "types": "../openpgp.d.ts" } diff --git a/openpgp.d.ts b/openpgp.d.ts index 56175b26..b4d54b5b 100644 --- a/openpgp.d.ts +++ b/openpgp.d.ts @@ -1,3 +1,5 @@ +/* eslint-disable max-lines, @typescript-eslint/indent */ + /** * Type definitions for OpenPGP.js http://openpgpjs.org/ * @@ -7,9 +9,19 @@ * - Errietta Kostala */ -import type { WebStream as GenericWebStream, NodeStream as GenericNodeStream } from '@openpgp/web-stream-tools'; +import type { WebStream as GenericWebStream, NodeWebStream as GenericNodeWebStream } from '@openpgp/web-stream-tools'; -/* ############## v5 KEY #################### */ +/* ############## STREAM #################### */ +type Data = Uint8Array | string; +// web-stream-tools might end up supporting additional data types, so we re-declare the types +// to enforce the type contraint that we need. +export type WebStream = GenericWebStream; +export type NodeWebStream = GenericNodeWebStream; +export type Stream = WebStream | NodeWebStream; +export type MaybeStream = T | Stream; +type MaybeArray = T | Array; + +/* ############## KEY #################### */ // The Key and PublicKey types can be used interchangably since TS cannot detect the difference, as they have the same class properties. // The declared readKey(s) return type is Key instead of a PublicKey since it seems more obvious that a Key can be cast to a PrivateKey. export function readKey(options: { armoredKey: string, config?: PartialConfig }): Promise; @@ -54,8 +66,8 @@ export abstract class Key { // NB: the order of the `update` declarations matters, since PublicKey includes PrivateKey public update(sourceKey: PrivateKey, date?: Date, config?: Config): Promise; public update(sourceKey: PublicKey, date?: Date, config?: Config): Promise; - public signPrimaryUser(privateKeys: PrivateKey[], date?: Date, userID?: UserID, config?: Config): Promise - public signAllUsers(privateKeys: PrivateKey[], date?: Date, config?: Config): Promise + public signPrimaryUser(privateKeys: PrivateKey[], date?: Date, userID?: UserID, config?: Config): Promise; + public signAllUsers(privateKeys: PrivateKey[], date?: Date, config?: Config): Promise; public verifyPrimaryKey(date?: Date, userID?: UserID, config?: Config): Promise; // throws on error public verifyPrimaryUser(publicKeys: PublicKey[], date?: Date, userIDs?: UserID, config?: Config): Promise<{ keyID: KeyID, valid: boolean | null }[]>; public verifyAllUsers(publicKeys?: PublicKey[], date?: Date, config?: Config): Promise<{ userID: string, keyID: KeyID, valid: boolean | null }[]>; @@ -82,7 +94,7 @@ export class PrivateKey extends PublicKey { public revoke(reason?: ReasonForRevocation, date?: Date, config?: Config): Promise; public isDecrypted(): boolean; public addSubkey(options: SubkeyOptions): Promise; - public getDecryptionKeys(keyID?: KeyID, date?: Date | null, userID?: UserID, config?: Config): Promise + public getDecryptionKeys(keyID?: KeyID, date?: Date | null, userID?: UserID, config?: Config): Promise; public update(sourceKey: PublicKey, date?: Date, config?: Config): Promise; } @@ -98,9 +110,9 @@ export class Subkey { public getCreationTime(): Date; public getAlgorithmInfo(): AlgorithmInfo; public getKeyID(): KeyID; - public getExpirationTime(date?: Date, config?: Config): Promise + public getExpirationTime(date?: Date, config?: Config): Promise; public isRevoked(signature: SignaturePacket, key: AnyKeyPacket, date?: Date, config?: Config): Promise; - public update(subKey: Subkey, date?: Date, config?: Config): Promise + public update(subKey: Subkey, date?: Date, config?: Config): Promise; public revoke(primaryKey: SecretKeyPacket, reasonForRevocation?: ReasonForRevocation, date?: Date, config?: Config): Promise; } @@ -118,13 +130,13 @@ export interface PrimaryUser { selfCertification: SignaturePacket; } -type AlgorithmInfo = { +export type AlgorithmInfo = { algorithm: enums.publicKeyNames; bits?: number; curve?: EllipticCurveName; }; -/* ############## v5 SIG #################### */ +/* ############## SIG #################### */ export function readSignature(options: { armoredSignature: string, config?: PartialConfig }): Promise; export function readSignature(options: { binarySignature: Uint8Array, config?: PartialConfig }): Promise; @@ -143,7 +155,7 @@ interface VerificationResult { signature: Promise; } -/* ############## v5 CLEARTEXT #################### */ +/* ############## CLEARTEXT #################### */ export function readCleartextMessage(options: { cleartextMessage: string, config?: PartialConfig }): Promise; @@ -176,12 +188,12 @@ export class CleartextMessage { verify(keys: PublicKey[], date?: Date, config?: Config): Promise; } -/* ############## v5 MSG #################### */ +/* ############## MSG #################### */ export function generateSessionKey(options: { encryptionKeys: MaybeArray, date?: Date, encryptionUserIDs?: MaybeArray, config?: PartialConfig }): Promise; export function encryptSessionKey(options: EncryptSessionKeyOptions & { format?: 'armored' }): Promise; export function encryptSessionKey(options: EncryptSessionKeyOptions & { format: 'binary' }): Promise; export function encryptSessionKey(options: EncryptSessionKeyOptions & { format: 'object' }): Promise>; -export function decryptSessionKeys>(options: { message: Message, decryptionKeys?: MaybeArray, passwords?: MaybeArray, date?: Date, config?: PartialConfig }): Promise; +export function decryptSessionKeys>(options: { message: Message, decryptionKeys?: MaybeArray, passwords?: MaybeArray, date?: Date, config?: PartialConfig }): Promise; export function readMessage>(options: { armoredMessage: T, config?: PartialConfig }): Promise>; export function readMessage>(options: { binaryMessage: T, config?: PartialConfig }): Promise>; @@ -190,25 +202,25 @@ export function createMessage>(options: { text: T, export function createMessage>(options: { binary: T, filename?: string, date?: Date, format?: enums.literalFormatNames }): Promise>; export function encrypt>(options: EncryptOptions & { message: Message, format?: 'armored' }): Promise< - T extends WebStream ? WebStream : - T extends NodeStream ? NodeStream : + T extends WebStream ? WebStream : + T extends NodeWebStream ? NodeWebStream : string >; export function encrypt>(options: EncryptOptions & { message: Message, format: 'binary' }): Promise< - T extends WebStream ? WebStream : - T extends NodeStream ? NodeStream : + T extends WebStream ? WebStream : + T extends NodeWebStream ? NodeWebStream : Uint8Array >; export function encrypt>(options: EncryptOptions & { message: Message, format: 'object' }): Promise>; export function sign>(options: SignOptions & { message: Message, format?: 'armored' }): Promise< - T extends WebStream ? WebStream : - T extends NodeStream ? NodeStream : + T extends WebStream ? WebStream : + T extends NodeWebStream ? NodeWebStream : string >; export function sign>(options: SignOptions & { message: Message, format: 'binary' }): Promise< - T extends WebStream ? WebStream : - T extends NodeStream ? NodeStream : + T extends WebStream ? WebStream : + T extends NodeWebStream ? NodeWebStream : Uint8Array >; export function sign>(options: SignOptions & { message: Message, format: 'object' }): Promise>; @@ -217,26 +229,26 @@ export function sign(options: SignOptions & { message: CleartextMessage, format: export function decrypt>(options: DecryptOptions & { message: Message, format: 'binary' }): Promise ? WebStream : - T extends NodeStream ? NodeStream : + T extends WebStream ? WebStream : + T extends NodeWebStream ? NodeWebStream : Uint8Array }>; export function decrypt>(options: DecryptOptions & { message: Message }): Promise ? WebStream : - T extends NodeStream ? NodeStream : + T extends WebStream ? WebStream : + T extends NodeWebStream ? NodeWebStream : string }>; export function verify(options: VerifyOptions & { message: CleartextMessage, format?: 'utf8' }): Promise>; export function verify>(options: VerifyOptions & { message: Message, format: 'binary' }): Promise ? WebStream : - T extends NodeStream ? NodeStream : + T extends WebStream ? WebStream : + T extends NodeWebStream ? NodeWebStream : Uint8Array >>; export function verify>(options: VerifyOptions & { message: Message }): Promise ? WebStream : - T extends NodeStream ? NodeStream : + T extends WebStream ? WebStream : + T extends NodeWebStream ? NodeWebStream : string >>; @@ -263,7 +275,7 @@ export class Message> { /** Encrypt the message @param encryptionKeys array of public keys, used to encrypt the message */ - public encrypt(encryptionKeys?: PublicKey[], passwords?: string[], sessionKeys?: SessionKey[], wildcard?: boolean, encryptionKeyIDs?: KeyID[], date?: Date, userIDs?: UserID[], config?: Config): Promise>>; + public encrypt(encryptionKeys?: PublicKey[], passwords?: string[], sessionKeys?: SessionKey[], wildcard?: boolean, encryptionKeyIDs?: KeyID[], date?: Date, userIDs?: UserID[], config?: Config): Promise>>; /** Returns the key IDs of the keys to which the session key is encrypted */ @@ -305,7 +317,7 @@ export class Message> { } -/* ############## v5 CONFIG #################### */ +/* ############## CONFIG #################### */ interface Config { preferredHashAlgorithm: enums.hash; @@ -313,44 +325,44 @@ interface Config { preferredCompressionAlgorithm: enums.compression; showVersion: boolean; showComment: boolean; - deflateLevel: number; aeadProtect: boolean; allowUnauthenticatedMessages: boolean; allowUnauthenticatedStream: boolean; - checksumRequired: boolean; minRSABits: number; passwordCollisionCheck: boolean; - revocationsExpire: boolean; ignoreUnsupportedPackets: boolean; ignoreMalformedPackets: boolean; versionString: string; commentString: string; allowInsecureDecryptionWithSigningKeys: boolean; allowInsecureVerificationWithReformattedKeys: boolean; + allowMissingKeyFlags: boolean; constantTimePKCS1Decryption: boolean; constantTimePKCS1DecryptionSupportedSymmetricAlgorithms: Set; - v5Keys: boolean; + v6Keys: boolean; + enableParsingV5Entities: boolean; preferredAEADAlgorithm: enums.aead; aeadChunkSizeByte: number; + s2kType: enums.s2k.iterated | enums.s2k.argon2; s2kIterationCountByte: number; - minBytesForWebCrypto: number; + s2kArgon2Params: { passes: number, parallelism: number; memoryExponent: number; }; maxUserIDLength: number; knownNotations: string[]; - useIndutnyElliptic: boolean; + useEllipticFallback: boolean; rejectHashAlgorithms: Set; rejectMessageHashAlgorithms: Set; rejectPublicKeyAlgorithms: Set; rejectCurves: Set; } -export var config: Config; +export const config: Config; // PartialConfig has the same properties as Config, but declared as optional. // This interface is relevant for top-level functions, which accept a subset of configuration options -interface PartialConfig extends Partial {} +export interface PartialConfig extends Partial {} -/* ############## v5 PACKET #################### */ +/* ############## PACKET #################### */ -declare abstract class BasePacket { +export declare abstract class BasePacket { static readonly tag: enums.packet; public read(bytes: Uint8Array): void; public write(): Uint8Array; @@ -423,7 +435,7 @@ export class AEADEncryptedDataPacket extends BasePacket { static readonly tag: enums.packet.aeadEncryptedData; private decrypt(sessionKeyAlgorithm: enums.symmetric, sessionKey: Uint8Array, config?: Config): void; private encrypt(sessionKeyAlgorithm: enums.symmetric, sessionKey: Uint8Array, config?: Config): void; - private crypt(fn: Function, sessionKey: Uint8Array, data: MaybeStream): MaybeStream + private crypt(fn: Function, sessionKey: Uint8Array, data: MaybeStream): MaybeStream; } export class PublicKeyEncryptedSessionKeyPacket extends BasePacket { @@ -486,7 +498,8 @@ export class SignaturePacket extends BasePacket { public hashAlgorithm: enums.hash | null; public publicKeyAlgorithm: enums.publicKey | null; public signatureData: null | Uint8Array; - public unhashedSubpackets: null | Uint8Array; + public unhashedSubpackets: RawSubpacket[]; + public unknownSubpackets: RawSubpacket[]; public signedHashValue: null | Uint8Array; public created: Date | null; public signatureExpirationTime: null | number; @@ -530,6 +543,12 @@ export class SignaturePacket extends BasePacket { public getExpirationTime(): Date | typeof Infinity; } +export interface RawSubpacket { + type: number; + critical: boolean; + body: Uint8Array; +} + export interface RawNotation { name: string; value: Uint8Array; @@ -560,16 +579,7 @@ export class PacketList extends Array { public findPacket(tag: enums.packet): T | undefined; } -/* ############## v5 STREAM #################### */ - -type Data = Uint8Array | string; -export interface WebStream extends GenericWebStream {} -export interface NodeStream extends GenericNodeStream {} -export type Stream = WebStream | NodeStream; -export type MaybeStream = T | Stream; - -/* ############## v5 GENERAL #################### */ -type MaybeArray = T | Array; +/* ############## GENERAL #################### */ export interface UserID { name?: string; email?: string; comment?: string; } export interface SessionKey { @@ -578,9 +588,14 @@ export interface SessionKey { aeadAlgorithm?: enums.aeadNames; } +export interface DecryptedSessionKey { + data: Uint8Array; + algorithm: enums.symmetricNames | null; // `null` if the session key is associated with a SEIPDv2 packet +} + export interface ReasonForRevocation { flag?: enums.reasonForRevocation, string?: string } -interface EncryptOptions { +export interface EncryptOptions { /** message to be encrypted as created by createMessage */ message: Message>; /** (optional) array of keys or single key, used to encrypt the message */ @@ -612,7 +627,7 @@ interface EncryptOptions { config?: PartialConfig; } -interface DecryptOptions { +export interface DecryptOptions { /** the message object with the encrypted data */ message: Message>; /** (optional) private keys with decrypted secret key data or session key */ @@ -634,7 +649,7 @@ interface DecryptOptions { config?: PartialConfig; } -interface SignOptions { +export interface SignOptions { message: CleartextMessage | Message>; signingKeys: MaybeArray; format?: 'armored' | 'binary' | 'object'; @@ -646,7 +661,7 @@ interface SignOptions { config?: PartialConfig; } -interface VerifyOptions { +export interface VerifyOptions { /** (cleartext) message object with signatures */ message: CleartextMessage | Message>; /** array of publicKeys or single key, to verify signatures */ @@ -662,7 +677,7 @@ interface VerifyOptions { config?: PartialConfig; } -interface EncryptSessionKeyOptions extends SessionKey { +export interface EncryptSessionKeyOptions extends SessionKey { encryptionKeys?: MaybeArray, passwords?: MaybeArray, format?: 'armored' | 'binary' | 'object', @@ -673,7 +688,7 @@ interface EncryptSessionKeyOptions extends SessionKey { config?: PartialConfig } -interface SerializedKeyPair { +interface SerializedKeyPair { privateKey: T; publicKey: T; } @@ -682,7 +697,7 @@ interface KeyPair { publicKey: PublicKey; } -export type EllipticCurveName = 'ed25519' | 'curve25519' | 'p256' | 'p384' | 'p521' | 'secp256k1' | 'brainpoolP256r1' | 'brainpoolP384r1' | 'brainpoolP512r1'; +export type EllipticCurveName = 'ed25519Legacy' | 'curve25519Legacy' | 'nistP256' | 'nistP384' | 'nistP521' | 'secp256k1' | 'brainpoolP256r1' | 'brainpoolP384r1' | 'brainpoolP512r1'; interface GenerateKeyOptions { userIDs: MaybeArray; @@ -698,7 +713,7 @@ interface GenerateKeyOptions { } export type KeyOptions = GenerateKeyOptions; -interface SubkeyOptions { +export interface SubkeyOptions { type?: 'ecc' | 'rsa'; curve?: EllipticCurveName; rsaBits?: number; @@ -708,20 +723,20 @@ interface SubkeyOptions { config?: PartialConfig; } -declare class KeyID { +export declare class KeyID { bytes: string; equals(keyID: KeyID, matchWildcard?: boolean): boolean; toHex(): string; static fromID(hex: string): KeyID; } -interface DecryptMessageResult { +export interface DecryptMessageResult { data: MaybeStream; signatures: VerificationResult[]; filename: string; } -interface VerifyMessageResult = MaybeStream> { +export interface VerifyMessageResult = MaybeStream> { data: T; signatures: VerificationResult[]; } @@ -730,7 +745,7 @@ interface VerifyMessageResult = MaybeStream> { /** * Armor an OpenPGP binary packet block */ -export function armor(messagetype: enums.armor, body: object, partindex?: number, parttotal?: number, customComment?: string, config?: Config): string; +export function armor(messagetype: enums.armor, body: object, partindex?: number, parttotal?: number, customComment?: string, emitChecksum?: boolean, config?: Config): string; /** * DeArmor an OpenPGP armored message; verify the checksum and return the encoded bytes @@ -740,44 +755,44 @@ export function unarmor(input: string, config?: Config): Promise<{ text: string, /* ############## v5 ENUMS #################### */ export namespace enums { - function read(type: typeof armor, e: armor): armorNames; - function read(type: typeof compression, e: compression): compressionNames; - function read(type: typeof hash, e: hash): hashNames; - function read(type: typeof packet, e: packet): packetNames; - function read(type: typeof publicKey, e: publicKey): publicKeyNames; - function read(type: typeof symmetric, e: symmetric): symmetricNames; - function read(type: typeof keyStatus, e: keyStatus): keyStatusNames; - function read(type: typeof keyFlags, e: keyFlags): keyFlagsNames; + export function read(type: typeof armor, e: armor): armorNames; + export function read(type: typeof compression, e: compression): compressionNames; + export function read(type: typeof hash, e: hash): hashNames; + export function read(type: typeof packet, e: packet): packetNames; + export function read(type: typeof publicKey, e: publicKey): publicKeyNames; + export function read(type: typeof symmetric, e: symmetric): symmetricNames; + export function read(type: typeof keyStatus, e: keyStatus): keyStatusNames; + export function read(type: typeof keyFlags, e: keyFlags): keyFlagsNames; export type armorNames = 'multipartSection' | 'multipartLast' | 'signed' | 'message' | 'publicKey' | 'privateKey'; - enum armor { + export enum armor { multipartSection = 0, multipartLast = 1, signed = 2, message = 3, publicKey = 4, privateKey = 5, - signature = 6, + signature = 6 } - enum reasonForRevocation { + export enum reasonForRevocation { noReason = 0, // No reason specified (key revocations or cert revocations) keySuperseded = 1, // Key is superseded (key revocations) keyCompromised = 2, // Key material has been compromised (key revocations) keyRetired = 3, // Key is retired and no longer used (key revocations) - userIDInvalid = 32, // User ID information is no longer valid (cert revocations) + userIDInvalid = 32 // User ID information is no longer valid (cert revocations) } export type compressionNames = 'uncompressed' | 'zip' | 'zlib' | 'bzip2'; - enum compression { + export enum compression { uncompressed = 0, zip = 1, zlib = 2, - bzip2 = 3, + bzip2 = 3 } - export type hashNames = 'md5' | 'sha1' | 'ripemd' | 'sha256' | 'sha384' | 'sha512' | 'sha224'; - enum hash { + export type hashNames = 'md5' | 'sha1' | 'ripemd' | 'sha256' | 'sha384' | 'sha512' | 'sha224' | 'sha3_256' | 'sha3_512'; + export enum hash { md5 = 1, sha1 = 2, ripemd = 3, @@ -785,12 +800,14 @@ export namespace enums { sha384 = 9, sha512 = 10, sha224 = 11, + sha3_256 = 12, + sha3_512 = 14 } - export type packetNames = 'publicKeyEncryptedSessionKey' | 'signature' | 'symEncryptedSessionKey' | 'onePassSignature' | 'secretKey' | 'publicKey' - | 'secretSubkey' | 'compressed' | 'symmetricallyEncrypted' | 'marker' | 'literal' | 'trust' | 'userID' | 'publicSubkey' | 'userAttribute' - | 'symEncryptedIntegrityProtected' | 'modificationDetectionCode' | 'AEADEncryptedDataPacket'; - enum packet { + export type packetNames = 'publicKeyEncryptedSessionKey' | 'signature' | 'symEncryptedSessionKey' | 'onePassSignature' | 'secretKey' | 'publicKey' | + 'secretSubkey' | 'compressed' | 'symmetricallyEncrypted' | 'marker' | 'literal' | 'trust' | 'userID' | 'publicSubkey' | 'userAttribute' | + 'symEncryptedIntegrityProtected' | 'modificationDetectionCode' | 'AEADEncryptedDataPacket'; + export enum packet { publicKeyEncryptedSessionKey = 1, signature = 2, symEncryptedSessionKey = 3, @@ -808,11 +825,11 @@ export namespace enums { userAttribute = 17, symEncryptedIntegrityProtectedData = 18, modificationDetectionCode = 19, - aeadEncryptedData = 20, + aeadEncryptedData = 20 } - export type publicKeyNames = 'rsaEncryptSign' | 'rsaEncrypt' | 'rsaSign' | 'elgamal' | 'dsa' | 'ecdh' | 'ecdsa' | 'eddsa' | 'aedh' | 'aedsa'; - enum publicKey { + export type publicKeyNames = 'rsaEncryptSign' | 'rsaEncrypt' | 'rsaSign' | 'elgamal' | 'dsa' | 'ecdh' | 'ecdsa' | 'eddsaLegacy' | 'aedh' | 'aedsa' | 'ed25519' | 'x25519' | 'ed448' | 'x448'; + export enum publicKey { rsaEncryptSign = 1, rsaEncrypt = 2, rsaSign = 3, @@ -820,32 +837,39 @@ export namespace enums { dsa = 17, ecdh = 18, ecdsa = 19, - /** @deprecated use `eddsaLegacy` instead */ - eddsa = 22, eddsaLegacy = 22, aedh = 23, aedsa = 24, + x25519 = 25, + x448 = 26, + ed25519 = 27, + ed448 = 28 } - enum curve { - p256 = 'p256', - p384 = 'p384', - p521 = 'p521', + export enum curve { + /** @deprecated use `nistP256` instead */ + p256 = 'nistP256', + nistP256 = 'nistP256', + /** @deprecated use `nistP384` instead */ + p384 = 'nistP384', + nistP384 = 'nistP384', + /** @deprecated use `nistP521` instead */ + p521 = 'nistP521', + nistP521 = 'nistP521', /** @deprecated use `ed25519Legacy` instead */ - ed25519 = 'ed25519', - ed25519Legacy = 'ed25519', + ed25519 = 'ed25519Legacy', + ed25519Legacy = 'ed25519Legacy', /** @deprecated use `curve25519Legacy` instead */ - curve25519 = 'curve25519', - curve25519Legacy = 'curve25519', + curve25519 = 'curve25519Legacy', + curve25519Legacy = 'curve25519Legacy', secp256k1 = 'secp256k1', brainpoolP256r1 = 'brainpoolP256r1', brainpoolP384r1 = 'brainpoolP384r1', brainpoolP512r1 = 'brainpoolP512r1' } - export type symmetricNames = 'plaintext' | 'idea' | 'tripledes' | 'cast5' | 'blowfish' | 'aes128' | 'aes192' | 'aes256' | 'twofish'; - enum symmetric { - plaintext = 0, + export type symmetricNames = 'idea' | 'tripledes' | 'cast5' | 'blowfish' | 'aes128' | 'aes192' | 'aes256' | 'twofish'; + export enum symmetric { idea = 1, tripledes = 2, cast5 = 3, @@ -853,31 +877,30 @@ export namespace enums { aes128 = 7, aes192 = 8, aes256 = 9, - twofish = 10, + twofish = 10 } export type keyStatusNames = 'invalid' | 'expired' | 'revoked' | 'valid' | 'noSelfCert'; - enum keyStatus { + export enum keyStatus { invalid = 0, expired = 1, revoked = 2, valid = 3, - noSelfCert = 4, + noSelfCert = 4 } - export type keyFlagsNames = 'certifyKeys' | 'signData' | 'encryptCommunication' | 'encryptStorage' | 'splitPrivateKey' | 'authentication' - | 'sharedPrivateKey'; - enum keyFlags { + export type keyFlagsNames = 'certifyKeys' | 'signData' | 'encryptCommunication' | 'encryptStorage' | 'splitPrivateKey' | 'authentication' | 'sharedPrivateKey'; + export enum keyFlags { certifyKeys = 1, signData = 2, encryptCommunication = 4, encryptStorage = 8, splitPrivateKey = 16, authentication = 32, - sharedPrivateKey = 128, + sharedPrivateKey = 128 } - enum signature { + export enum signature { binary = 0, text = 1, standalone = 2, @@ -896,17 +919,25 @@ export namespace enums { } export type aeadNames = 'eax' | 'ocb' | 'gcm'; - enum aead { + export enum aead { eax = 1, ocb = 2, experimentalGCM = 100 // Private algorithm } - export type literalFormatNames = 'utf8' | 'binary' | 'text' | 'mime' - enum literal { + export type literalFormatNames = 'utf8' | 'binary' | 'text' | 'mime'; + export enum literal { binary = 98, text = 116, utf8 = 117, mime = 109 } + + export enum s2k { + simple = 0, + salted = 1, + iterated = 3, + argon2 = 4, + gnu = 101 + } } diff --git a/package-lock.json b/package-lock.json index f4a487c6..ecfdc9f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,200 +1,106 @@ { "name": "openpgp", - "version": "5.11.2", - "lockfileVersion": 2, + "version": "6.0.0-beta.3.patch.1", + "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "openpgp", - "version": "5.11.2", + "version": "6.0.0-beta.3.patch.1", "license": "LGPL-3.0+", - "dependencies": { - "asn1.js": "^5.0.0" - }, "devDependencies": { - "@openpgp/asmcrypto.js": "^2.3.2", - "@openpgp/elliptic": "^6.5.1", - "@openpgp/jsdoc": "^3.6.4", - "@openpgp/pako": "^1.0.12", + "@noble/ciphers": "^1.0.0", + "@noble/curves": "^1.6.0", + "@noble/hashes": "^1.5.0", + "@openpgp/jsdoc": "^3.6.11", "@openpgp/seek-bzip": "^1.0.5-git", - "@openpgp/tweetnacl": "^1.0.3", - "@openpgp/web-stream-tools": "0.0.11-patch-1", - "@rollup/plugin-commonjs": "^11.1.0", - "@rollup/plugin-node-resolve": "^7.1.3", - "@rollup/plugin-replace": "^2.3.2", - "@types/chai": "^4.2.14", + "@openpgp/tweetnacl": "^1.0.4-1", + "@openpgp/web-stream-tools": "~0.1.3", + "@rollup/plugin-alias": "^5.1.1", + "@rollup/plugin-commonjs": "^25.0.8", + "@rollup/plugin-node-resolve": "^15.3.0", + "@rollup/plugin-replace": "^5.0.7", + "@rollup/plugin-terser": "^0.4.4", + "@rollup/plugin-typescript": "^11.1.6", + "@rollup/plugin-wasm": "^6.2.2", + "@types/chai": "^4.3.19", + "@types/sinon": "^17.0.3", + "@typescript-eslint/parser": "^7.18.0", + "@web/test-runner": "^0.19.0", + "@web/test-runner-browserstack": "^0.7.2", + "@web/test-runner-mocha": "^0.9.0", + "@web/test-runner-playwright": "^0.11.0", + "argon2id": "^1.0.1", "benchmark": "^2.1.4", - "bn.js": "^4.11.8", - "chai": "^4.3.6", - "chai-as-promised": "^7.1.1", - "email-addresses": "3.1.0", - "eslint": "^8.34.0", + "bn.js": "^5.2.1", + "c8": "^8.0.1", + "chai": "^4.4.1", + "chai-as-promised": "^7.1.2", + "eckey-utils": "^0.7.14", + "eslint": "^8.57.1", "eslint-config-airbnb": "^19.0.4", "eslint-config-airbnb-base": "^15.0.0", - "eslint-plugin-chai-friendly": "^0.7.2", - "eslint-plugin-import": "^2.27.5", - "esm": "^3.2.25", - "hash.js": "^1.1.3", - "http-server": "^14.1.1", - "karma": "^6.4.0", - "karma-browserstack-launcher": "^1.6.0", - "karma-chrome-launcher": "^3.1.1", - "karma-firefox-launcher": "^2.1.2", - "karma-mocha": "^2.0.1", - "karma-mocha-reporter": "^2.2.5", - "karma-webkit-launcher": "^2.1.0", - "mocha": "^8.4.0", - "nyc": "^14.1.1", - "playwright": "^1.30.0", - "rollup": "^2.38.5", - "rollup-plugin-terser": "^7.0.2", - "sinon": "^4.3.0", - "typescript": "^4.1.2", - "web-streams-polyfill": "^3.2.0" + "eslint-config-airbnb-typescript": "^18.0.0", + "eslint-import-resolver-typescript": "^3.6.3", + "eslint-plugin-chai-friendly": "^0.7.4", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-unicorn": "^48.0.1", + "fflate": "^0.7.4", + "mocha": "^10.7.3", + "playwright": "^1.48.2", + "rollup": "^4.24.2", + "sinon": "^18.0.1", + "ts-node": "^10.9.2", + "tslib": "^2.8.0", + "tsx": "^4.19.2", + "typescript": "^5.6.3", + "web-streams-polyfill": "^4.0.0" }, "engines": { - "node": ">= 8.0.0" + "node": ">= 18.0.0" } }, "node_modules/@babel/code-frame": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", - "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", + "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", "dev": true, "dependencies": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/code-frame/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/generator": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", - "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.23.6", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dev": true, - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" + "@babel/highlight": "^7.24.7", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", - "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", + "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", + "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", - "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", + "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-validator-identifier": "^7.24.7", "chalk": "^2.4.2", - "js-tokens": "^4.0.0" + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" @@ -226,6 +132,39 @@ "node": ">=4" } }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/highlight/node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -239,10 +178,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.9.tgz", - "integrity": "sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz", + "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==", "dev": true, + "dependencies": { + "@babel/types": "^7.25.6" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -250,123 +192,465 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/runtime": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.4.tgz", - "integrity": "sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA==", - "dev": true, - "peer": true, - "dependencies": { - "regenerator-runtime": "^0.13.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/runtime-corejs3": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.19.4.tgz", - "integrity": "sha512-HzjQ8+dzdx7dmZy4DQ8KV8aHi/74AjEbBGTFutBmg/pd3dY5/q1sfuOGPTFGEytlQhWoeVXqcK5BwMgIkRkNDQ==", - "dev": true, - "peer": true, - "dependencies": { - "core-js-pure": "^3.25.1", - "regenerator-runtime": "^0.13.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.23.9.tgz", - "integrity": "sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/parser": "^7.23.9", - "@babel/types": "^7.23.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.9.tgz", - "integrity": "sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.9", - "@babel/types": "^7.23.9", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@babel/traverse/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/@babel/types": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz", - "integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz", + "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-string-parser": "^7.24.8", + "@babel/helper-validator-identifier": "^7.24.7", "to-fast-properties": "^2.0.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", + "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", "dev": true, "engines": { - "node": ">=0.1.90" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", - "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.4.0", + "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -381,54 +665,14 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@eslint/eslintrc/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/@eslint/eslintrc/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "node_modules/@eslint/eslintrc/node_modules/minimatch": { @@ -443,41 +687,48 @@ "node": "*" } }, - "node_modules/@eslint/eslintrc/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@hapi/bourne": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-3.0.0.tgz", + "integrity": "sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w==", + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", "minimatch": "^3.0.5" }, "engines": { "node": ">=10.10.0" } }, - "node_modules/@humanwhocodes/config-array/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { @@ -485,6 +736,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -492,12 +744,6 @@ "node": "*" } }, - "node_modules/@humanwhocodes/config-array/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -512,76 +758,214 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", "dev": true, "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@mattiasbuelens/web-streams-adapter": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@mattiasbuelens/web-streams-adapter/-/web-streams-adapter-0.1.0.tgz", - "integrity": "sha512-oV4PyZfwJNtmFWhvlJLqYIX1Nn22ML8FZpS16ZUKv0hg7414xV1fjsGqxQzLT2dyK92TKxsJSwMOd7VNHAtPmA==", + "node_modules/@noble/ciphers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.0.0.tgz", + "integrity": "sha512-wH5EHOmLi0rEazphPbecAzmjd12I6/Yv/SiHdkA9LSycsQk7RuuTp7am5/o62qYr0RScE7Pc9icXGBbsr6cesA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.6.0.tgz", + "integrity": "sha512-TlaHRXDehJuRNR9TfZDNQ45mMEd5dwUwmicsafcIX4SsNiqnCHKjE/1alYPd/lDRVhxdhUAlv8uEhMCI5zjIJQ==", + "dev": true, + "dependencies": { + "@noble/hashes": "1.5.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.5.0.tgz", + "integrity": "sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==", "dev": true, "engines": { - "node": ">= 12" + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, "node_modules/@nodelib/fs.scandir": { @@ -619,25 +1003,13 @@ "node": ">= 8" } }, - "node_modules/@openpgp/asmcrypto.js": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@openpgp/asmcrypto.js/-/asmcrypto.js-2.3.2.tgz", - "integrity": "sha512-CEb3I/Tqg+i5NgEnhYj3fi6XsT5JTuvYdwbMq+STGxlZ8uYSWmYFmVyz9vQgtNwCll/FbB6eR1opa4hoeHGceQ==", - "dev": true - }, - "node_modules/@openpgp/elliptic": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@openpgp/elliptic/-/elliptic-6.5.1.tgz", - "integrity": "sha512-VR20QWndMXoZTAzCUqauDT4dLrHO4RTnyVV3szuRHllQSU/JZToLvWtFxpEQth4XWyqlxHPwq7tljE5V97+n1g==", + "node_modules/@nolyfill/is-core-module": { + "version": "1.0.39", + "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", + "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", "dev": true, - "dependencies": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" + "engines": { + "node": ">=12.4.0" } }, "node_modules/@openpgp/jsdoc": { @@ -669,33 +1041,6 @@ "node": ">=12.0.0" } }, - "node_modules/@openpgp/jsdoc/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@openpgp/jsdoc/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@openpgp/pako": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@openpgp/pako/-/pako-1.0.12.tgz", - "integrity": "sha512-r3+UotSXn4j2snQIuIYjsvA9xzx/5hEPi4vAqHhjhtQ+Q/XyLxYwfJpeIAdJvud6dKp57h48lDQpMkGda4MBQw==", - "dev": true - }, "node_modules/@openpgp/seek-bzip": { "version": "1.0.5-git", "resolved": "https://registry.npmjs.org/@openpgp/seek-bzip/-/seek-bzip-1.0.5-git.tgz", @@ -710,196 +1055,843 @@ } }, "node_modules/@openpgp/tweetnacl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@openpgp/tweetnacl/-/tweetnacl-1.0.3.tgz", - "integrity": "sha512-KGXNhU/mRg+uTsLGva55V340jwbX2pC8LndjOVI2oQ8vewPVTS2KnDOIXQ8O6KyT/c9Qy16KUQ5mwewe72m1Yw==", + "version": "1.0.4-1", + "resolved": "https://registry.npmjs.org/@openpgp/tweetnacl/-/tweetnacl-1.0.4-1.tgz", + "integrity": "sha512-coYo04Op1+g4h6yE6q0GglGdvWkdfvpQWKmR9nDIrW+LqdTtwHFXIyIQGs5cosR4tCajxRn9aF/+WK207zxFrg==", "dev": true }, "node_modules/@openpgp/web-stream-tools": { - "version": "0.0.11-patch-1", - "resolved": "https://registry.npmjs.org/@openpgp/web-stream-tools/-/web-stream-tools-0.0.11-patch-1.tgz", - "integrity": "sha512-sZkx4FsHGFPcGrEBmBLvg0PcFBgR7KWe+NXo3SI/e+gpVoK3rPzPgv4TpI3UFKiXrohaJyY/klf24tNbJCutBA==", - "dev": true, - "dependencies": { - "@mattiasbuelens/web-streams-adapter": "~0.1.0", - "web-streams-polyfill": "~3.0.3" - } - }, - "node_modules/@openpgp/web-stream-tools/node_modules/web-streams-polyfill": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.0.3.tgz", - "integrity": "sha512-d2H/t0eqRNM4w2WvmTdoeIvzAUSpK7JmATB8Nr2lb7nQ9BTIJVjbQ/TRFVEh2gUH1HwclPdoPtfMoFfetXaZnA==", + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@openpgp/web-stream-tools/-/web-stream-tools-0.1.3.tgz", + "integrity": "sha512-mT/ds43cH6c+AO5RFpxs+LkACr7KjC3/dZWHrP6KPrWJu4uJ/XJ+p7telaoYiqUfdjiiIvdNSOfhezW9fkmboQ==", "dev": true, "engines": { - "node": ">= 8" + "node": ">= 18.0.0" + }, + "peerDependencies": { + "typescript": ">=4.2" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@promptbook/utils": { + "version": "0.69.5", + "resolved": "https://registry.npmjs.org/@promptbook/utils/-/utils-0.69.5.tgz", + "integrity": "sha512-xm5Ti/Hp3o4xHrsK9Yy3MS6KbDxYbq485hDsFvxqaNA7equHLPdo8H8faTitTeb14QCDfLW4iwCxdVYu5sn6YQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://buymeacoffee.com/hejny" + }, + { + "type": "github", + "url": "https://github.com/webgptorg/promptbook/blob/main/README.md#%EF%B8%8F-contributing" + } + ], + "license": "CC-BY-4.0", + "dependencies": { + "spacetrim": "0.11.59" + } + }, + "node_modules/@puppeteer/browsers": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.4.0.tgz", + "integrity": "sha512-x8J1csfIygOwf6D6qUAZ0ASk3z63zPb7wkNeHRerCMh82qWKUrOgkuP005AJC8lDL6/evtXETGEJVcwykKT4/g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.3.6", + "extract-zip": "^2.0.1", + "progress": "^2.0.3", + "proxy-agent": "^6.4.0", + "semver": "^7.6.3", + "tar-fs": "^3.0.6", + "unbzip2-stream": "^1.4.3", + "yargs": "^17.7.2" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@rollup/plugin-alias": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-alias/-/plugin-alias-5.1.1.tgz", + "integrity": "sha512-PR9zDb+rOzkRb2VD+EuKB7UC41vU5DIwZ5qqCpk0KJudcWAyi8rvYOhS7+L5aZCspw1stTViLgN5v6FF1p5cgQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } } }, "node_modules/@rollup/plugin-commonjs": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-11.1.0.tgz", - "integrity": "sha512-Ycr12N3ZPN96Fw2STurD21jMqzKwL9QuFhms3SD7KKRK7oaXUsBU9Zt0jL/rOPHiPYisI21/rXGO3jr9BnLHUA==", + "version": "25.0.8", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.8.tgz", + "integrity": "sha512-ZEZWTK5n6Qde0to4vS9Mr5x/0UZoqCxPVR9KRUjU4kA2sO7GEUn1fop0DAwpO6z0Nw/kJON9bDmSxdWxO/TT1A==", "dev": true, "dependencies": { - "@rollup/pluginutils": "^3.0.8", + "@rollup/pluginutils": "^5.0.1", "commondir": "^1.0.1", - "estree-walker": "^1.0.1", - "glob": "^7.1.2", - "is-reference": "^1.1.2", - "magic-string": "^0.25.2", - "resolve": "^1.11.0" + "estree-walker": "^2.0.2", + "glob": "^8.0.3", + "is-reference": "1.2.1", + "magic-string": "^0.30.3" }, "engines": { - "node": ">= 8.0.0" + "node": ">=14.0.0" }, "peerDependencies": { - "rollup": "^1.20.0||^2.0.0" + "rollup": "^2.68.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } } }, "node_modules/@rollup/plugin-node-resolve": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-7.1.3.tgz", - "integrity": "sha512-RxtSL3XmdTAE2byxekYLnx+98kEUOrPHF/KRVjLH+DEIHy6kjIw7YINQzn+NXiH/NTrQLAwYs0GWB+csWygA9Q==", + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.0.tgz", + "integrity": "sha512-9eO5McEICxMzJpDW9OnMYSv4Sta3hmt7VtBFz5zR9273suNOydOyq/FrGeGy+KsTRFm8w0SLVhzig2ILFT63Ag==", "dev": true, + "license": "MIT", "dependencies": { - "@rollup/pluginutils": "^3.0.8", - "@types/resolve": "0.0.8", - "builtin-modules": "^3.1.0", + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", "is-module": "^1.0.0", - "resolve": "^1.14.2" + "resolve": "^1.22.1" }, "engines": { - "node": ">= 8.0.0" + "node": ">=14.0.0" }, "peerDependencies": { - "rollup": "^1.20.0||^2.0.0" - } - }, - "node_modules/@rollup/plugin-node-resolve/node_modules/builtin-modules": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz", - "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==", - "dev": true, - "engines": { - "node": ">=6" + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } } }, "node_modules/@rollup/plugin-replace": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.3.2.tgz", - "integrity": "sha512-KEEL7V2tMNOsbAoNMKg91l1sNXBDoiP31GFlqXVOuV5691VQKzKBh91+OKKOG4uQWYqcFskcjFyh1d5YnZd0Zw==", + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-5.0.7.tgz", + "integrity": "sha512-PqxSfuorkHz/SPpyngLyg5GCEkOcee9M1bkxiVDr41Pd61mqP1PLOoDPbpl44SB2mQGKwV/In74gqQmGITOhEQ==", "dev": true, "dependencies": { - "@rollup/pluginutils": "^3.0.8", - "magic-string": "^0.25.5" + "@rollup/pluginutils": "^5.0.1", + "magic-string": "^0.30.3" + }, + "engines": { + "node": ">=14.0.0" }, "peerDependencies": { - "rollup": "^1.20.0 || ^2.0.0" + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-terser": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz", + "integrity": "sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==", + "dev": true, + "dependencies": { + "serialize-javascript": "^6.0.1", + "smob": "^1.0.0", + "terser": "^5.17.4" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-typescript": { + "version": "11.1.6", + "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-11.1.6.tgz", + "integrity": "sha512-R92yOmIACgYdJ7dJ97p4K69I8gg6IEHt8M7dUBxN3W6nrO8uUxX5ixl0yU/N3aZTi8WhPuICvOHXQvF6FaykAA==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.1.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.14.0||^3.0.0||^4.0.0", + "tslib": "*", + "typescript": ">=3.7.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + }, + "tslib": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-wasm": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-wasm/-/plugin-wasm-6.2.2.tgz", + "integrity": "sha512-gpC4R1G9Ni92ZIRTexqbhX7U+9estZrbhP+9SRb0DW9xpB9g7j34r+J2hqrcW/lRI7dJaU84MxZM0Rt82tqYPQ==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } } }, "node_modules/@rollup/pluginutils": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.0.9.tgz", - "integrity": "sha512-TLZavlfPAZYI7v33wQh4mTP6zojne14yok3DNSLcjoG/Hirxfkonn6icP5rrNWRn8nZsirJBFFpijVOJzkUHDg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", + "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", "dev": true, "dependencies": { - "@types/estree": "0.0.39", - "estree-walker": "^1.0.1", - "micromatch": "^4.0.2" + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" }, "engines": { - "node": ">= 8.0.0" + "node": ">=14.0.0" }, "peerDependencies": { - "rollup": "^1.20.0||^2.0.0" + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.2.tgz", + "integrity": "sha512-ufoveNTKDg9t/b7nqI3lwbCG/9IJMhADBNjjz/Jn6LxIZxD7T5L8l2uO/wD99945F1Oo8FvgbbZJRguyk/BdzA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.2.tgz", + "integrity": "sha512-iZoYCiJz3Uek4NI0J06/ZxUgwAfNzqltK0MptPDO4OR0a88R4h0DSELMsflS6ibMCJ4PnLvq8f7O1d7WexUvIA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.2.tgz", + "integrity": "sha512-/UhrIxobHYCBfhi5paTkUDQ0w+jckjRZDZ1kcBL132WeHZQ6+S5v9jQPVGLVrLbNUebdIRpIt00lQ+4Z7ys4Rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.2.tgz", + "integrity": "sha512-1F/jrfhxJtWILusgx63WeTvGTwE4vmsT9+e/z7cZLKU8sBMddwqw3UV5ERfOV+H1FuRK3YREZ46J4Gy0aP3qDA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.24.2.tgz", + "integrity": "sha512-1YWOpFcGuC6iGAS4EI+o3BV2/6S0H+m9kFOIlyFtp4xIX5rjSnL3AwbTBxROX0c8yWtiWM7ZI6mEPTI7VkSpZw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.24.2.tgz", + "integrity": "sha512-3qAqTewYrCdnOD9Gl9yvPoAoFAVmPJsBvleabvx4bnu1Kt6DrB2OALeRVag7BdWGWLhP1yooeMLEi6r2nYSOjg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.2.tgz", + "integrity": "sha512-ArdGtPHjLqWkqQuoVQ6a5UC5ebdX8INPuJuJNWRe0RGa/YNhVvxeWmCTFQ7LdmNCSUzVZzxAvUznKaYx645Rig==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.2.tgz", + "integrity": "sha512-B6UHHeNnnih8xH6wRKB0mOcJGvjZTww1FV59HqJoTJ5da9LCG6R4SEBt6uPqzlawv1LoEXSS0d4fBlHNWl6iYw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.2.tgz", + "integrity": "sha512-kr3gqzczJjSAncwOS6i7fpb4dlqcvLidqrX5hpGBIM1wtt0QEVtf4wFaAwVv8QygFU8iWUMYEoJZWuWxyua4GQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.2.tgz", + "integrity": "sha512-TDdHLKCWgPuq9vQcmyLrhg/bgbOvIQ8rtWQK7MRxJ9nvaxKx38NvY7/Lo6cYuEnNHqf6rMqnivOIPIQt6H2AoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.2.tgz", + "integrity": "sha512-xv9vS648T3X4AxFFZGWeB5Dou8ilsv4VVqJ0+loOIgDO20zIhYfDLkk5xoQiej2RiSQkld9ijF/fhLeonrz2mw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.2.tgz", + "integrity": "sha512-tbtXwnofRoTt223WUZYiUnbxhGAOVul/3StZ947U4A5NNjnQJV5irKMm76G0LGItWs6y+SCjUn/Q0WaMLkEskg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.2.tgz", + "integrity": "sha512-gc97UebApwdsSNT3q79glOSPdfwgwj5ELuiyuiMY3pEWMxeVqLGKfpDFoum4ujivzxn6veUPzkGuSYoh5deQ2Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.2.tgz", + "integrity": "sha512-jOG/0nXb3z+EM6SioY8RofqqmZ+9NKYvJ6QQaa9Mvd3RQxlH68/jcB/lpyVt4lCiqr04IyaC34NzhUqcXbB5FQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.2.tgz", + "integrity": "sha512-XAo7cJec80NWx9LlZFEJQxqKOMz/lX3geWs2iNT5CHIERLFfd90f3RYLLjiCBm1IMaQ4VOX/lTC9lWfzzQm14Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.2.tgz", + "integrity": "sha512-A+JAs4+EhsTjnPQvo9XY/DC0ztaws3vfqzrMNMKlwQXuniBKOIIvAAI8M0fBYiTCxQnElYu7mLk7JrhlQ+HeOw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.2.tgz", + "integrity": "sha512-ZhcrakbqA1SCiJRMKSU64AZcYzlZ/9M5LaYil9QWxx9vLnkQ9Vnkve17Qn4SjlipqIIBFKjBES6Zxhnvh0EAEw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.2.tgz", + "integrity": "sha512-2mLH46K1u3r6uwc95hU+OR9q/ggYMpnS7pSp83Ece1HUQgF9Nh/QwTK5rcgbFnV9j+08yBrU5sA/P0RK2MSBNA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true + }, + "node_modules/@sindresorhus/is": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", + "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" } }, "node_modules/@sinonjs/commons": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.7.1.tgz", - "integrity": "sha512-Debi3Baff1Qu1Unc3mjJ96MgpbwTn43S1+9yJ0llWygPwDNu2aaWBD6yc9y/Z8XDRNhx7U+u2UDg2OGQXkclUQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.2.2.tgz", + "integrity": "sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@sinonjs/samsam": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz", + "integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^2.0.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } + }, + "node_modules/@sinonjs/samsam/node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", "dev": true, "dependencies": { "type-detect": "4.0.8" } }, - "node_modules/@sinonjs/commons/node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@sinonjs/formatio": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz", - "integrity": "sha512-ls6CAMA6/5gG+O/IdsBcblvnd8qcO/l1TYoNeAzp3wcISOxlPXQEus0mLcdwazEkWjaBdaJ3TaxmNgCLWwvWzg==", - "dev": true, - "dependencies": { - "samsam": "1.3.0" - } - }, - "node_modules/@sinonjs/samsam": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.3.tgz", - "integrity": "sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^1.3.0", - "array-from": "^2.1.1", - "lodash": "^4.17.15" - } - }, "node_modules/@sinonjs/text-encoding": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", - "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", - "dev": true - }, - "node_modules/@socket.io/component-emitter": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", - "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", - "dev": true - }, - "node_modules/@types/chai": { - "version": "4.2.14", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.14.tgz", - "integrity": "sha512-G+ITQPXkwTrslfG5L/BksmbLUA0M1iybEsmCWPqzSxsRRhJZimBKJkoMi8fr/CPygPTj4zO5pJH7I2/cm9M7SQ==", - "dev": true - }, - "node_modules/@types/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", - "dev": true - }, - "node_modules/@types/cors": { - "version": "2.8.13", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", - "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.3.tgz", + "integrity": "sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==", "dev": true, + "license": "(Unlicense OR Apache-2.0)" + }, + "node_modules/@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.1" + }, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "node_modules/@types/accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==", + "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } }, - "node_modules/@types/estree": { - "version": "0.0.39", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", - "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "node_modules/@types/babel__code-frame": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@types/babel__code-frame/-/babel__code-frame-7.0.6.tgz", + "integrity": "sha512-Anitqkl3+KrzcW2k77lRlg/GfLZLWXBuNgbEcIOU6M92yw42vsd3xV/Z/yAHEj8m+KUjL6bWOVOFqX8PFPJ4LA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/chai": { + "version": "4.3.19", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.19.tgz", + "integrity": "sha512-2hHHvQBVE2FiSK4eN0Br6snX9MtolHaTo/batnLjlGRhoQzlCL61iVpxoqO7SfFyOw+P/pwv+0zNHzKoGWz9Cw==", "dev": true }, + "node_modules/@types/co-body": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/@types/co-body/-/co-body-6.1.3.tgz", + "integrity": "sha512-UhuhrQ5hclX6UJctv5m4Rfp52AfG9o9+d9/HwjxhVB5NjXxr5t9oKgJxN8xRHgr35oo8meUEHUPFWiKg6y71aA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*" + } + }, + "node_modules/@types/command-line-args": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/command-line-args/-/command-line-args-5.2.3.tgz", + "integrity": "sha512-uv0aG6R0Y8WHZLTamZwtfsDLVRnOa+n+n5rEvFWL5Na5gZ8V2Teab/duDPFzIIIhs9qizDpcavCusCLJZu62Kw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/content-disposition": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/@types/content-disposition/-/content-disposition-0.5.8.tgz", + "integrity": "sha512-QVSSvno3dE0MgO76pJhmv4Qyi/j0Yk9pBp0Y7TJ2Tlj+KCgJWY6qX7nnxCOLkZ3VYRSIk1WTxCvwUSdx6CCLdg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/convert-source-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/convert-source-map/-/convert-source-map-2.0.3.tgz", + "integrity": "sha512-ag0BfJLZf6CQz8VIuRIEYQ5Ggwk/82uvTQf27RcpyDNbY0Vw49LIPqAxk5tqYfrCs9xDaIMvl4aj7ZopnYL8bA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/cookies": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@types/cookies/-/cookies-0.9.0.tgz", + "integrity": "sha512-40Zk8qR147RABiQ7NQnBzWzDcjKzNrntB5BAmeGCb2p/MIyOE+4BVvc17wumsUqUw00bJYqoXFHYygQnEFh4/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/express": "*", + "@types/keygrip": "*", + "@types/node": "*" + } + }, + "node_modules/@types/debounce": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/debounce/-/debounce-1.2.4.tgz", + "integrity": "sha512-jBqiORIzKDOToaF63Fm//haOCHuwQuLa2202RK4MozpA6lh93eCBc+/8+wZn5OzjJt3ySdc+74SXWXB55Ewtyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.5", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.5.tgz", + "integrity": "sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-assert": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.5.tgz", + "integrity": "sha512-4+tE/lwdAahgZT1g30Jkdm9PzFRde0xwxBNUyRsCitRvCQB90iuA2uJYdUnhnANRcqGXaWOGY4FEoxeElNAK2g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/keygrip": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.6.tgz", + "integrity": "sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/koa": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.15.0.tgz", + "integrity": "sha512-7QFsywoE5URbuVnG3loe03QXuGajrnotr3gQkXcEBShORai23MePfFYdhz90FEtBBpkyIYQbVD+evKtloCgX3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/accepts": "*", + "@types/content-disposition": "*", + "@types/cookies": "*", + "@types/http-assert": "*", + "@types/http-errors": "*", + "@types/keygrip": "*", + "@types/koa-compose": "*", + "@types/node": "*" + } + }, + "node_modules/@types/koa-compose": { + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/@types/koa-compose/-/koa-compose-3.2.8.tgz", + "integrity": "sha512-4Olc63RY+MKvxMwVknCUDhRQX1pFQoBZ/lXcRLP69PQkEpze/0cr8LNqJQe5NFb/b19DWi2a5bTi2VAlQzhJuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/koa": "*" + } + }, "node_modules/@types/linkify-it": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.5.tgz", - "integrity": "sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", "dev": true }, "node_modules/@types/markdown-it": { @@ -913,32 +1905,1034 @@ } }, "node_modules/@types/mdurl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.5.tgz", - "integrity": "sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", "dev": true }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { - "version": "13.13.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.2.tgz", - "integrity": "sha512-LB2R1Oyhpg8gu4SON/mfforE525+Hi/M1ineICEDftqNVTyFg1aRIeGuTvXAoWHc4nbrFncWtJgMmoyRvuGh7A==", + "version": "22.5.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.3.tgz", + "integrity": "sha512-njripolh85IA9SQGTAqbmnNZTdxv7X/4OYGPz8tgy5JDr8MP+uDBa921GpYEoDDnwm0Hmn5ZPeJgiiSTPoOzkQ==", + "dev": true, + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", "dev": true }, - "node_modules/@types/resolve": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.8.tgz", - "integrity": "sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==", + "node_modules/@types/parse5": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-6.0.3.tgz", + "integrity": "sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==", "dev": true, + "license": "MIT" + }, + "node_modules/@types/qs": { + "version": "6.9.16", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz", + "integrity": "sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "dev": true + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/sinon": { + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-17.0.3.tgz", + "integrity": "sha512-j3uovdn8ewky9kRBG19bOwaZbexJu/XjtkHyjvUgt4xfPFz18dcORIMqnYh66Fx3Powhcr85NT5+er3+oViapw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/sinonjs__fake-timers": "*" + } + }, + "node_modules/@types/sinonjs__fake-timers": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.5.tgz", + "integrity": "sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.2.tgz", + "integrity": "sha512-113D3mDkZDjo+EeUEHCFy0qniNc1ZpecGiAU7WSo7YDoSzolZIQKpYFHrPpjkB2nuyahcKfrmLXeQlh7gqJYdw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "7.4.7", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz", + "integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==", + "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } }, - "node_modules/@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", + "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", + "dev": true, + "peer": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/type-utils": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", + "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", + "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz", + "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", + "dev": true, + "peer": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", + "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", + "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", + "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", + "dev": true, + "peer": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", + "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", "dev": true }, + "node_modules/@wdio/config": { + "version": "8.40.6", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.40.6.tgz", + "integrity": "sha512-rHCSmrhdJf7FlidcQPDvRKRPLYjklbrdxQa6J20BxHifTO4h2v23Wrq4OqqYIcq23gf9LpZvCA/PAMiET/QdVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@wdio/logger": "8.38.0", + "@wdio/types": "8.40.6", + "@wdio/utils": "8.40.6", + "decamelize": "^6.0.0", + "deepmerge-ts": "^5.0.0", + "glob": "^10.2.2", + "import-meta-resolve": "^4.0.0" + }, + "engines": { + "node": "^16.13 || >=18" + } + }, + "node_modules/@wdio/config/node_modules/decamelize": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.0.tgz", + "integrity": "sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@wdio/config/node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@wdio/config/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@wdio/config/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@wdio/logger": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-8.38.0.tgz", + "integrity": "sha512-kcHL86RmNbcQP+Gq/vQUGlArfU6IIcbbnNp32rRIraitomZow+iEoc519rdQmSVusDozMS5DZthkgDdxK+vz6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.1.2", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": "^16.13 || >=18" + } + }, + "node_modules/@wdio/logger/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@wdio/logger/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@wdio/logger/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@wdio/protocols": { + "version": "8.40.3", + "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-8.40.3.tgz", + "integrity": "sha512-wK7+eyrB3TAei8RwbdkcyoNk2dPu+mduMBOdPJjp8jf/mavd15nIUXLID1zA+w5m1Qt1DsT1NbvaeO9+aJQ33A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@wdio/repl": { + "version": "8.40.3", + "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-8.40.3.tgz", + "integrity": "sha512-mWEiBbaC7CgxvSd2/ozpbZWebnRIc8KRu/J81Hlw/txUWio27S7IpXBlZGVvhEsNzq0+cuxB/8gDkkXvMPbesw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "^22.2.0" + }, + "engines": { + "node": "^16.13 || >=18" + } + }, + "node_modules/@wdio/types": { + "version": "8.40.6", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.40.6.tgz", + "integrity": "sha512-ALftLri1BdsRuPrQkuW3evBNdOA5n4IkuoegOw6UE2z+R0f1YI5fHGSHNRWLnhtbOECbGyHXXqzbSxCEb+o+MA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "^22.2.0" + }, + "engines": { + "node": "^16.13 || >=18" + } + }, + "node_modules/@wdio/utils": { + "version": "8.40.6", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.40.6.tgz", + "integrity": "sha512-+TWfV6h+4f8gs7QiYUAWbWEylpZudQ+xkJPN34tRzPJK6dOBYEnIT/j6+1m3j39m1WPDehyYxIf1wCsrGKBxNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@puppeteer/browsers": "^1.6.0", + "@wdio/logger": "8.38.0", + "@wdio/types": "8.40.6", + "decamelize": "^6.0.0", + "deepmerge-ts": "^5.1.0", + "edgedriver": "^5.5.0", + "geckodriver": "^4.3.1", + "get-port": "^7.0.0", + "import-meta-resolve": "^4.0.0", + "locate-app": "^2.1.0", + "safaridriver": "^0.1.0", + "split2": "^4.2.0", + "wait-port": "^1.0.4" + }, + "engines": { + "node": "^16.13 || >=18" + } + }, + "node_modules/@wdio/utils/node_modules/@puppeteer/browsers": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.9.1.tgz", + "integrity": "sha512-PuvK6xZzGhKPvlx3fpfdM2kYY3P/hB1URtK8wA7XUJ6prn6pp22zvJHu48th0SGcHL9SutbPHrFuQgfXTFobWA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "4.3.4", + "extract-zip": "2.0.1", + "progress": "2.0.3", + "proxy-agent": "6.3.1", + "tar-fs": "3.0.4", + "unbzip2-stream": "1.4.3", + "yargs": "17.7.2" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" + }, + "engines": { + "node": ">=16.3.0" + } + }, + "node_modules/@wdio/utils/node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@wdio/utils/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@wdio/utils/node_modules/decamelize": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.0.tgz", + "integrity": "sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@wdio/utils/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/@wdio/utils/node_modules/proxy-agent": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.1.tgz", + "integrity": "sha512-Rb5RVBy1iyqOtNl15Cw/llpeLH8bsb37gM1FUfKQ+Wck6xHlbAhWGUFiTRHtkjqGTA5pSHz6+0hrPW/oECihPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.1", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@wdio/utils/node_modules/tar-fs": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", + "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + } + }, + "node_modules/@web/browser-logs": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@web/browser-logs/-/browser-logs-0.4.0.tgz", + "integrity": "sha512-/EBiDAUCJ2DzZhaFxTPRIznEPeafdLbXShIL6aTu7x73x7ZoxSDv7DGuTsh2rWNMUa4+AKli4UORrpyv6QBOiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "errorstacks": "^2.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@web/config-loader": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@web/config-loader/-/config-loader-0.3.2.tgz", + "integrity": "sha512-Vrjv/FexBGmAdnCYpJKLHX1dfT1UaUdvHmX1JRaWos9OvDf/tFznYJ5SpJwww3Rl87/ewvLSYG7kfsMqEAsizQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@web/dev-server": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/@web/dev-server/-/dev-server-0.4.6.tgz", + "integrity": "sha512-jj/1bcElAy5EZet8m2CcUdzxT+CRvUjIXGh8Lt7vxtthkN9PzY9wlhWx/9WOs5iwlnG1oj0VGo6f/zvbPO0s9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.11", + "@types/command-line-args": "^5.0.0", + "@web/config-loader": "^0.3.0", + "@web/dev-server-core": "^0.7.2", + "@web/dev-server-rollup": "^0.6.1", + "camelcase": "^6.2.0", + "command-line-args": "^5.1.1", + "command-line-usage": "^7.0.1", + "debounce": "^1.2.0", + "deepmerge": "^4.2.2", + "internal-ip": "^6.2.0", + "nanocolors": "^0.2.1", + "open": "^8.0.2", + "portfinder": "^1.0.32" + }, + "bin": { + "wds": "dist/bin.js", + "web-dev-server": "dist/bin.js" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@web/dev-server-core": { + "name": "@openpgp/wtr-dev-server-core", + "version": "0.7.3-patch.1", + "resolved": "https://registry.npmjs.org/@openpgp/wtr-dev-server-core/-/wtr-dev-server-core-0.7.3-patch.1.tgz", + "integrity": "sha512-aYTXhw5Q9Z4MAbhIi3ty5X+94tU2AnrE3ZYbIXoxWrfLZ3ibUkFQzPaEL0qtD+fARJJ3qBIQHwzJTfhaOm5NyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/koa": "^2.11.6", + "@types/ws": "^7.4.0", + "@web/parse5-utils": "^2.1.0", + "chokidar": "^4.0.1", + "clone": "^2.1.2", + "es-module-lexer": "^1.0.0", + "get-stream": "^6.0.0", + "is-stream": "^2.0.0", + "isbinaryfile": "^5.0.0", + "koa": "^2.13.0", + "koa-etag": "^4.0.0", + "koa-send": "^5.0.1", + "koa-static": "^5.0.0", + "lru-cache": "^8.0.4", + "mime-types": "^2.1.27", + "parse5": "^6.0.1", + "picomatch": "^2.2.2", + "ws": "^7.5.10" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@web/dev-server-core/node_modules/chokidar": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", + "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@web/dev-server-core/node_modules/readdirp": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", + "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@web/dev-server-core/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@web/dev-server-rollup": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@web/dev-server-rollup/-/dev-server-rollup-0.6.4.tgz", + "integrity": "sha512-sJZfTGCCrdku5xYnQQG51odGI092hKY9YFM0X3Z0tRY3iXKXcYRaLZrErw5KfCxr6g0JRuhe4BBhqXTA5Q2I3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/plugin-node-resolve": "^15.0.1", + "@web/dev-server-core": "^0.7.2", + "nanocolors": "^0.2.1", + "parse5": "^6.0.1", + "rollup": "^4.4.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@web/parse5-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@web/parse5-utils/-/parse5-utils-2.1.0.tgz", + "integrity": "sha512-GzfK5disEJ6wEjoPwx8AVNwUe9gYIiwc+x//QYxYDAFKUp4Xb1OJAGLc2l2gVrSQmtPGLKrTRcW90Hv4pEq1qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/parse5": "^6.0.1", + "parse5": "^6.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@web/test-runner": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@web/test-runner/-/test-runner-0.19.0.tgz", + "integrity": "sha512-qLUupi88OK1Kl52cWPD/2JewUCRUxYsZ1V1DyLd05P7u09zCdrUYrtkB/cViWyxlBe/TOvqkSNpcTv6zLJ9GoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@web/browser-logs": "^0.4.0", + "@web/config-loader": "^0.3.0", + "@web/dev-server": "^0.4.0", + "@web/test-runner-chrome": "^0.17.0", + "@web/test-runner-commands": "^0.9.0", + "@web/test-runner-core": "^0.13.0", + "@web/test-runner-mocha": "^0.9.0", + "camelcase": "^6.2.0", + "command-line-args": "^5.1.1", + "command-line-usage": "^7.0.1", + "convert-source-map": "^2.0.0", + "diff": "^5.0.0", + "globby": "^11.0.1", + "nanocolors": "^0.2.1", + "portfinder": "^1.0.32", + "source-map": "^0.7.3" + }, + "bin": { + "web-test-runner": "dist/bin.js", + "wtr": "dist/bin.js" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@web/test-runner-browserstack": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@web/test-runner-browserstack/-/test-runner-browserstack-0.7.2.tgz", + "integrity": "sha512-zFux8OLXsQPHHXZWGiU5m3dpESp19XXg21WgzxoazxC9iKE0stW1ZJ/DyypCQEi0c0m01izzwA+7isZVv4OIgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@web/test-runner-webdriver": "^0.8.0", + "browserstack-local": "^1.4.8", + "internal-ip": "^6.2.0", + "nanoid": "^3.1.25" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@web/test-runner-chrome": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@web/test-runner-chrome/-/test-runner-chrome-0.17.0.tgz", + "integrity": "sha512-Il5N9z41NKWCrQM1TVgRaDWWYoJtG5Ha4fG+cN1MWL2OlzBS4WoOb4lFV3EylZ7+W3twZOFr1zy2Rx61yDYd/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@web/test-runner-core": "^0.13.0", + "@web/test-runner-coverage-v8": "^0.8.0", + "async-mutex": "0.4.0", + "chrome-launcher": "^0.15.0", + "puppeteer-core": "^23.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@web/test-runner-commands": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@web/test-runner-commands/-/test-runner-commands-0.9.0.tgz", + "integrity": "sha512-zeLI6QdH0jzzJMDV5O42Pd8WLJtYqovgdt0JdytgHc0d1EpzXDsc7NTCJSImboc2NcayIsWAvvGGeRF69SMMYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@web/test-runner-core": "^0.13.0", + "mkdirp": "^1.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@web/test-runner-core": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/@web/test-runner-core/-/test-runner-core-0.13.3.tgz", + "integrity": "sha512-ilDqF/v2sj0sD69FNSIDT7uw4M1yTVedLBt32/lXy3MMi6suCM7m/ZlhsBy8PXhf879WMvzBOl/vhJBpEMB9vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.11", + "@types/babel__code-frame": "^7.0.2", + "@types/co-body": "^6.1.0", + "@types/convert-source-map": "^2.0.0", + "@types/debounce": "^1.2.0", + "@types/istanbul-lib-coverage": "^2.0.3", + "@types/istanbul-reports": "^3.0.0", + "@web/browser-logs": "^0.4.0", + "@web/dev-server-core": "^0.7.2", + "chokidar": "^3.4.3", + "cli-cursor": "^3.1.0", + "co-body": "^6.1.0", + "convert-source-map": "^2.0.0", + "debounce": "^1.2.0", + "dependency-graph": "^0.11.0", + "globby": "^11.0.1", + "internal-ip": "^6.2.0", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.0.2", + "log-update": "^4.0.0", + "nanocolors": "^0.2.1", + "nanoid": "^3.1.25", + "open": "^8.0.2", + "picomatch": "^2.2.2", + "source-map": "^0.7.3" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@web/test-runner-core/node_modules/@web/dev-server-core/node_modules/chokidar": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", + "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", + "extraneous": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@web/test-runner-core/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@web/test-runner-coverage-v8": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@web/test-runner-coverage-v8/-/test-runner-coverage-v8-0.8.0.tgz", + "integrity": "sha512-PskiucYpjUtgNfR2zF2AWqWwjXL7H3WW/SnCAYmzUrtob7X9o/+BjdyZ4wKbOxWWSbJO4lEdGIDLu+8X2Xw+lA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@web/test-runner-core": "^0.13.0", + "istanbul-lib-coverage": "^3.0.0", + "lru-cache": "^8.0.4", + "picomatch": "^2.2.2", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@web/test-runner-mocha": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@web/test-runner-mocha/-/test-runner-mocha-0.9.0.tgz", + "integrity": "sha512-ZL9F6FXd0DBQvo/h/+mSfzFTSRVxzV9st/AHhpgABtUtV/AIpVE9to6+xdkpu6827kwjezdpuadPfg+PlrBWqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@web/test-runner-core": "^0.13.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@web/test-runner-playwright": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@web/test-runner-playwright/-/test-runner-playwright-0.11.0.tgz", + "integrity": "sha512-s+f43DSAcssKYVOD9SuzueUcctJdHzq1by45gAnSCKa9FQcaTbuYe8CzmxA21g+NcL5+ayo4z+MA9PO4H+PssQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@web/test-runner-core": "^0.13.0", + "@web/test-runner-coverage-v8": "^0.8.0", + "playwright": "^1.22.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@web/test-runner-webdriver": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@web/test-runner-webdriver/-/test-runner-webdriver-0.8.0.tgz", + "integrity": "sha512-Ps+xJeHSh8uvTTRcSOTsDFPEVTjP0oW7r8XVc3wXP9qBmCOsTWyQhDALsYqWwT0FwHr9UV2iRiiSZK8FPBRHjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@web/test-runner-core": "^0.13.0", + "webdriverio": "^8.8.6" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@web/test-runner/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@zip.js/zip.js": { + "version": "2.7.52", + "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.52.tgz", + "integrity": "sha512-+5g7FQswvrCHwYKNMd/KFxZSObctLSsQOgqBSi0LzwHo3li9Eh1w5cF5ndjQw9Zbr3ajVnd2+XyiX85gAetx1Q==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "bun": ">=0.7.0", + "deno": ">=1.0.0", + "node": ">=16.5.0" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -953,9 +2947,9 @@ } }, "node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -973,16 +2967,28 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "node_modules/acorn-walk": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", + "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", "dev": true, "dependencies": { - "es6-promisify": "^5.0.0" + "acorn": "^8.11.0" }, "engines": { - "node": ">= 4.0.0" + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" } }, "node_modules/ajv": { @@ -1002,14 +3008,43 @@ } }, "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true, "engines": { "node": ">=6" } }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -1034,22 +3069,10 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/ansi-styles/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, "node_modules/anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "dependencies": { "normalize-path": "^3.0.0", @@ -1059,63 +3082,166 @@ "node": ">= 8" } }, - "node_modules/append-transform": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", - "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", + "node_modules/archiver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz", + "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==", "dev": true, + "license": "MIT", "dependencies": { - "default-require-extensions": "^2.0.0" + "archiver-utils": "^5.0.2", + "async": "^3.2.4", + "buffer-crc32": "^1.0.0", + "readable-stream": "^4.0.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^3.0.0", + "zip-stream": "^6.0.1" }, "engines": { - "node": ">=4" + "node": ">= 14" } }, - "node_modules/archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "node_modules/archiver-utils": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz", + "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "^10.0.0", + "graceful-fs": "^4.2.0", + "is-stream": "^2.0.1", + "lazystream": "^1.0.0", + "lodash": "^4.17.15", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/archiver-utils/node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/archiver-utils/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/archiver-utils/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/archiver/node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/argon2id": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/argon2id/-/argon2id-1.0.1.tgz", + "integrity": "sha512-rsiD3lX+0L0CsiZARp3bf9EGxprtuWAT7PpiJd+Fk53URV0/USOQkBIP1dLTV8t6aui0ECbymQ9W9YCcTd6XgA==", "dev": true }, "node_modules/argparse": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", - "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/aria-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", - "dev": true, - "peer": true, - "dependencies": { - "@babel/runtime": "^7.10.2", - "@babel/runtime-corejs3": "^7.10.2" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/array-from": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", - "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "node_modules/array-includes": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", - "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", + "node_modules/aria-query": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", + "deep-equal": "^2.0.5" + } + }, + "node_modules/array-back": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", + "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", "is-string": "^1.0.7" }, "engines": { @@ -1125,15 +3251,65 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array.prototype.flat": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", - "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", "es-shim-unscopables": "^1.0.0" }, "engines": { @@ -1144,14 +3320,14 @@ } }, "node_modules/array.prototype.flatmap": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", - "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", "es-shim-unscopables": "^1.0.0" }, "engines": { @@ -1161,14 +3337,43 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/asn1.js": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.0.0.tgz", - "integrity": "sha512-Y+FKviD0uyIWWo/xE0XkUl0x1allKFhzEVJ+//2Dgqpy+n+B77MlPNqvyk7Vx50M9XyVzjnRhDqJAEAsyivlbA==", + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "peer": true, "dependencies": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/assertion-error": { @@ -1180,13 +3385,36 @@ "node": "*" } }, + "node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/ast-types-flow": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", - "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==", + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", "dev": true, "peer": true }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/async": { "version": "2.6.4", "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", @@ -1196,10 +3424,35 @@ "lodash": "^4.17.14" } }, + "node_modules/async-mutex": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.4.0.tgz", + "integrity": "sha512-eJFZ1YhRR8UN8eBLoNzcDPcy/jqjsg6I1AP+KvWQX80BqOSW1oJPJXDylPUEeMr2ZQvHgnQ//Lp6f3RQ1zI7HA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/axe-core": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.4.3.tgz", - "integrity": "sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.0.tgz", + "integrity": "sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==", "dev": true, "peer": true, "engines": { @@ -1207,43 +3460,115 @@ } }, "node_modules/axobject-query": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", - "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==", - "dev": true, - "peer": true - }, - "node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "node_modules/base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", "dev": true, + "peer": true, "engines": { - "node": "^4.5.0 || >= 5.9" + "node": ">= 0.4" } }, - "node_modules/basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "node_modules/b4a": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz", + "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==", "dev": true, + "license": "Apache-2.0" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/bare-events": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.4.2.tgz", + "integrity": "sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q==", + "dev": true, + "license": "Apache-2.0", + "optional": true + }, + "node_modules/bare-fs": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.5.tgz", + "integrity": "sha512-SlE9eTxifPDJrT6YgemQ1WGFleevzwY+XAP1Xqgl56HtcrisC2CHCZ2tq6dBpcH2TnNxwUEUGhweo+lrQtYuiw==", + "dev": true, + "license": "Apache-2.0", + "optional": true, "dependencies": { - "safe-buffer": "5.1.2" - }, + "bare-events": "^2.0.0", + "bare-path": "^2.0.0", + "bare-stream": "^2.0.0" + } + }, + "node_modules/bare-os": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.4.tgz", + "integrity": "sha512-z3UiI2yi1mK0sXeRdc4O1Kk8aOa/e+FNWZcTiPB/dfTWyLypuE99LibgRaQki914Jq//yAWylcAt+mknKdixRQ==", + "dev": true, + "license": "Apache-2.0", + "optional": true + }, + "node_modules/bare-path": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.3.tgz", + "integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-os": "^2.1.0" + } + }, + "node_modules/bare-stream": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.3.0.tgz", + "integrity": "sha512-pVRWciewGUeCyKEuRxwv06M079r+fRjAQjBEK2P6OYGrO43O+Z0LrPZZEjlc4mB6C2RpZ9AxJ1s7NLEtOHO6eA==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "b4a": "^1.6.6", + "streamx": "^2.20.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/basic-ftp": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", + "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", + "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.8" + "node": ">=10.0.0" } }, "node_modules/benchmark": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz", - "integrity": "sha1-CfPeMckWQl1JjMLuVloOvzwqVik=", + "integrity": "sha512-l9MlfN4M1K/H2fbhfMy3B7vJd6AGKJVQn2h6Sg/Yx+KckoUA7ewS5Vv6TjSq18ooE1kS9hhAlQRH3AkXIh/aOQ==", "dev": true, "dependencies": { "lodash": "^4.17.4", @@ -1251,12 +3576,15 @@ } }, "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/bluebird": { @@ -1266,154 +3594,98 @@ "dev": true }, "node_modules/bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" - }, - "node_modules/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", - "dev": true, - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.8.1", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.9.7", - "raw-body": "2.4.3", - "type-is": "~1.6.18" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/body-parser/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/body-parser/node_modules/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==", - "dev": true, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true }, "node_modules/brace-expansion": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", - "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" } }, - "node_modules/brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", - "dev": true - }, "node_modules/browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, - "node_modules/browserstack": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.5.3.tgz", - "integrity": "sha512-AO+mECXsW4QcqC9bxwM29O7qWa7bJT94uBFzeb5brylIQwawuEziwq20dPYbins95GlWzOawgyDNdjYAo32EKg==", - "dev": true, - "dependencies": { - "https-proxy-agent": "^2.2.1" - } - }, "node_modules/browserstack-local": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/browserstack-local/-/browserstack-local-1.4.8.tgz", - "integrity": "sha512-s+mc3gTOJwELdLWi4qFVKtGwMbb5JWsR+JxKlMaJkRJxoZ0gg3WREgPxAN0bm6iU5+S4Bi0sz0oxBRZT8BiNsQ==", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/browserstack-local/-/browserstack-local-1.5.5.tgz", + "integrity": "sha512-jKne7yosrMcptj3hqxp36TP9k0ZW2sCqhyurX24rUL4G3eT7OLgv+CSQN8iq5dtkv5IK+g+v8fWvsiC/S9KxMg==", "dev": true, "dependencies": { - "https-proxy-agent": "^4.0.0", + "agent-base": "^6.0.2", + "https-proxy-agent": "^5.0.1", "is-running": "^2.1.0", "ps-tree": "=1.2.0", "temp-fs": "^0.9.9" } }, - "node_modules/browserstack-local/node_modules/agent-base": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz", - "integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==", - "dev": true, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/browserstack-local/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, "node_modules/browserstack-local/node_modules/https-proxy-agent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz", - "integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "dev": true, "dependencies": { - "agent-base": "5", + "agent-base": "6", "debug": "4" }, "engines": { - "node": ">= 6.0.0" + "node": ">= 6" } }, - "node_modules/browserstack-local/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } }, "node_modules/buffer-from": { "version": "1.1.2", @@ -1422,12 +3694,15 @@ "dev": true }, "node_modules/builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/bytes": { @@ -1439,40 +3714,89 @@ "node": ">= 0.8" } }, - "node_modules/caching-transform": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-3.0.2.tgz", - "integrity": "sha512-Mtgcv3lh3U0zRii/6qVgQODdPA4G3zhG+jtbCWj39RXuUFTMzH0vcdMtaJS1jPowd+It2Pqr6y3NJMQqOqCE2w==", + "node_modules/c8": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/c8/-/c8-8.0.1.tgz", + "integrity": "sha512-EINpopxZNH1mETuI0DzRA4MZpAUH+IFiRhnmFD3vFr3vdrgxqi3VfE3KL0AIL+zDq8rC9bZqwM/VDmmoe04y7w==", "dev": true, "dependencies": { - "hasha": "^3.0.0", - "make-dir": "^2.0.0", - "package-hash": "^3.0.0", - "write-file-atomic": "^2.4.2" + "@bcoe/v8-coverage": "^0.2.3", + "@istanbuljs/schema": "^0.1.3", + "find-up": "^5.0.0", + "foreground-child": "^2.0.0", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.1.6", + "rimraf": "^3.0.2", + "test-exclude": "^6.0.0", + "v8-to-istanbul": "^9.0.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "c8": "bin/c8.js" }, "engines": { - "node": ">=6" + "node": ">=12" } }, - "node_modules/caching-transform/node_modules/write-file-atomic": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", - "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "node_modules/cache-content-type": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-content-type/-/cache-content-type-1.0.1.tgz", + "integrity": "sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==", "dev": true, + "license": "MIT", "dependencies": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" + "mime-types": "^2.1.18", + "ylru": "^1.2.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/cacheable-lookup": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + } + }, + "node_modules/cacheable-request": { + "version": "10.2.14", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", + "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "^4.0.2", + "get-stream": "^6.0.1", + "http-cache-semantics": "^4.1.1", + "keyv": "^4.5.3", + "mimic-response": "^4.0.0", + "normalize-url": "^8.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" } }, "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -1488,12 +3812,15 @@ } }, "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/catharsis": { @@ -1509,33 +3836,33 @@ } }, "node_modules/chai": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", - "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", + "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", "dev": true, "dependencies": { "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^3.0.1", - "get-func-name": "^2.0.0", - "loupe": "^2.3.1", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", "pathval": "^1.1.1", - "type-detect": "^4.0.5" + "type-detect": "^4.0.8" }, "engines": { "node": ">=4" } }, "node_modules/chai-as-promised": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", - "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.2.tgz", + "integrity": "sha512-aBDHZxRzYnUYuIAIPBH2s511DjlKPzXNlXSGFC8CwmroWQLfrW0LtE1nK3MAwwNhJPa9raEjNCmRoFpG0Hurdw==", "dev": true, "dependencies": { "check-error": "^1.0.2" }, "peerDependencies": { - "chai": ">= 2.1.2 < 5" + "chai": ">= 2.1.2 < 6" } }, "node_modules/chalk": { @@ -1554,40 +3881,121 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "node_modules/chalk-template": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz", + "integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==", "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/chalk-template?sponsor=1" + } + }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.2" + }, "engines": { "node": "*" } }, "node_modules/chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, "dependencies": { - "anymatch": "~3.1.1", + "anymatch": "~3.1.2", "braces": "~3.0.2", - "glob-parent": "~5.1.0", + "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" + "readdirp": "~3.6.0" }, "engines": { "node": ">= 8.10.0" }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, "optionalDependencies": { - "fsevents": "~2.3.1" + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/chrome-launcher": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.15.2.tgz", + "integrity": "sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/node": "*", + "escape-string-regexp": "^4.0.0", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^1.0.0" + }, + "bin": { + "print-chrome-path": "bin/print-chrome-path.js" + }, + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/chrome-launcher/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chromium-bidi": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.6.5.tgz", + "integrity": "sha512-RuLrmzYrxSb0s9SgpB+QN5jJucPduZQ/9SIe76MDxYJuecPW5mxMdacJ1f4EtgiV+R0p3sCkznTMvH0MPGFqjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "mitt": "3.0.1", + "urlpattern-polyfill": "10.0.0", + "zod": "3.23.8" + }, + "peerDependencies": { + "devtools-protocol": "*" } }, "node_modules/ci-info": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", - "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "dev": true, "funding": [ { @@ -1599,45 +4007,102 @@ "node": ">=8" } }, - "node_modules/cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "node_modules/clean-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz", + "integrity": "sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==", "dev": true, "dependencies": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "dependencies": { - "ansi-regex": "^4.1.0" + "escape-string-regexp": "^1.0.5" }, "engines": { - "node": ">=6" + "node": ">=4" + } + }, + "node_modules/clean-regexp/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/co-body": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/co-body/-/co-body-6.2.0.tgz", + "integrity": "sha512-Kbpv2Yd1NdL1V/V4cwLVxraHDV6K8ayohr2rmH0J87Er8+zJjcTa6dAn9QMPC9CRgU8+aNajKbSf1TzDB1yKPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@hapi/bourne": "^3.0.0", + "inflation": "^2.0.0", + "qs": "^6.5.2", + "raw-body": "^2.3.3", + "type-is": "^1.6.16" + }, + "engines": { + "node": ">=8.0.0" } }, "node_modules/color-convert": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", - "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { - "color-name": "^1.1.1" + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, "node_modules/color-name": { @@ -1646,10 +4111,62 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/command-line-args": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz", + "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^3.1.0", + "find-replace": "^3.0.0", + "lodash.camelcase": "^4.3.0", + "typical": "^4.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/command-line-usage": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-7.0.3.tgz", + "integrity": "sha512-PqMLy5+YGwhMh1wS04mVG44oqDsgyLRSKJBdOo1bnYhMKBW65gZF1dRp2OZRhiTjgUHljy99qkO7bsctLaw35Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^6.2.2", + "chalk-template": "^0.4.0", + "table-layout": "^4.1.0", + "typical": "^7.1.1" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/command-line-usage/node_modules/array-back": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", + "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.17" + } + }, + "node_modules/command-line-usage/node_modules/typical": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-7.2.0.tgz", + "integrity": "sha512-W1+HdVRUl8fS3MZ9ogD51GOb46xMmhAZzR0WPw5jcgIZQJVvkddYzAl4YTU6g5w33Y1iRQLdIi2/1jhi2RNL0g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.17" + } + }, "node_modules/commander": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", - "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", + "integrity": "sha512-+pJLBFVk+9ZZdlAOB5WuIElVPPth47hILFkmGym57aq8kwxsowvByvB0DHs1vQAhyMZzdcpTtF0VDKGkSDR4ZQ==", "dev": true, "dependencies": { "graceful-readlink": ">= 1.0.0" @@ -1661,13 +4178,30 @@ "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", "dev": true }, + "node_modules/compress-commons": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", + "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "crc32-stream": "^6.0.0", + "is-stream": "^2.0.1", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, "node_modules/confusing-browser-globals": { @@ -1676,105 +4210,163 @@ "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", "dev": true }, - "node_modules/connect": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", - "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dev": true, + "license": "MIT", "dependencies": { - "debug": "2.6.9", - "finalhandler": "1.1.2", - "parseurl": "~1.3.3", - "utils-merge": "1.0.1" + "safe-buffer": "5.2.1" }, "engines": { - "node": ">= 0.10.0" + "node": ">= 0.6" } }, + "node_modules/content-disposition/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "dev": true, "engines": { "node": ">= 0.6" } }, "node_modules/convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/cookies": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.9.1.tgz", + "integrity": "sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==", "dev": true, + "license": "MIT", "dependencies": { - "safe-buffer": "~5.1.1" - } - }, - "node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/core-js-pure": { - "version": "3.25.5", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.25.5.tgz", - "integrity": "sha512-oml3M22pHM+igfWHDfdLVq2ShWmjM2V4L+dQEBs0DWVIqEm9WHCwGAlZ6BmyBQGy5sFrJmcx+856D9lVKyGWYg==", - "dev": true, - "hasInstallScript": true, - "peer": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "dev": true, - "dependencies": { - "object-assign": "^4", - "vary": "^1" + "depd": "~2.0.0", + "keygrip": "~1.1.0" }, "engines": { - "node": ">= 0.10" + "node": ">= 0.8" } }, - "node_modules/corser": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", - "integrity": "sha1-jtolLsqrWEDc2XXOuQ2TcMgZ/4c=", + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true, - "engines": { - "node": ">= 0.4.0" - } + "license": "MIT" }, - "node_modules/cp-file": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/cp-file/-/cp-file-6.2.0.tgz", - "integrity": "sha512-fmvV4caBnofhPe8kOcitBwSn2f39QLjnAnGq3gO9dfd75mUytzKNZB1hde6QHunW2Rt+OwuBOMc3i1tNElbszA==", + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "make-dir": "^2.0.0", - "nested-error-stacks": "^2.0.0", - "pify": "^4.0.1", - "safe-buffer": "^5.0.1" + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" }, "engines": { - "node": ">=6" + "node": ">=0.8" } }, - "node_modules/cp-file/node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "node_modules/crc32-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz", + "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==", "dev": true, + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^4.0.0" + }, "engines": { - "node": ">=6" + "node": ">= 14" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "node-fetch": "^2.6.12" + } + }, + "node_modules/cross-fetch/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/cross-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/cross-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" } }, "node_modules/cross-spawn": { @@ -1791,25 +4383,16 @@ "node": ">= 8" } }, - "node_modules/cross-spawn/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } + "node_modules/css-shorthand-properties": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/css-shorthand-properties/-/css-shorthand-properties-1.1.1.tgz", + "integrity": "sha512-Md+Juc7M3uOdbAFwOYlTrccIZ7oCFuzrhKYQjdeUEW/sE1hv17Jp/Bws+ReOPpGVBTYCBoYo+G17V5Qo8QQ75A==", + "dev": true }, - "node_modules/custom-event": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", - "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", + "node_modules/css-value": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/css-value/-/css-value-0.0.1.tgz", + "integrity": "sha512-FUV3xaJ63buRLgHrLQVlVgQnQdR4yqdLGaDu7g8CQcWjInDfM9plBTPI9FRfpahju1UBSaMckeb2/46ApS/V1Q==", "dev": true }, "node_modules/damerau-levenshtein": { @@ -1819,71 +4402,25 @@ "dev": true, "peer": true }, - "node_modules/date-format": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.3.tgz", - "integrity": "sha512-7P3FyqDcfeznLZp2b+OMitV9Sz2lUnsT87WaTat9nVwqsBkTzPG3lPLNwW3en6F4pHUiWzr6vb8CLhjdK9bcxQ==", + "node_modules/data-uri-to-buffer": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", "dev": true, + "license": "MIT", "engines": { - "node": ">=4.0" + "node": ">= 14" } }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/data-view-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", "dev": true, "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "dev": true, - "dependencies": { - "type-detect": "^4.0.0" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/default-require-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", - "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", - "dev": true, - "dependencies": { - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", - "dev": true, - "dependencies": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -1892,124 +4429,55 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/di": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", - "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", - "dev": true - }, - "node_modules/diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "node_modules/data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", "dev": true, "dependencies": { - "esutils": "^2.0.2" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/dom-serialize": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", - "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", + "node_modules/data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", "dev": true, "dependencies": { - "custom-event": "~1.0.0", - "ent": "~2.2.0", - "extend": "^3.0.0", - "void-elements": "^2.0.0" - } - }, - "node_modules/duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "dev": true - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", - "dev": true - }, - "node_modules/email-addresses": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.1.0.tgz", - "integrity": "sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg==", - "dev": true - }, - "node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/engine.io": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.2.tgz", - "integrity": "sha512-FKn/3oMiJjrOEOeUub2WCox6JhxBXq/Zn3fZOMCBxKnNYtsdKjxhl7yR3fZhM9PV+rdE75SU5SYMc+2PGzo+Tg==", - "dev": true, - "dependencies": { - "@types/cookie": "^0.4.1", - "@types/cors": "^2.8.12", - "@types/node": ">=10.0.0", - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.4.1", - "cors": "~2.8.5", - "debug": "~4.3.1", - "engine.io-parser": "~5.0.3", - "ws": "~8.11.0" + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" }, "engines": { - "node": ">=10.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/engine.io-parser": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", - "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==", + "node_modules/debounce": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", + "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==", "dev": true, - "engines": { - "node": ">=10.0.0" - } + "license": "MIT" }, - "node_modules/engine.io/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -2020,18 +4488,420 @@ } } }, - "node_modules/engine.io/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "node_modules/debug/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-eql": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-equal": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", + "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.5", + "es-get-iterator": "^1.1.3", + "get-intrinsic": "^1.2.2", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.2", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "isarray": "^2.0.5", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "node_modules/ent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", - "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/deepmerge-ts": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-5.1.0.tgz", + "integrity": "sha512-eS8dRJOckyo9maw9Tu5O5RUi/4inFLrnoLkBe3cPfDMx3WZioXtmOew4TXQaxq7Rhl4xjDtR7c6x8nNTxOvbFw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dependency-graph": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", + "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/devtools-protocol": { + "version": "0.0.1330662", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1330662.tgz", + "integrity": "sha512-pzh6YQ8zZfz3iKlCvgzVCu22NdpZ8hNmwU6WnQjNVquh0A9iVosPtNLWDwaWVGyrntQlltPFztTMK5Cg6lfCuw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", "dev": true }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/eckey-utils": { + "version": "0.7.14", + "resolved": "https://registry.npmjs.org/eckey-utils/-/eckey-utils-0.7.14.tgz", + "integrity": "sha512-s/mENS+mMnJjDSydy0muBQQHMTWJ1nPe8EiphANZrf+lv/1u35aP9WvWHTWqCBJ21blNIurGF7UoLjtaOpoCFw==", + "dev": true + }, + "node_modules/edge-paths": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/edge-paths/-/edge-paths-3.0.5.tgz", + "integrity": "sha512-sB7vSrDnFa4ezWQk9nZ/n0FdpdUuC6R1EOrlU3DL+bovcNFK28rqu2emmAUjujYEJTWIgQGqgVVWUZXMnc8iWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/which": "^2.0.1", + "which": "^2.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/shirshak55" + } + }, + "node_modules/edgedriver": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/edgedriver/-/edgedriver-5.6.1.tgz", + "integrity": "sha512-3Ve9cd5ziLByUdigw6zovVeWJjVs8QHVmqOB0sJ0WNeVPcwf4p18GnxMmVvlFmYRloUwf5suNuorea4QzwBIOA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@wdio/logger": "^8.38.0", + "@zip.js/zip.js": "^2.7.48", + "decamelize": "^6.0.0", + "edge-paths": "^3.0.5", + "fast-xml-parser": "^4.4.1", + "node-fetch": "^3.3.2", + "which": "^4.0.0" + }, + "bin": { + "edgedriver": "bin/edgedriver.js" + } + }, + "node_modules/edgedriver/node_modules/decamelize": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.0.tgz", + "integrity": "sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/edgedriver/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/edgedriver/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/entities": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", @@ -2042,44 +4912,73 @@ } }, "node_modules/error-ex": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", - "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, "dependencies": { "is-arrayish": "^0.2.1" } }, + "node_modules/errorstacks": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/errorstacks/-/errorstacks-2.4.1.tgz", + "integrity": "sha512-jE4i0SMYevwu/xxAuzhly/KTwtj0xDhbzB6m1xPImxTkw8wcCbgarOQPfCVMi5JKVyW7in29pNJCCJrry3Ynnw==", + "dev": true, + "license": "MIT" + }, "node_modules/es-abstract": { - "version": "1.20.4", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz", - "integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==", + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.1.3", - "get-symbol-description": "^1.0.0", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", + "hasown": "^2.0.2", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", + "is-data-view": "^1.0.1", + "is-negative-zero": "^2.0.3", "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", + "is-shared-array-buffer": "^1.0.3", "is-string": "^1.0.7", + "is-typed-array": "^1.1.13", "is-weakref": "^1.0.2", - "object-inspect": "^1.12.2", + "object-inspect": "^1.13.1", "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "safe-regex-test": "^1.0.0", - "string.prototype.trimend": "^1.0.5", - "string.prototype.trimstart": "^1.0.5", - "unbox-primitive": "^1.0.2" + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.2", + "safe-regex-test": "^1.0.3", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.6", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.15" }, "engines": { "node": ">= 0.4" @@ -2088,13 +4987,113 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-shim-unscopables": { + "node_modules/es-define-property": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", "dev": true, "dependencies": { - "has": "^1.0.3" + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-get-iterator": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.19.tgz", + "integrity": "sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.3", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "globalthis": "^1.0.3", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.7", + "iterator.prototype": "^1.1.2", + "safe-array-concat": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", + "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" } }, "node_modules/es-to-primitive": { @@ -2114,31 +5113,49 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true - }, - "node_modules/es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", - "dev": true - }, - "node_modules/es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "node_modules/esbuild": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", "dev": true, - "dependencies": { - "es6-promise": "^4.0.3" + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" } }, "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, "engines": { "node": ">=6" @@ -2147,62 +5164,85 @@ "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "dev": true }, "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true, "engines": { - "node": ">=0.8.0" + "node": ">=8" + } + }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" } }, "node_modules/eslint": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.34.0.tgz", - "integrity": "sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, + "license": "MIT", "dependencies": { - "@eslint/eslintrc": "^1.4.1", - "@humanwhocodes/config-array": "^0.11.8", + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", + "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", + "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "bin": { @@ -2264,15 +5304,29 @@ "semver": "bin/semver.js" } }, + "node_modules/eslint-config-airbnb-typescript": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-18.0.0.tgz", + "integrity": "sha512-oc+Lxzgzsu8FQyFVa4QFaVKiitTYiiW3frB9KYW5OWdPrqFc7FzxgB20hP4cHMlr+MBzGcLl3jnCOVOydL9mIg==", + "dev": true, + "dependencies": { + "eslint-config-airbnb-base": "^15.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^7.0.0", + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" + } + }, "node_modules/eslint-import-resolver-node": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", - "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", "dev": true, "dependencies": { "debug": "^3.2.7", - "is-core-module": "^2.11.0", - "resolve": "^1.22.1" + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" } }, "node_modules/eslint-import-resolver-node/node_modules/debug": { @@ -2284,17 +5338,47 @@ "ms": "^2.1.1" } }, - "node_modules/eslint-import-resolver-node/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "node_modules/eslint-import-resolver-typescript": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.3.tgz", + "integrity": "sha512-ud9aw4szY9cCT1EWWdGv1L1XR6hh2PaRWif0j2QjQ0pgTY/69iw+W0Z4qZv5wHahOl8isEr+k/JnyAqNQkLkIA==", + "dev": true, + "dependencies": { + "@nolyfill/is-core-module": "1.0.39", + "debug": "^4.3.5", + "enhanced-resolve": "^5.15.0", + "eslint-module-utils": "^2.8.1", + "fast-glob": "^3.3.2", + "get-tsconfig": "^4.7.5", + "is-bun-module": "^1.0.2", + "is-glob": "^4.0.3" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*", + "eslint-plugin-import-x": "*" + }, + "peerDependenciesMeta": { + "eslint-plugin-import": { + "optional": true + }, + "eslint-plugin-import-x": { + "optional": true + } + } }, "node_modules/eslint-module-utils": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", - "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", + "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^3.2.7" }, @@ -2316,16 +5400,10 @@ "ms": "^2.1.1" } }, - "node_modules/eslint-module-utils/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, "node_modules/eslint-plugin-chai-friendly": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-chai-friendly/-/eslint-plugin-chai-friendly-0.7.2.tgz", - "integrity": "sha512-LOIfGx5sZZ5FwM1shr2GlYAWV9Omdi+1/3byuVagvQNoGUuU0iHhp7AfjA1uR+4dJ4Isfb4+FwBJgQajIw9iAg==", + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-chai-friendly/-/eslint-plugin-chai-friendly-0.7.4.tgz", + "integrity": "sha512-PGPjJ8diYgX1mjLxGJqRop2rrGwZRKImoEOwUOgoIhg0p80MkTaqvmFLe5TF7/iagZHggasvIfQlUyHIhK/PYg==", "dev": true, "engines": { "node": ">=0.10.0" @@ -2335,32 +5413,47 @@ } }, "node_modules/eslint-plugin-import": { - "version": "2.27.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", - "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", + "version": "2.31.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", + "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", "dev": true, + "license": "MIT", "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "array.prototype.flatmap": "^1.3.1", + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.8", + "array.prototype.findlastindex": "^1.2.5", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", "debug": "^3.2.7", "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.7", - "eslint-module-utils": "^2.7.4", - "has": "^1.0.3", - "is-core-module": "^2.11.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.0", + "hasown": "^2.0.2", + "is-core-module": "^2.15.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", - "object.values": "^1.1.6", - "resolve": "^1.22.1", - "semver": "^6.3.0", - "tsconfig-paths": "^3.14.1" + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.0", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.8", + "tsconfig-paths": "^3.15.0" }, "engines": { "node": ">=4" }, "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-import/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "node_modules/eslint-plugin-import/node_modules/debug": { @@ -2372,6 +5465,18 @@ "ms": "^2.1.1" } }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/eslint-plugin-import/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -2384,12 +5489,6 @@ "node": "*" } }, - "node_modules/eslint-plugin-import/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, "node_modules/eslint-plugin-import/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -2400,39 +5499,46 @@ } }, "node_modules/eslint-plugin-jsx-a11y": { - "version": "6.6.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.1.tgz", - "integrity": "sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.0.tgz", + "integrity": "sha512-ySOHvXX8eSN6zz8Bywacm7CvGNhUtdjvqfQDVe6020TUK34Cywkw7m0KsCCk1Qtm9G1FayfTN1/7mMYnYO2Bhg==", "dev": true, "peer": true, "dependencies": { - "@babel/runtime": "^7.18.9", - "aria-query": "^4.2.2", - "array-includes": "^3.1.5", - "ast-types-flow": "^0.0.7", - "axe-core": "^4.4.3", - "axobject-query": "^2.2.0", + "aria-query": "~5.1.3", + "array-includes": "^3.1.8", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "^4.10.0", + "axobject-query": "^4.1.0", "damerau-levenshtein": "^1.0.8", "emoji-regex": "^9.2.2", - "has": "^1.0.3", - "jsx-ast-utils": "^3.3.2", - "language-tags": "^1.0.5", + "es-iterator-helpers": "^1.0.19", + "hasown": "^2.0.2", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", "minimatch": "^3.1.2", - "semver": "^6.3.0" + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.0" }, "engines": { "node": ">=4.0" }, "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" } }, - "node_modules/eslint-plugin-jsx-a11y/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "node_modules/eslint-plugin-jsx-a11y/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "peer": true + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } }, "node_modules/eslint-plugin-jsx-a11y/node_modules/minimatch": { "version": "3.1.2", @@ -2447,49 +5553,43 @@ "node": "*" } }, - "node_modules/eslint-plugin-jsx-a11y/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "peer": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/eslint-plugin-react": { - "version": "7.31.10", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.31.10.tgz", - "integrity": "sha512-e4N/nc6AAlg4UKW/mXeYWd3R++qUano5/o+t+wnWxIf+bLsOaH3a4q74kX3nDjYym3VBN4HyO9nEn1GcAqgQOA==", + "version": "7.35.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.35.2.tgz", + "integrity": "sha512-Rbj2R9zwP2GYNcIak4xoAMV57hrBh3hTaR0k7hVjwCQgryE/pw5px4b13EYjduOI0hfXyZhwBxaGpOTbWSGzKQ==", "dev": true, "peer": true, "dependencies": { - "array-includes": "^3.1.5", - "array.prototype.flatmap": "^1.3.0", + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.2", + "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.0.19", "estraverse": "^5.3.0", + "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", - "object.entries": "^1.1.5", - "object.fromentries": "^2.0.5", - "object.hasown": "^1.1.1", - "object.values": "^1.1.5", + "object.entries": "^1.1.8", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.0", "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.3", - "semver": "^6.3.0", - "string.prototype.matchall": "^4.0.7" + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.11", + "string.prototype.repeat": "^1.0.0" }, "engines": { "node": ">=4" }, "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, "node_modules/eslint-plugin-react-hooks": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", - "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", + "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", "dev": true, "peer": true, "engines": { @@ -2499,6 +5599,30 @@ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" } }, + "node_modules/eslint-plugin-react/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "peer": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/eslint-plugin-react/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -2513,13 +5637,13 @@ } }, "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", - "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "dev": true, "peer": true, "dependencies": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -2540,10 +5664,42 @@ "semver": "bin/semver.js" } }, + "node_modules/eslint-plugin-unicorn": { + "version": "48.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-48.0.1.tgz", + "integrity": "sha512-FW+4r20myG/DqFcCSzoumaddKBicIPeFnTrifon2mWIzlfyvzwyqZjqVP7m4Cqr/ZYisS2aiLghkUWaPg6vtCw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.5", + "@eslint-community/eslint-utils": "^4.4.0", + "ci-info": "^3.8.0", + "clean-regexp": "^1.0.0", + "esquery": "^1.5.0", + "indent-string": "^4.0.0", + "is-builtin-module": "^3.2.1", + "jsesc": "^3.0.2", + "lodash": "^4.17.21", + "pluralize": "^8.0.0", + "read-pkg-up": "^7.0.1", + "regexp-tree": "^0.1.27", + "regjsparser": "^0.10.0", + "semver": "^7.5.4", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" + }, + "peerDependencies": { + "eslint": ">=8.44.0" + } + }, "node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -2551,68 +5707,31 @@ }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" }, "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" + "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/eslint/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/eslint/node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "node_modules/eslint/node_modules/escape-string-regexp": { @@ -2627,54 +5746,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/eslint/node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/eslint/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -2687,30 +5758,15 @@ "node": "*" } }, - "node_modules/eslint/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/esm": { - "version": "3.2.25", - "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", - "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "dependencies": { - "acorn": "^8.8.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -2719,20 +5775,12 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true, + "license": "BSD-2-Clause", "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -2742,9 +5790,9 @@ } }, "node_modules/esquery": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.2.tgz", - "integrity": "sha512-JVSoLdTlTDkmjFmab7H/9SL9qGSyjElT3myyKp7krqjVFQCDLmj1QFaCLRFBszBKI0XVZaiiXvuPIX3ZwHe1Ng==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -2775,24 +5823,34 @@ } }, "node_modules/estree-walker": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", - "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", "dev": true }, "node_modules/esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/event-stream": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", - "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", + "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", "dev": true, "dependencies": { "duplexer": "~0.1.1", @@ -2804,17 +5862,86 @@ "through": "~2.3.1" } }, - "node_modules/eventemitter3": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz", - "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==", - "dev": true + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extract-zip/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/fast-deep-equal": { "version": "3.1.3", @@ -2822,6 +5949,41 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -2834,15 +5996,88 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fast-xml-parser": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz", + "integrity": "sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dev": true, "dependencies": { "reusify": "^1.0.4" } }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/fetch-blob/node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/fflate": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.7.4.tgz", + "integrity": "sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==", + "dev": true + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -2856,9 +6091,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -2867,130 +6102,17 @@ "node": ">=8" } }, - "node_modules/fill-range/node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "dev": true, - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dev": true, - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/find-cache-dir/node_modules/find-up": { + "node_modules/find-replace": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", + "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", "dev": true, + "license": "MIT", "dependencies": { - "locate-path": "^3.0.0" + "array-back": "^3.0.1" }, "engines": { - "node": ">=6" - } - }, - "node_modules/find-cache-dir/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/find-cache-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/find-cache-dir/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/find-cache-dir/node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/find-cache-dir/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/find-cache-dir/node_modules/pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "dependencies": { - "find-up": "^3.0.0" - }, - "engines": { - "node": ">=6" + "node": ">=4.0.0" } }, "node_modules/find-up": { @@ -3019,12 +6141,13 @@ } }, "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, "dependencies": { - "flatted": "^3.1.0", + "flatted": "^3.2.9", + "keyv": "^4.5.3", "rimraf": "^3.0.2" }, "engines": { @@ -3032,97 +6155,82 @@ } }, "node_modules/flatted": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", - "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, - "node_modules/follow-redirects": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } + "dependencies": { + "is-callable": "^1.1.3" } }, "node_modules/foreground-child": { - "version": "1.5.6", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-1.5.6.tgz", - "integrity": "sha1-T9ca0t/elnibmApcCilZN8svXOk=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", "dev": true, "dependencies": { - "cross-spawn": "^4", - "signal-exit": "^3.0.0" + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8.0.0" } }, - "node_modules/foreground-child/node_modules/cross-spawn": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", - "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", + "node_modules/form-data-encoder": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", + "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", "dev": true, - "dependencies": { - "lru-cache": "^4.0.1", - "which": "^1.2.9" + "license": "MIT", + "engines": { + "node": ">= 14.17" } }, - "node_modules/foreground-child/node_modules/lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", "dev": true, + "license": "MIT", "dependencies": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" } }, "node_modules/from": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", - "dev": true - }, - "node_modules/fs-extra": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz", - "integrity": "sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/fs-extra/node_modules/graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", + "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", "dev": true }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, "optional": true, @@ -3134,21 +6242,24 @@ } }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" }, "engines": { "node": ">= 0.4" @@ -3166,6 +6277,127 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/geckodriver": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-4.5.1.tgz", + "integrity": "sha512-lGCRqPMuzbRNDWJOQcUqhNqPvNsIFu6yzXF8J/6K3WCYFd2r5ckbeF7h1cxsnjA7YLSEiWzERCt6/gjZ3tW0ug==", + "dev": true, + "hasInstallScript": true, + "license": "MPL-2.0", + "dependencies": { + "@wdio/logger": "^9.0.0", + "@zip.js/zip.js": "^2.7.48", + "decamelize": "^6.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", + "node-fetch": "^3.3.2", + "tar-fs": "^3.0.6", + "which": "^4.0.0" + }, + "bin": { + "geckodriver": "bin/geckodriver.js" + }, + "engines": { + "node": "^16.13 || >=18 || >=20" + } + }, + "node_modules/geckodriver/node_modules/@wdio/logger": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-9.1.3.tgz", + "integrity": "sha512-cumRMK/gE1uedBUw3WmWXOQ7HtB6DR8EyKQioUz2P0IJtRRpglMBdZV7Svr3b++WWawOuzZHMfbTkJQmaVt8Gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.1.2", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18.20.0" + } + }, + "node_modules/geckodriver/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/geckodriver/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/geckodriver/node_modules/decamelize": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.0.tgz", + "integrity": "sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/geckodriver/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/geckodriver/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/geckodriver/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -3185,27 +6417,16 @@ } }, "node_modules/get-intrinsic": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", - "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" }, "engines": { "node": ">= 0.4" @@ -3214,101 +6435,282 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-port": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.1.0.tgz", + "integrity": "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.0.tgz", + "integrity": "sha512-Pgba6TExTZ0FJAn1qkJAjIeKoDJ3CsI2ChuLohJnZl/tTU8MVrq3b+2t5UOPfRa4RMsorClBjJALkJUMjG1PAw==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/get-uri": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz", + "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==", + "dev": true, + "license": "MIT", + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4", + "fs-extra": "^11.2.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/get-uri/node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/get-uri/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/get-uri/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "minimatch": "^5.0.1", + "once": "^1.3.0" }, "engines": { - "node": "*" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "dependencies": { - "is-glob": "^4.0.1" + "is-glob": "^4.0.3" }, "engines": { - "node": ">= 6" + "node": ">=10.13.0" } }, "node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=10" } }, "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "engines": { - "node": ">=4" + "node": ">=8" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", + "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^5.2.0", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.8", + "decompress-response": "^6.0.0", + "form-data-encoder": "^2.1.2", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" } }, "node_modules/graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true, - "engines": { - "node": ">=0.4.0" - } + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true }, "node_modules/graceful-readlink": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", + "integrity": "sha512-8tLu60LgxF6XpdbK8OW3FA+IfTNBn1ZHGHKF4KQbEeSkajYw5PlYJcKluntgegDPTg8UkHjpet1T82vk6TQ68w==", "dev": true }, "node_modules/grapheme-splitter": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, - "node_modules/growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true, - "engines": { - "node": ">=4.x" - } - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -3319,21 +6721,33 @@ } }, "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.1" + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3352,12 +6766,12 @@ } }, "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, "dependencies": { - "has-symbols": "^1.0.2" + "has-symbols": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -3366,26 +6780,16 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/hash.js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", - "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, "dependencies": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.0" - } - }, - "node_modules/hasha": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-3.0.0.tgz", - "integrity": "sha1-UqMvq4Vp1BymmmH/GiFPjrfIvTk=", - "dev": true, - "dependencies": { - "is-stream": "^1.0.1" + "function-bind": "^1.1.2" }, "engines": { - "node": ">=4" + "node": ">= 0.4" } }, "node_modules/he": { @@ -3397,46 +6801,55 @@ "he": "bin/he" } }, - "node_modules/hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dev": true, - "dependencies": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, "node_modules/hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, - "node_modules/html-encoding-sniffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", - "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", - "dev": true, - "dependencies": { - "whatwg-encoding": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, - "node_modules/http-errors": { + "node_modules/http-assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.5.0.tgz", + "integrity": "sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-equal": "~1.0.1", + "http-errors": "~1.8.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-assert/node_modules/deep-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", + "integrity": "sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-assert/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-assert/node_modules/http-errors": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", "dev": true, + "license": "MIT", "dependencies": { "depd": "~1.1.2", "inherits": "2.0.4", @@ -3448,102 +6861,165 @@ "node": ">= 0.6" } }, - "node_modules/http-errors/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "dev": true, + "license": "BSD-2-Clause" }, - "node_modules/http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dev": true, "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" }, "engines": { - "node": ">=8.0.0" + "node": ">= 0.8" } }, - "node_modules/http-server": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz", - "integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==", + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", "dependencies": { - "basic-auth": "^2.0.1", - "chalk": "^4.1.2", - "corser": "^2.0.1", - "he": "^1.2.0", - "html-encoding-sniffer": "^3.0.0", - "http-proxy": "^1.18.1", - "mime": "^1.6.0", - "minimist": "^1.2.6", - "opener": "^1.5.1", - "portfinder": "^1.0.28", - "secure-compare": "3.0.1", - "union": "~0.5.0", - "url-join": "^4.0.1" - }, - "bin": { - "http-server": "bin/http-server" + "agent-base": "^7.1.0", + "debug": "^4.3.4" }, "engines": { - "node": ">=12" + "node": ">= 14" } }, - "node_modules/http-server/node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "dev": true, - "bin": { - "mime": "cli.js" + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" }, "engines": { - "node": ">=4" + "node": ">= 14" + } + }, + "node_modules/http2-wrapper": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", + "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + }, + "engines": { + "node": ">=10.19.0" } }, "node_modules/https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", "dev": true, + "license": "MIT", "dependencies": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" + "agent-base": "^7.0.2", + "debug": "4" }, "engines": { - "node": ">= 4.5.0" + "node": ">= 14" } }, - "node_modules/https-proxy-agent/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/https-proxy-agent/node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "dependencies": { - "ms": "^2.1.1" + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/https-proxy-agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" }, "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "engines": { "node": ">= 4" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true, + "license": "MIT" + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -3560,19 +7036,50 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/import-meta-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", + "integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "engines": { "node": ">=0.8.19" } }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inflation": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/inflation/-/inflation-2.1.0.tgz", + "integrity": "sha512-t54PPJHG1Pp7VQvxyVCJ9mBbjG3Hqryges9bXoOO6GExCPa+//i/d5GSuFtpx3ALLd7lgIAur6zrIlBQyJuMlQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, "dependencies": { "once": "^1.3.0", @@ -3580,30 +7087,132 @@ } }, "node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/internal-ip": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-6.2.0.tgz", + "integrity": "sha512-D8WGsR6yDt8uq7vDMu7mjcR+yRMm3dW8yufyChmszWRjcSHuxLBkR3GdS2HZAjodsaGuCvXeEJpueisXJULghg==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-gateway": "^6.0.0", + "ipaddr.js": "^1.9.1", + "is-ip": "^3.1.0", + "p-event": "^4.2.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/internal-ip?sponsor=1" + } }, "node_modules/internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", + "es-errors": "^1.3.0", + "hasown": "^2.0.0", "side-channel": "^1.0.4" }, "engines": { "node": ">= 0.4" } }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ip-regex": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", + "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, + "node_modules/is-async-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", + "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", + "dev": true, + "peer": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-bigint": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", @@ -3645,15 +7254,27 @@ } }, "node_modules/is-builtin-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", - "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", "dev": true, "dependencies": { - "builtin-modules": "^1.0.0" + "builtin-modules": "^3.3.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-bun-module": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-1.1.0.tgz", + "integrity": "sha512-4mTAVPlrXpaN3jtF0lsnPCMGnq4+qZjVIKq0HCpfcqf8OC1SM5oATCIAPM5V5FN05qp2NNnFndphmdZS9CV3hA==", + "dev": true, + "dependencies": { + "semver": "^7.6.3" } }, "node_modules/is-callable": { @@ -3668,25 +7289,31 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-ci": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", "dev": true, "dependencies": { - "ci-info": "^3.2.0" + "hasown": "^2.0.2" }, - "bin": { - "is-ci": "bin.js" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "node_modules/is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", "dev": true, "dependencies": { - "has": "^1.0.3" + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3725,19 +7352,47 @@ "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/is-finalizationregistry": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", + "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, "engines": { - "node": ">=4" + "node": ">=8" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-glob": { @@ -3752,16 +7407,41 @@ "node": ">=0.10.0" } }, + "node_modules/is-ip": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-3.1.0.tgz", + "integrity": "sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ip-regex": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-module": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", "dev": true }, "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true, "engines": { "node": ">= 0.4" @@ -3813,12 +7493,12 @@ } }, "node_modules/is-reference": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.1.4.tgz", - "integrity": "sha512-uJA/CDPO3Tao3GTrxYn6AwkM4nUPJiGGYu5+cB8qbC7WGFlrKZbiRo7SFKxUAEpFUfiHofWCXBUNhvYJMh+6zw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", "dev": true, "dependencies": { - "@types/estree": "0.0.39" + "@types/estree": "*" } }, "node_modules/is-regex": { @@ -3840,28 +7520,47 @@ "node_modules/is-running": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-running/-/is-running-2.1.0.tgz", - "integrity": "sha1-MKc/9cw4VOT8JUkICen1q/jeCeA=", + "integrity": "sha512-mjJd3PujZMl7j+D395WTIO5tU5RIDBfVSRtRR4VOJou3H66E38UjbjvDGh3slJzPuolsb+yQFqwHNNdyp5jg3w==", "dev": true }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2" + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-string": { @@ -3894,6 +7593,45 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "dev": true, + "dependencies": { + "which-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-weakref": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", @@ -3906,6 +7644,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-weakset": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", + "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-wsl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", @@ -3918,13 +7672,20 @@ "node": ">=8" } }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, "node_modules/isbinaryfile": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.8.tgz", - "integrity": "sha512-53h6XFniq77YdW+spoRrebh0mnmTxRPTlcuIArO57lmMdq4uBKFKaeTjnb92oYWrSn/LVL+LT+Hap2tFQj8V+w==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.2.tgz", + "integrity": "sha512-GvcjojwonMjWbTkfMpnVHVqXW/wKMYDfEpY94/8zy8HFMOqb/VL6oeONq9v87q4ttVlaTLnGXnJD4B5B1OTGIg==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 8.0.0" + "node": ">= 18.0.0" }, "funding": { "url": "https://github.com/sponsors/gjtorikian/" @@ -3933,177 +7694,73 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "node_modules/istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-lib-hook": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz", - "integrity": "sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA==", - "dev": true, - "dependencies": { - "append-transform": "^1.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", - "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", - "dev": true, - "dependencies": { - "@babel/generator": "^7.4.0", - "@babel/parser": "^7.4.3", - "@babel/template": "^7.4.0", - "@babel/traverse": "^7.4.3", - "@babel/types": "^7.4.0", - "istanbul-lib-coverage": "^2.0.5", - "semver": "^6.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" + "node": ">=8" } }, "node_modules/istanbul-lib-report": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", - "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "dependencies": { - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "supports-color": "^6.1.0" + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", - "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "rimraf": "^2.6.3", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/istanbul-lib-source-maps/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, "node_modules/istanbul-reports": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.7.tgz", - "integrity": "sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", "dev": true, "dependencies": { - "html-escaper": "^2.0.0" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" }, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/jest-worker": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", - "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "node_modules/iterator.prototype": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", + "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", "dev": true, + "peer": true, "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">= 10.13.0" + "define-properties": "^1.2.1", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "reflect.getprototypeof": "^1.0.4", + "set-function-name": "^2.0.1" } }, - "node_modules/js-sdsl": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", - "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" } }, "node_modules/js-tokens": { @@ -4113,13 +7770,12 @@ "dev": true }, "node_modules/js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" @@ -4134,22 +7790,35 @@ "xmlcreate": "^2.0.4" } }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "dev": true, + "license": "MIT" + }, "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", "dev": true, "bin": { "jsesc": "bin/jsesc" }, "engines": { - "node": ">=4" + "node": ">=6" } }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, "node_modules/json-schema-traverse": { @@ -4161,7 +7830,7 @@ "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, "node_modules/json5": { @@ -4176,342 +7845,95 @@ "json5": "lib/cli.js" } }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, "node_modules/jsx-ast-utils": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", - "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==", + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", "dev": true, "peer": true, "dependencies": { - "array-includes": "^3.1.5", - "object.assign": "^4.1.3" + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" }, "engines": { "node": ">=4.0" } }, - "node_modules/karma": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.1.tgz", - "integrity": "sha512-Cj57NKOskK7wtFWSlMvZf459iX+kpYIPXmkNUzP2WAFcA7nhr/ALn5R7sw3w+1udFDcpMx/tuB8d5amgm3ijaA==", + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", "dev": true, + "license": "(MIT OR GPL-3.0-or-later)", "dependencies": { - "@colors/colors": "1.5.0", - "body-parser": "^1.19.0", - "braces": "^3.0.2", - "chokidar": "^3.5.1", - "connect": "^3.7.0", - "di": "^0.0.1", - "dom-serialize": "^2.2.1", - "glob": "^7.1.7", - "graceful-fs": "^4.2.6", - "http-proxy": "^1.18.1", - "isbinaryfile": "^4.0.8", - "lodash": "^4.17.21", - "log4js": "^6.4.1", - "mime": "^2.5.2", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.5", - "qjobs": "^1.2.0", - "range-parser": "^1.2.1", - "rimraf": "^3.0.2", - "socket.io": "^4.4.1", - "source-map": "^0.6.1", - "tmp": "^0.2.1", - "ua-parser-js": "^0.7.30", - "yargs": "^16.1.1" - }, - "bin": { - "karma": "bin/karma" + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/just-extend": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz", + "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/keygrip": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz", + "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tsscmp": "1.0.6" }, "engines": { - "node": ">= 10" + "node": ">= 0.6" } }, - "node_modules/karma-browserstack-launcher": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/karma-browserstack-launcher/-/karma-browserstack-launcher-1.6.0.tgz", - "integrity": "sha512-Y/UWPdHZkHIVH2To4GWHCTzmrsB6H7PBWy6pw+TWz5sr4HW2mcE+Uj6qWgoVNxvQU1Pfn5LQQzI6EQ65p8QbiQ==", + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "dependencies": { - "browserstack": "~1.5.1", - "browserstack-local": "^1.3.7", - "q": "~1.5.0" - }, - "peerDependencies": { - "karma": ">=0.9" - } - }, - "node_modules/karma-chrome-launcher": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.1.tgz", - "integrity": "sha512-hsIglcq1vtboGPAN+DGCISCFOxW+ZVnIqhDQcCMqqCp+4dmJ0Qpq5QAjkbA0X2L9Mi6OBkHi2Srrbmm7pUKkzQ==", - "dev": true, - "dependencies": { - "which": "^1.2.1" - } - }, - "node_modules/karma-firefox-launcher": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/karma-firefox-launcher/-/karma-firefox-launcher-2.1.2.tgz", - "integrity": "sha512-VV9xDQU1QIboTrjtGVD4NCfzIH7n01ZXqy/qpBhnOeGVOkG5JYPEm8kuSd7psHE6WouZaQ9Ool92g8LFweSNMA==", - "dev": true, - "dependencies": { - "is-wsl": "^2.2.0", - "which": "^2.0.1" - } - }, - "node_modules/karma-firefox-launcher/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/karma-mocha": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/karma-mocha/-/karma-mocha-2.0.1.tgz", - "integrity": "sha512-Tzd5HBjm8his2OA4bouAsATYEpZrp9vC7z5E5j4C5Of5Rrs1jY67RAwXNcVmd/Bnk1wgvQRou0zGVLey44G4tQ==", - "dev": true, - "dependencies": { - "minimist": "^1.2.3" - } - }, - "node_modules/karma-mocha-reporter": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/karma-mocha-reporter/-/karma-mocha-reporter-2.2.5.tgz", - "integrity": "sha1-FRIAlejtgZGG5HoLAS8810GJVWA=", - "dev": true, - "dependencies": { - "chalk": "^2.1.0", - "log-symbols": "^2.1.0", - "strip-ansi": "^4.0.0" - }, - "peerDependencies": { - "karma": ">=0.13" - } - }, - "node_modules/karma-mocha-reporter/node_modules/ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/karma-mocha-reporter/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/karma-mocha-reporter/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/karma-mocha-reporter/node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/karma-mocha-reporter/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/karma-webkit-launcher": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/karma-webkit-launcher/-/karma-webkit-launcher-2.1.0.tgz", - "integrity": "sha512-S5eqhH0DIcuJFi27nC6eBxZ3MTrPnYybPthDU2Q8dfG0yFrXx8FqNDKSbRZsFFvAKJ55QVtYH1bbArd3ddI5Sg==", - "dev": true, - "dependencies": { - "is-ci": "^3.0.1", - "uuid": "^9.0.0" - }, - "peerDependenciesMeta": { - "playwright": { - "optional": true - } - } - }, - "node_modules/karma-webkit-launcher/node_modules/uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", - "dev": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/karma/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/karma/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/karma/node_modules/graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", - "dev": true - }, - "node_modules/karma/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/karma/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/karma/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/karma/node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "dependencies": { - "rimraf": "^3.0.0" - }, - "engines": { - "node": ">=8.17.0" - } - }, - "node_modules/karma/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/karma/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/karma/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/karma/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "engines": { - "node": ">=10" + "json-buffer": "3.0.1" } }, "node_modules/klaw": { @@ -4523,21 +7945,242 @@ "graceful-fs": "^4.1.9" } }, + "node_modules/koa": { + "version": "2.15.3", + "resolved": "https://registry.npmjs.org/koa/-/koa-2.15.3.tgz", + "integrity": "sha512-j/8tY9j5t+GVMLeioLaxweJiKUayFhlGqNTzf2ZGwL0ZCQijd2RLHK0SLW5Tsko8YyyqCZC2cojIb0/s62qTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "^1.3.5", + "cache-content-type": "^1.0.0", + "content-disposition": "~0.5.2", + "content-type": "^1.0.4", + "cookies": "~0.9.0", + "debug": "^4.3.2", + "delegates": "^1.0.0", + "depd": "^2.0.0", + "destroy": "^1.0.4", + "encodeurl": "^1.0.2", + "escape-html": "^1.0.3", + "fresh": "~0.5.2", + "http-assert": "^1.3.0", + "http-errors": "^1.6.3", + "is-generator-function": "^1.0.7", + "koa-compose": "^4.1.0", + "koa-convert": "^2.0.0", + "on-finished": "^2.3.0", + "only": "~0.0.2", + "parseurl": "^1.3.2", + "statuses": "^1.5.0", + "type-is": "^1.6.16", + "vary": "^1.1.2" + }, + "engines": { + "node": "^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4" + } + }, + "node_modules/koa-compose": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-4.1.0.tgz", + "integrity": "sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==", + "dev": true, + "license": "MIT" + }, + "node_modules/koa-convert": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/koa-convert/-/koa-convert-2.0.0.tgz", + "integrity": "sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA==", + "dev": true, + "license": "MIT", + "dependencies": { + "co": "^4.6.0", + "koa-compose": "^4.1.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/koa-etag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/koa-etag/-/koa-etag-4.0.0.tgz", + "integrity": "sha512-1cSdezCkBWlyuB9l6c/IFoe1ANCDdPBxkDkRiaIup40xpUub6U/wwRXoKBZw/O5BifX9OlqAjYnDyzM6+l+TAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "etag": "^1.8.1" + } + }, + "node_modules/koa-send": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/koa-send/-/koa-send-5.0.1.tgz", + "integrity": "sha512-tmcyQ/wXXuxpDxyNXv5yNNkdAMdFRqwtegBXUaowiQzUKqJehttS0x2j0eOZDQAyloAth5w6wwBImnFzkUz3pQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "http-errors": "^1.7.3", + "resolve-path": "^1.4.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/koa-send/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/koa-send/node_modules/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/koa-static": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/koa-static/-/koa-static-5.0.0.tgz", + "integrity": "sha512-UqyYyH5YEXaJrf9S8E23GoJFQZXkBVJ9zYYMPGz919MSX1KuvAcycIuS0ci150HCoPf4XQVhQ84Qf8xRPWxFaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.1.0", + "koa-send": "^5.0.0" + }, + "engines": { + "node": ">= 7.6.0" + } + }, + "node_modules/koa-static/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/koa/node_modules/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/koa/node_modules/http-errors/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ky": { + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/ky/-/ky-0.33.3.tgz", + "integrity": "sha512-CasD9OCEQSFIam2U8efFK81Yeg8vNMTBUqtMOHlrcWQHqUX3HeCl9Dr31u4toV7emlH8Mymk5+9p0lL6mKb/Xw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/ky?sponsor=1" + } + }, "node_modules/language-subtag-registry": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", - "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==", + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", "dev": true, "peer": true }, "node_modules/language-tags": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", - "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", "dev": true, "peer": true, "dependencies": { - "language-subtag-registry": "~0.3.2" + "language-subtag-registry": "^0.3.20" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" } }, "node_modules/levn": { @@ -4553,6 +8196,50 @@ "node": ">= 0.8.0" } }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lighthouse-logger": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-1.4.2.tgz", + "integrity": "sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^2.6.9", + "marky": "^1.2.2" + } + }, + "node_modules/lighthouse-logger/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/lighthouse-logger/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, "node_modules/linkify-it": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", @@ -4562,6 +8249,41 @@ "uc.micro": "^1.0.1" } }, + "node_modules/locate-app": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/locate-app/-/locate-app-2.5.0.tgz", + "integrity": "sha512-xIqbzPMBYArJRmPGUZD9CzV9wOqmVtQnaAn3wrj3s6WYW0bQvPI7x+sPYUGmDTYMHefVK//zc6HEYZ1qnxIK+Q==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://buymeacoffee.com/hejny" + }, + { + "type": "github", + "url": "https://github.com/hejny/locate-app/blob/main/README.md#%EF%B8%8F-contributing" + } + ], + "license": "Apache-2.0", + "dependencies": { + "@promptbook/utils": "0.69.5", + "type-fest": "4.26.0", + "userhome": "1.0.1" + } + }, + "node_modules/locate-app/node_modules/type-fest": { + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.0.tgz", + "integrity": "sha512-OduNjVJsFbifKb57UqZ2EMP1i4u64Xwow3NYXUtBbD4vIwJdQd4+xl8YDou1dlm4DVrtwT/7Ky8z8WyCULVfxw==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -4583,16 +8305,24 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, - "node_modules/lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", - "dev": true + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "dev": true, + "license": "MIT" }, "node_modules/lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", "dev": true }, "node_modules/lodash.merge": { @@ -4601,100 +8331,67 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "node_modules/log-symbols": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", - "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "node_modules/lodash.zip": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.zip/-/lodash.zip-4.2.0.tgz", + "integrity": "sha512-C7IOaBBK/0gMORRBd8OETNx3kmOkgIWIPvyDpZSCTwUrpYmgZwJkjZeOD8ww4xbOUOs4/attY+pciKvadNfFbg==", "dev": true, + "license": "MIT" + }, + "node_modules/log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, + "license": "MIT", "dependencies": { - "chalk": "^2.0.1" + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log-symbols/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, + "license": "MIT", "dependencies": { - "color-convert": "^1.9.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/loglevel": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz", + "integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==", "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" } }, - "node_modules/log-symbols/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/loglevel-plugin-prefix": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/loglevel-plugin-prefix/-/loglevel-plugin-prefix-0.8.4.tgz", + "integrity": "sha512-WpG9CcFAOjz/FtNht+QJeGpvVl/cdR6P0z6OcXSkr8wFJOsV2GRj2j10JLfjuA4aYkcKCNIEqRGCyTife9R8/g==", "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/log4js": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.4.1.tgz", - "integrity": "sha512-iUiYnXqAmNKiIZ1XSAitQ4TmNs8CdZYTAWINARF3LjnsLN8tY5m0vRwd6uuWj/yNY0YHxeZodnbmxKFUOM2rMg==", - "dev": true, - "dependencies": { - "date-format": "^4.0.3", - "debug": "^4.3.3", - "flatted": "^3.2.4", - "rfdc": "^1.3.0", - "streamroller": "^3.0.2" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/log4js/node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/log4js/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/lolex": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.3.2.tgz", - "integrity": "sha512-A5pN2tkFj7H0dGIAM6MFvHKMJcPnjZsOMvR7ujCjfgW5TbV6H9vb1PgxLtHvjqNZTHsUolz+6/WEO0N1xNx2ng==", - "dev": true + "license": "MIT" }, "node_modules/loose-envify": { "version": "1.4.0", @@ -4710,49 +8407,71 @@ } }, "node_modules/loupe": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", - "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", "dev": true, "dependencies": { - "get-func-name": "^2.0.0" + "get-func-name": "^2.0.1" + } + }, + "node_modules/lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz", + "integrity": "sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16.14" } }, "node_modules/magic-string": { - "version": "0.25.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", - "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "version": "0.30.11", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", + "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", "dev": true, "dependencies": { - "sourcemap-codec": "^1.4.4" + "@jridgewell/sourcemap-codec": "^1.5.0" } }, "node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" + "semver": "^7.5.3" }, "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-dir/node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true, - "engines": { - "node": ">=6" - } + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true }, "node_modules/map-stream": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", - "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", + "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==", "dev": true }, "node_modules/markdown-it": { @@ -4781,12 +8500,6 @@ "markdown-it": "*" } }, - "node_modules/markdown-it/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, "node_modules/marked": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", @@ -4799,6 +8512,13 @@ "node": ">= 12" } }, + "node_modules/marky": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/marky/-/marky-1.2.5.tgz", + "integrity": "sha512-q9JtQJKjpsVxCRVgQ+WapguSbKC3SQ5HEzFGPAJMStgh3QjCawp00UKv3MTTAArTmGmmPUvllHZoNbZ3gs0I+Q==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/mdurl": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", @@ -4808,103 +8528,107 @@ "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "dev": true, "engines": { "node": ">= 0.6" } }, - "node_modules/merge-source-map": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", - "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", - "dev": true, - "dependencies": { - "source-map": "^0.6.1" - } - }, - "node_modules/merge-source-map/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", "dev": true, - "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - }, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, "engines": { - "node": ">=8" + "node": ">= 8" } }, - "node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, - "bin": { - "mime": "cli.js" + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" }, "engines": { - "node": ">=4.0.0" + "node": ">=8.6" } }, "node_modules/mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, "dependencies": { - "mime-db": "1.51.0" + "mime-db": "1.52.0" }, "engines": { "node": ">= 0.6" } }, - "node_modules/minimalistic-assert": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", - "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=" + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } }, - "node_modules/minimalistic-crypto-utils": { + "node_modules/mimic-response": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/min-indent": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", - "dev": true + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "engines": { + "node": ">=4" + } }, "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/minimist": { @@ -4916,68 +8640,77 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" } }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "dev": true, + "license": "MIT" + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true, + "license": "MIT" + }, "node_modules/mocha": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", - "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.3.tgz", + "integrity": "sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A==", "dev": true, "dependencies": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.1", - "debug": "4.3.1", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.1.6", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "4.0.0", - "log-symbols": "4.0.0", - "minimatch": "3.0.4", - "ms": "2.1.3", - "nanoid": "3.1.20", - "serialize-javascript": "5.0.1", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "which": "2.0.2", - "wide-align": "1.1.3", - "workerpool": "6.1.0", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" }, "bin": { "_mocha": "bin/_mocha", - "mocha": "bin/mocha" + "mocha": "bin/mocha.js" }, "engines": { - "node": ">= 10.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" + "node": ">= 14.0.0" } }, - "node_modules/mocha/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, "node_modules/mocha/node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -4989,44 +8722,6 @@ "wrap-ansi": "^7.0.0" } }, - "node_modules/mocha/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/mocha/node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/mocha/node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/mocha/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, "node_modules/mocha/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -5039,75 +8734,29 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mocha/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "node_modules/mocha/node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" }, "engines": { - "node": "*" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mocha/node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/mocha/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/mocha/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/mocha/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/mocha/node_modules/js-yaml": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", - "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/mocha/node_modules/log-symbols": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", - "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0" + "brace-expansion": "^2.0.1" }, "engines": { "node": ">=10" @@ -5119,29 +8768,6 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "node_modules/mocha/node_modules/serialize-javascript": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", - "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/mocha/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/mocha/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -5157,47 +8783,6 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/mocha/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/mocha/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/mocha/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/mocha/node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -5217,25 +8802,39 @@ } }, "node_modules/mocha/node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, "engines": { "node": ">=10" } }, "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "node_modules/nanoid": { - "version": "3.1.20", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", - "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", + "node_modules/nanocolors": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/nanocolors/-/nanocolors-0.2.13.tgz", + "integrity": "sha512-0n3mSAQLPpGLV9ORXT5+C/D4mwew7Ebws69Hx4E2sgz2ZA5+32Q80B9tL8PbL7XHnRDiAxH/pnrUJ9a4fkTNTA==", "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -5246,7 +8845,7 @@ "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, "node_modules/negotiator": { @@ -5258,62 +8857,100 @@ "node": ">= 0.6" } }, - "node_modules/nested-error-stacks": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz", - "integrity": "sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==", - "dev": true + "node_modules/netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } }, "node_modules/nise": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.3.tgz", - "integrity": "sha512-Ymbac/94xeIrMf59REBPOv0thr+CJVFMhrlAkW/gjCIE58BGQdCj0x7KRCb3yz+Ga2Rz3E9XXSvUyyxqqhjQAQ==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/nise/-/nise-6.0.1.tgz", + "integrity": "sha512-DAyWGPQEuJVlL2eqKw6gdZKT+E/jo/ZrjEUDAslJLluCz81nWy+KSYybNp3KFm887Yvp7hv12jSM82ld8BmLxg==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@sinonjs/formatio": "^3.2.1", - "@sinonjs/text-encoding": "^0.7.1", - "just-extend": "^4.0.2", - "lolex": "^5.0.1", - "path-to-regexp": "^1.7.0" + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/text-encoding": "^0.7.2", + "just-extend": "^6.2.0", + "path-to-regexp": "^8.1.0" } }, - "node_modules/nise/node_modules/@sinonjs/formatio": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.2.tgz", - "integrity": "sha512-B8SEsgd8gArBLMD6zpRw3juQ2FVSsmdd7qlevyDqzS9WTCtvF55/gAL+h6gue8ZvPYcdiPdvueM/qm//9XzyTQ==", + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", "dev": true, - "dependencies": { - "@sinonjs/commons": "^1", - "@sinonjs/samsam": "^3.1.0" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" } }, - "node_modules/nise/node_modules/just-extend": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.1.0.tgz", - "integrity": "sha512-ApcjaOdVTJ7y4r08xI5wIqpvwS48Q0PBG4DJROcEkH1f8MdAiNFyFxz3xoL0LWAVwjrwPYZdVHHxhRHcx/uGLA==", - "dev": true - }, - "node_modules/nise/node_modules/lolex": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz", - "integrity": "sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A==", + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", "dev": true, + "license": "MIT", "dependencies": { - "@sinonjs/commons": "^1.7.0" + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/node-fetch/node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" } }, "node_modules/normalize-package-data": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "dev": true, "dependencies": { "hosted-git-info": "^2.1.4", - "is-builtin-module": "^1.0.0", + "resolve": "^1.10.0", "semver": "2 || 3 || 4 || 5", "validate-npm-package-license": "^3.0.1" } }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -5323,141 +8960,66 @@ "node": ">=0.10.0" } }, - "node_modules/nyc": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-14.1.1.tgz", - "integrity": "sha512-OI0vm6ZGUnoGZv/tLdZ2esSVzDwUC88SNs+6JoSOMVxA+gKMB8Tk7jBwgemLx4O40lhhvZCVw1C+OYLOBOPXWw==", + "node_modules/normalize-url": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz", + "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==", "dev": true, - "dependencies": { - "archy": "^1.0.0", - "caching-transform": "^3.0.2", - "convert-source-map": "^1.6.0", - "cp-file": "^6.2.0", - "find-cache-dir": "^2.1.0", - "find-up": "^3.0.0", - "foreground-child": "^1.5.6", - "glob": "^7.1.3", - "istanbul-lib-coverage": "^2.0.5", - "istanbul-lib-hook": "^2.0.7", - "istanbul-lib-instrument": "^3.3.0", - "istanbul-lib-report": "^2.0.8", - "istanbul-lib-source-maps": "^3.0.6", - "istanbul-reports": "^2.2.4", - "js-yaml": "^3.13.1", - "make-dir": "^2.1.0", - "merge-source-map": "^1.1.0", - "resolve-from": "^4.0.0", - "rimraf": "^2.6.3", - "signal-exit": "^3.0.2", - "spawn-wrap": "^1.4.2", - "test-exclude": "^5.2.3", - "uuid": "^3.3.2", - "yargs": "^13.2.2", - "yargs-parser": "^13.0.0" - }, - "bin": { - "nyc": "bin/nyc.js" - }, + "license": "MIT", "engines": { - "node": ">=6" - } - }, - "node_modules/nyc/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/nyc/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/nyc/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/nyc/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, + "license": "MIT", "dependencies": { - "p-limit": "^2.0.0" + "path-key": "^3.0.0" }, "engines": { - "node": ">=6" - } - }, - "node_modules/nyc/node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/nyc/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/nyc/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" + "node": ">=8" } }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true, + "peer": true, "engines": { "node": ">=0.10.0" } }, "node_modules/object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -5472,13 +9034,13 @@ } }, "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", "has-symbols": "^1.0.3", "object-keys": "^1.1.1" }, @@ -5490,29 +9052,29 @@ } }, "node_modules/object.entries": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", - "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", + "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" } }, "node_modules/object.fromentries": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz", - "integrity": "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, - "peer": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -5521,29 +9083,29 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object.hasown": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.1.tgz", - "integrity": "sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A==", + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", "dev": true, - "peer": true, "dependencies": { - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">= 0.4" } }, "node_modules/object.values": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", - "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", + "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -5553,9 +9115,9 @@ } }, "node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dev": true, "dependencies": { "ee-first": "1.1.1" @@ -5567,25 +9129,56 @@ "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "dependencies": { "wrappy": "1" } }, - "node_modules/opener": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.1.tgz", - "integrity": "sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==", + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, - "bin": { - "opener": "bin/opener-bin.js" + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/only": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/only/-/only-0.0.2.tgz", + "integrity": "sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ==", + "dev": true + }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "dependencies": { "deep-is": "^0.1.3", @@ -5593,19 +9186,46 @@ "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "word-wrap": "^1.2.5" }, "engines": { "node": ">= 0.8.0" } }, - "node_modules/os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "node_modules/p-cancelable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=12.20" + } + }, + "node_modules/p-event": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz", + "integrity": "sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-timeout": "^3.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" } }, "node_modules/p-limit": { @@ -5638,26 +9258,88 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/package-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-3.0.0.tgz", - "integrity": "sha512-lOtmukMDVvtkL84rJHI7dpTYq+0rli8N2wlnqUcBuDWCfVhRUfOmnR9SsoHFMLpACvEV60dX7rd0rFaYDZI+FA==", + "node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", "dev": true, + "license": "MIT", "dependencies": { - "graceful-fs": "^4.1.15", - "hasha": "^3.0.0", - "lodash.flattendeep": "^4.4.0", - "release-zalgo": "^1.0.0" + "p-finally": "^1.0.0" }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, "engines": { "node": ">=6" } }, - "node_modules/package-hash/node_modules/graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true + "node_modules/pac-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.2.tgz", + "integrity": "sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.5", + "pac-resolver": "^7.0.1", + "socks-proxy-agent": "^8.0.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-resolver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "dev": true, + "license": "MIT", + "dependencies": { + "degenerator": "^5.0.0", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", + "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true, + "license": "(MIT AND Zlib)" }, "node_modules/parent-module": { "version": "1.0.1", @@ -5671,6 +9353,31 @@ "node": ">=6" } }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true, + "license": "MIT" + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -5692,7 +9399,7 @@ "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, "engines": { "node": ">=0.10.0" @@ -5713,20 +9420,48 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, - "node_modules/path-to-regexp": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", - "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "isarray": "0.0.1" + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/path-to-regexp/node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/path-to-regexp": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.1.0.tgz", + "integrity": "sha512-Bqn3vc8CMHty6zuD+tG23s6v2kwxslHEhTj4eYaVKGIEB+YX/2wd0/rgXLFD9G9id9KCtbVy/3ZgmvZjpa0UdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } }, "node_modules/pathval": { "version": "1.1.1", @@ -5740,16 +9475,29 @@ "node_modules/pause-stream": { "version": "0.0.11", "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", - "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", + "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", "dev": true, "dependencies": { "through": "~2.3" } }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", + "dev": true + }, "node_modules/picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "engines": { "node": ">=8.6" @@ -5765,42 +9513,69 @@ "dev": true }, "node_modules/playwright": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.30.0.tgz", - "integrity": "sha512-ENbW5o75HYB3YhnMTKJLTErIBExrSlX2ZZ1C/FzmHjUYIfxj/UnI+DWpQr992m+OQVSg0rCExAOlRwB+x+yyIg==", + "version": "1.48.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.48.2.tgz", + "integrity": "sha512-NjYvYgp4BPmiwfe31j4gHLa3J7bD2WiBz8Lk2RoSsmX38SVIARZ18VYjxLjAcDsAhA+F4iSEXTSGgjua0rrlgQ==", "dev": true, - "hasInstallScript": true, + "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.30.0" + "playwright-core": "1.48.2" }, "bin": { "playwright": "cli.js" }, "engines": { - "node": ">=14" + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" } }, "node_modules/playwright-core": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.30.0.tgz", - "integrity": "sha512-7AnRmTCf+GVYhHbLJsGUtskWTE33SwMZkybJ0v6rqR1boxq2x36U7p1vDRV7HO2IwTZgmycracLxPEJI49wu4g==", + "version": "1.48.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.48.2.tgz", + "integrity": "sha512-sjjw+qrLFlriJo64du+EK0kJgZzoQPsabGF4lBvsid+3CNIZIYLgnMj9V6JY5VhM2Peh20DJWIVpVljLLnlawA==", "dev": true, + "license": "Apache-2.0", "bin": { - "playwright": "cli.js" + "playwright-core": "cli.js" }, "engines": { - "node": ">=14" + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "engines": { + "node": ">=4" } }, "node_modules/portfinder": { - "version": "1.0.28", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", - "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "version": "1.0.32", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", + "integrity": "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==", "dev": true, "dependencies": { - "async": "^2.6.2", - "debug": "^3.1.1", - "mkdirp": "^0.5.5" + "async": "^2.6.4", + "debug": "^3.2.7", + "mkdirp": "^0.5.6" }, "engines": { "node": ">= 0.12.0" @@ -5815,11 +9590,26 @@ "ms": "^2.1.1" } }, - "node_modules/portfinder/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "node_modules/portfinder/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + } }, "node_modules/prelude-ls": { "version": "1.2.1", @@ -5830,6 +9620,33 @@ "node": ">= 0.8.0" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -5842,6 +9659,56 @@ "react-is": "^16.13.1" } }, + "node_modules/proxy-agent": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", + "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.3", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.1", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true, + "license": "MIT" + }, "node_modules/ps-tree": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", @@ -5857,47 +9724,43 @@ "node": ">= 0.10" } }, - "node_modules/pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, - "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "node_modules/pump": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", "dev": true, - "engines": { - "node": ">=6" + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } }, - "node_modules/q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", + "node_modules/puppeteer-core": { + "version": "23.3.1", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-23.3.1.tgz", + "integrity": "sha512-m5gTpITEqqpSgAvPUI/Ch9igh5sNJV+BVVbqQMzqirRDVHDCkLGHaydEQZx2NZvSXdwCFrIV///cpSlX/uD0Sg==", "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@puppeteer/browsers": "2.4.0", + "chromium-bidi": "0.6.5", + "debug": "^4.3.7", + "devtools-protocol": "0.0.1330662", + "typed-query-selector": "^2.12.0", + "ws": "^8.18.0" + }, "engines": { - "node": ">=0.6.0", - "teleport": ">=0.2.0" - } - }, - "node_modules/qjobs": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", - "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", - "dev": true, - "engines": { - "node": ">=0.9" + "node": ">=18" } }, "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -5906,6 +9769,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/query-selector-shadow-dom": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/query-selector-shadow-dom/-/query-selector-shadow-dom-1.0.1.tgz", + "integrity": "sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw==", + "dev": true, + "license": "MIT" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -5926,6 +9796,26 @@ } ] }, + "node_modules/queue-tick": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", + "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", + "dev": true, + "license": "MIT" + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -5935,23 +9825,14 @@ "safe-buffer": "^5.1.0" } }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/raw-body": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", - "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dev": true, "dependencies": { "bytes": "3.1.2", - "http-errors": "1.8.1", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, @@ -5959,18 +9840,6 @@ "node": ">= 0.8" } }, - "node_modules/raw-body/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -5978,10 +9847,177 @@ "dev": true, "peer": true }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/readable-stream/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "dependencies": { "picomatch": "^2.2.1" @@ -5990,22 +10026,20 @@ "node": ">=8.10.0" } }, - "node_modules/regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", - "dev": true, - "peer": true - }, - "node_modules/regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "node_modules/reflect.getprototypeof": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", + "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==", "dev": true, + "peer": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.1", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "globalthis": "^1.0.3", + "which-builtin-type": "^1.1.3" }, "engines": { "node": ">= 0.4" @@ -6014,67 +10048,79 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "node_modules/regexp-tree": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", + "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" + "bin": { + "regexp-tree": "bin/regexp-tree" } }, - "node_modules/release-zalgo": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", + "node_modules/regexp.prototype.flags": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", "dev": true, "dependencies": { - "es6-error": "^4.0.1" + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" }, "engines": { - "node": ">=4" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regjsparser": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.10.0.tgz", + "integrity": "sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA==", + "dev": true, + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" } }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", - "dev": true - }, "node_modules/requizzle": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz", - "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", + "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", "dev": true, "dependencies": { - "lodash": "^4.17.14" + "lodash": "^4.17.21" } }, "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, "dependencies": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -6085,6 +10131,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true, + "license": "MIT" + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -6094,6 +10147,116 @@ "node": ">=4" } }, + "node_modules/resolve-path": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/resolve-path/-/resolve-path-1.4.0.tgz", + "integrity": "sha512-i1xevIst/Qa+nA9olDxLWnLk8YZbi8R/7JPbCMcgyWaFR6bKWaexgJgEB5oc2PKMjYdrHynyz0NY+if+H98t1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "http-errors": "~1.6.2", + "path-is-absolute": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/resolve-path/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/resolve-path/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/resolve-path/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true, + "license": "ISC" + }, + "node_modules/resolve-path/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/responselike": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", + "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lowercase-keys": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/resq": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/resq/-/resq-1.11.0.tgz", + "integrity": "sha512-G10EBz+zAAy3zUd/CDoBbXRL6ia9kOo3xRHrMDsHljI0GDkhYlyjwoCx5+3eCC4swi1uCoZQhskuJkj7Gp57Bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^2.0.1" + } + }, + "node_modules/resq/node_modules/fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==", + "dev": true, + "license": "MIT" + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -6104,16 +10267,18 @@ "node": ">=0.10.0" } }, - "node_modules/rfdc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", - "dev": true + "node_modules/rgb2hex": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/rgb2hex/-/rgb2hex-0.2.5.tgz", + "integrity": "sha512-22MOP1Rh7sAo1BZpDG6R5RFYzR2lYEgwq7HEmyW2qcsOqR2lQKmn+O//xV3YG/0rrhMC6KVX2hU+ZXuaw9a5bw==", + "dev": true, + "license": "MIT" }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, "dependencies": { "glob": "^7.1.3" @@ -6125,34 +10290,85 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rollup": { - "version": "2.38.5", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.38.5.tgz", - "integrity": "sha512-VoWt8DysFGDVRGWuHTqZzT02J0ASgjVq/hPs9QcBOGMd7B+jfTr/iqMVEyOi901rE3xq+Deq66GzIT1yt7sGwQ==", + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/rollup": { + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.2.tgz", + "integrity": "sha512-do/DFGq5g6rdDhdpPq5qb2ecoczeK6y+2UAjdJ5trjQJj5f1AiVdLRWRc9A9/fFukfvJRgM0UXzxBIYMovm5ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.6" + }, "bin": { "rollup": "dist/bin/rollup" }, "engines": { - "node": ">=10.0.0" + "node": ">=18.0.0", + "npm": ">=8.0.0" }, "optionalDependencies": { - "fsevents": "~2.3.1" - } - }, - "node_modules/rollup-plugin-terser": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", - "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.10.4", - "jest-worker": "^26.2.1", - "serialize-javascript": "^4.0.0", - "terser": "^5.0.0" - }, - "peerDependencies": { - "rollup": "^2.0.0" + "@rollup/rollup-android-arm-eabi": "4.24.2", + "@rollup/rollup-android-arm64": "4.24.2", + "@rollup/rollup-darwin-arm64": "4.24.2", + "@rollup/rollup-darwin-x64": "4.24.2", + "@rollup/rollup-freebsd-arm64": "4.24.2", + "@rollup/rollup-freebsd-x64": "4.24.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.24.2", + "@rollup/rollup-linux-arm-musleabihf": "4.24.2", + "@rollup/rollup-linux-arm64-gnu": "4.24.2", + "@rollup/rollup-linux-arm64-musl": "4.24.2", + "@rollup/rollup-linux-powerpc64le-gnu": "4.24.2", + "@rollup/rollup-linux-riscv64-gnu": "4.24.2", + "@rollup/rollup-linux-s390x-gnu": "4.24.2", + "@rollup/rollup-linux-x64-gnu": "4.24.2", + "@rollup/rollup-linux-x64-musl": "4.24.2", + "@rollup/rollup-win32-arm64-msvc": "4.24.2", + "@rollup/rollup-win32-ia32-msvc": "4.24.2", + "@rollup/rollup-win32-x64-msvc": "4.24.2", + "fsevents": "~2.3.2" } }, "node_modules/run-parallel": { @@ -6178,6 +10394,31 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/safaridriver": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/safaridriver/-/safaridriver-0.1.2.tgz", + "integrity": "sha512-4R309+gWflJktzPXBQCobbWEHlzC4aK3a+Ov3tz2Ib2aBxiwd11phkdIBH1l0EO22x24CJMUQkpKFumRriCSRg==", + "dev": true, + "license": "MIT" + }, + "node_modules/safe-array-concat": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -6185,15 +10426,18 @@ "dev": true }, "node_modules/safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", "is-regex": "^1.1.4" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -6204,42 +10448,94 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, - "node_modules/samsam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz", - "integrity": "sha512-1HwIYD/8UlOtFS3QO3w7ey+SdSDFE4HRNLZoZRYVQefrOY3l17epswImeB1ijgJFQJodIaHcwkp3r/myBjFVbg==", - "deprecated": "This package has been deprecated in favour of @sinonjs/samsam", - "dev": true - }, - "node_modules/secure-compare": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", - "integrity": "sha1-8aAymzCLIh+uN7mXTz1XjQypmeM=", - "dev": true - }, "node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, "bin": { - "semver": "bin/semver" + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serialize-error": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-11.0.3.tgz", + "integrity": "sha512-2G2y++21dhj2R7iHAdd0FIzjGwuKZld+7Pl/bTU6YIkrC2ZMbVUjm+luj6A6V34Rv9XfKJDKpTWu9W4Gse1D9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^2.12.2" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serialize-error/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/serialize-javascript": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", - "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, "dependencies": { "randombytes": "^2.1.0" } }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true, + "license": "MIT" }, "node_modules/setprototypeof": { "version": "1.2.0", @@ -6269,136 +10565,134 @@ } }, "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "dev": true, "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, "node_modules/sinon": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-4.3.0.tgz", - "integrity": "sha512-pmf05hFgEZUS52AGJcsVjOjqAyJW2yo14cOwVYvzCyw7+inv06YXkLyW75WG6X6p951lzkoKh51L2sNbR9CDvw==", + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-18.0.1.tgz", + "integrity": "sha512-a2N2TDY1uGviajJ6r4D1CyRAkzE9NNVlYOV1wX5xQDuAk0ONgzgRl0EjCQuRCPxOwp13ghsMwt9Gdldujs39qw==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@sinonjs/formatio": "^2.0.0", - "diff": "^3.1.0", - "lodash.get": "^4.4.2", - "lolex": "^2.2.0", - "nise": "^1.2.0", - "supports-color": "^5.1.0", - "type-detect": "^4.0.5" + "@sinonjs/commons": "^3.0.1", + "@sinonjs/fake-timers": "11.2.2", + "@sinonjs/samsam": "^8.0.0", + "diff": "^5.2.0", + "nise": "^6.0.0", + "supports-color": "^7" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" } }, - "node_modules/sinon/node_modules/supports-color": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", - "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dev": true, + "license": "MIT", "dependencies": { - "has-flag": "^3.0.0" + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/socket.io": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.0.tgz", - "integrity": "sha512-b65bp6INPk/BMMrIgVvX12x3Q+NqlGqSlTuvKQWt0BUJ3Hyy3JangBl7fEoWZTXbOKlCqNPbQ6MbWgok/km28w==", + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "dev": true, - "dependencies": { - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "debug": "~4.3.2", - "engine.io": "~6.4.0", - "socket.io-adapter": "~2.5.2", - "socket.io-parser": "~4.2.1" - }, + "license": "MIT", "engines": { - "node": ">=10.0.0" + "node": ">= 6.0.0", + "npm": ">= 3.0.0" } }, - "node_modules/socket.io-adapter": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", - "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", - "dev": true, - "dependencies": { - "ws": "~8.11.0" - } - }, - "node_modules/socket.io-parser": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", - "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", - "dev": true, - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/socket.io-parser/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socket.io-parser/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "node_modules/smob": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/smob/-/smob-1.5.0.tgz", + "integrity": "sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==", "dev": true }, - "node_modules/socket.io/node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "node_modules/socks": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">= 10.0.0", + "npm": ">= 3.0.0" } }, - "node_modules/socket.io/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "node_modules/socks-proxy-agent": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz", + "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.1", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/socks-proxy-agent/node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, "node_modules/source-map-support": { "version": "0.5.21", @@ -6410,84 +10704,59 @@ "source-map": "^0.6.0" } }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/spacetrim": { + "version": "0.11.59", + "resolved": "https://registry.npmjs.org/spacetrim/-/spacetrim-0.11.59.tgz", + "integrity": "sha512-lLYsktklSRKprreOm7NXReW8YiX2VBjbgmXYEziOoGf/qsJqAEACaDvoTtUOycwjpaSh+bT8eu0KrJn7UNxiCg==", "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "dev": true - }, - "node_modules/spawn-wrap": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.3.tgz", - "integrity": "sha512-IgB8md0QW/+tWqcavuFgKYR/qIRvJkRLPJDFaoXtLLUaVcCDK0+HeFTkmQHj3eprcYhc+gOl0aEA1w7qZlYezw==", - "dev": true, - "dependencies": { - "foreground-child": "^1.5.6", - "mkdirp": "^0.5.0", - "os-homedir": "^1.0.1", - "rimraf": "^2.6.2", - "signal-exit": "^3.0.2", - "which": "^1.3.0" - } - }, - "node_modules/spawn-wrap/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/spawn-wrap/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } + "funding": [ + { + "type": "individual", + "url": "https://buymeacoffee.com/hejny" + }, + { + "type": "github", + "url": "https://github.com/hejny/spacetrim/blob/main/README.md#%EF%B8%8F-contributing" + } + ], + "license": "Apache-2.0" }, "node_modules/spdx-correct": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", - "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", "dev": true, "dependencies": { - "spdx-license-ids": "^1.0.2" + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" } }, - "node_modules/spdx-expression-parse": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz", - "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=", + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", "dev": true }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, "node_modules/spdx-license-ids": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz", - "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=", + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz", + "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==", "dev": true }, "node_modules/split": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", - "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", + "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==", "dev": true, "dependencies": { "through": "2" @@ -6496,145 +10765,235 @@ "node": "*" } }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", "dev": true, "engines": { "node": ">= 0.6" } }, + "node_modules/stop-iteration-iterator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", + "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", + "dev": true, + "dependencies": { + "internal-slot": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/stream-combiner": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", - "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", + "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==", "dev": true, "dependencies": { "duplexer": "~0.1.1" } }, - "node_modules/streamroller": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.0.2.tgz", - "integrity": "sha512-ur6y5S5dopOaRXBuRIZ1u6GC5bcEXHRZKgfBjfCglMhmIf+roVCECjvkEYzNQOXIN2/JPnkMPW/8B3CZoKaEPA==", + "node_modules/streamx": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.20.1.tgz", + "integrity": "sha512-uTa0mU6WUC65iUvzKH4X9hEdvSW7rbPxPtwfWiLMSj3qTdQbAiUboZTxauKfpFuGIGa1C2BYijZ7wgdUXICJhA==", "dev": true, + "license": "MIT", "dependencies": { - "date-format": "^4.0.3", - "debug": "^4.1.1", - "fs-extra": "^10.0.0" + "fast-fifo": "^1.3.2", + "queue-tick": "^1.0.1", + "text-decoder": "^1.1.0" }, - "engines": { - "node": ">=8.0" + "optionalDependencies": { + "bare-events": "^2.2.0" } }, - "node_modules/streamroller/node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" } - } - }, - "node_modules/streamroller/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + ], + "license": "MIT" }, "node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/string-width/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/string-width/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-regex": "^4.1.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/string.prototype.matchall": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", - "integrity": "sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==", + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string.prototype.includes": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.0.tgz", + "integrity": "sha512-E34CkBgyeqNDcrbU76cDjL5JLcVrtSdYq0MEh/B10r17pRP4ciHLwTgnuLV8Ay6cgEMLkcBkFCKyFZ43YldYzg==", "dev": true, "peer": true, "dependencies": { - "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.19.1", - "get-intrinsic": "^1.1.1", + "es-abstract": "^1.17.5" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", + "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.4.1", - "side-channel": "^1.0.4" + "internal-slot": "^1.0.7", + "regexp.prototype.flags": "^1.5.2", + "set-function-name": "^2.0.2", + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "peer": true, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimend": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", - "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimstart": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", - "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6652,6 +11011,20 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -6661,6 +11034,28 @@ "node": ">=4" } }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -6673,6 +11068,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "dev": true, + "license": "MIT" + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -6685,15 +11087,6 @@ "node": ">=8" } }, - "node_modules/supports-color/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -6706,16 +11099,76 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/table-layout": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-4.1.1.tgz", + "integrity": "sha512-iK5/YhZxq5GO5z8wb0bY1317uDF3Zjpha0QFFLA8/trAoiLbQD0HUbMesEaxyzUgDxi2QlcbM8IvqOlEjgoXBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^6.2.2", + "wordwrapjs": "^5.1.0" + }, + "engines": { + "node": ">=12.17" + } + }, + "node_modules/table-layout/node_modules/array-back": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", + "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.17" + } + }, "node_modules/taffydb": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", "integrity": "sha512-y3JaeRSplks6NYQuCOj3ZFMO3j60rTwbuKCvZxsAraGYH2epusatvZ0baZYA01WsGqJBq/Dl6vOrMUJqyMj8kA==", "dev": true }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar-fs": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.6.tgz", + "integrity": "sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^2.1.1", + "bare-path": "^2.1.0" + } + }, + "node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, "node_modules/temp-fs": { "version": "0.9.9", "resolved": "https://registry.npmjs.org/temp-fs/-/temp-fs-0.9.9.tgz", - "integrity": "sha1-gHFzBDeHByDpQxUy/igUNk+IA9c=", + "integrity": "sha512-WfecDCR1xC9b0nsrzSaxPf3ZuWeWLUWblW4vlDQAa1biQaKHiImHnJfeQocQe/hXKMcolRzgkcVX/7kK4zoWbw==", "dev": true, "dependencies": { "rimraf": "~2.5.2" @@ -6724,10 +11177,54 @@ "node": ">=0.8.0" } }, + "node_modules/temp-fs/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/temp-fs/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/temp-fs/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/temp-fs/node_modules/rimraf": { "version": "2.5.4", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz", - "integrity": "sha1-loAAk8vxoMhr2VtGJUZ1NcKd+gQ=", + "integrity": "sha512-Lw7SHMjssciQb/rRz7JyPIy9+bbUshEucPoLRvWqy09vC5zQixl8Uet+Zl+SROBB/JMWHJRdCk1qdxNWHNMvlQ==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, "dependencies": { "glob": "^7.0.5" @@ -6737,13 +11234,13 @@ } }, "node_modules/terser": { - "version": "5.16.4", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.4.tgz", - "integrity": "sha512-5yEGuZ3DZradbogeYQ1NaGz7rXVBDWujWlx1PT8efXO6Txn+eWbfKqB2bTDVmFXmePFkoLU6XI8UektMIEA0ug==", + "version": "5.31.6", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.6.tgz", + "integrity": "sha512-PQ4DAriWzKj+qgehQ7LK5bQqCFNMmlhjR2PFFLuqGCpuCAauxemVBWwWOxo3UIwWQx8+Pr61Df++r76wDmkQBg==", "dev": true, "dependencies": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -6761,187 +11258,105 @@ "dev": true }, "node_modules/test-exclude": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", - "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, "dependencies": { - "glob": "^7.1.3", - "minimatch": "^3.0.4", - "read-pkg-up": "^4.0.0", - "require-main-filename": "^2.0.0" + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" }, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/test-exclude/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/test-exclude/node_modules/load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": ">=4" - } - }, - "node_modules/test-exclude/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/test-exclude/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" + "node": "*" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/test-exclude/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { - "p-limit": "^2.0.0" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=6" + "node": "*" } }, - "node_modules/test-exclude/node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/test-exclude/node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "node_modules/text-decoder": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.0.tgz", + "integrity": "sha512-n1yg1mOj9DNpk3NeZOx7T6jchTbyJS3i3cucbNN6FcdPriMZx7NsgrGpWWdWZZGxD7ES1XB+3uoqHMgOKaN+fg==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/test-exclude/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/test-exclude/node_modules/path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "dependencies": { - "pify": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/test-exclude/node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/test-exclude/node_modules/read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "dev": true, - "dependencies": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/test-exclude/node_modules/read-pkg-up": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", - "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", - "dev": true, - "dependencies": { - "find-up": "^3.0.0", - "read-pkg": "^3.0.0" - }, - "engines": { - "node": ">=6" + "b4a": "^1.6.4" } }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", "dev": true, "engines": { "node": ">=4" } }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -6951,18 +11366,142 @@ "node": ">=0.6" } }, + "node_modules/tr46": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tr46/node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/tsconfig-paths": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", - "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", "dev": true, "dependencies": { "@types/json5": "^0.0.29", - "json5": "^1.0.1", + "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, + "node_modules/tslib": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", + "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==", + "dev": true, + "license": "0BSD" + }, + "node_modules/tsscmp": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", + "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.x" + } + }, + "node_modules/tsx": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz", + "integrity": "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.23.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -6976,9 +11515,9 @@ } }, "node_modules/type-detect": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.5.tgz", - "integrity": "sha512-N9IvkQslUGYGC24RkJk1ba99foK6TkwC2FHAEBlQFBP0RxQZS8ZpJuAZcwiY/w9ZJHFQb1aOXBI60OdxhTrwEQ==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true, "engines": { "node": ">=4" @@ -7009,36 +11548,108 @@ "node": ">= 0.6" } }, - "node_modules/typescript": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.2.tgz", - "integrity": "sha512-thGloWsGH3SOxv1SoY7QojKi0tc+8FnOmiarEGMbd/lar7QOEd3hvlx3Fp5y6FlDUGl9L+pd4n2e+oToGMmhRQ==", + "node_modules/typed-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-query-selector": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz", + "integrity": "sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==", + "dev": true, + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, - "node_modules/ua-parser-js": { - "version": "0.7.33", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.33.tgz", - "integrity": "sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw==", + "node_modules/typical": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", + "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/ua-parser-js" - }, - { - "type": "paypal", - "url": "https://paypal.me/faisalman" - } - ], + "license": "MIT", "engines": { - "node": "*" + "node": ">=8" } }, "node_modules/uc.micro": { @@ -7062,37 +11673,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, "node_modules/underscore": { - "version": "1.13.6", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", - "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", + "version": "1.13.7", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz", + "integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==", "dev": true }, - "node_modules/union": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", - "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", - "dev": true, - "dependencies": { - "qs": "^6.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "dev": true, "engines": { "node": ">= 0.8" @@ -7107,39 +11714,67 @@ "punycode": "^2.1.0" } }, - "node_modules/url-join": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", - "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", - "dev": true - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "node_modules/uri-js/node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "engines": { - "node": ">= 0.4.0" + "node": ">=6" } }, - "node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "node_modules/urlpattern-polyfill": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", + "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", "dev": true, - "bin": { - "uuid": "bin/uuid" + "license": "MIT" + }, + "node_modules/userhome": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/userhome/-/userhome-1.0.1.tgz", + "integrity": "sha512-5cnLm4gseXjAclKowC4IjByaGsjtAoV6PrOQOljplNB54ReUYJP8HdAFq2muHinSDAh09PPX/uXDPfdxRHvuSA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" } }, "node_modules/validate-npm-package-license": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", - "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, "dependencies": { - "spdx-correct": "~1.0.0", - "spdx-expression-parse": "~1.0.0" + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" } }, "node_modules/vary": { @@ -7151,58 +11786,334 @@ "node": ">= 0.8" } }, - "node_modules/void-elements": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", - "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", + "node_modules/wait-port": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/wait-port/-/wait-port-1.1.0.tgz", + "integrity": "sha512-3e04qkoN3LxTMLakdqeWth8nih8usyg+sf1Bgdf9wwUkp05iuK1eSY/QpLvscT/+F/gA89+LpUmmgBtesbqI2Q==", "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "commander": "^9.3.0", + "debug": "^4.3.4" + }, + "bin": { + "wait-port": "bin/wait-port.js" + }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + } + }, + "node_modules/wait-port/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || >=14" } }, "node_modules/web-streams-polyfill": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz", - "integrity": "sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0.tgz", + "integrity": "sha512-0zJXHRAYEjM2tUfZ2DiSOHAa2aw1tisnnhU3ufD57R8iefL+DcdJyRBRyJpG+NUimDgbTI/lH+gAE1PAvV3Cgw==", "dev": true, "engines": { "node": ">= 8" } }, - "node_modules/whatwg-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", - "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "node_modules/webdriver": { + "version": "8.40.6", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.40.6.tgz", + "integrity": "sha512-jkslwUvOmqhFfc1E21Tz48NgYD8ykiR+09iWZlVLtx3P43k4jOfS+CfasvQ+6hJiVck+N5dXjYfg6zDjpkIFRw==", "dev": true, + "license": "MIT", "dependencies": { - "iconv-lite": "0.6.3" + "@types/node": "^22.2.0", + "@types/ws": "^8.5.3", + "@wdio/config": "8.40.6", + "@wdio/logger": "8.38.0", + "@wdio/protocols": "8.40.3", + "@wdio/types": "8.40.6", + "@wdio/utils": "8.40.6", + "deepmerge-ts": "^5.1.0", + "got": "^12.6.1", + "ky": "^0.33.0", + "ws": "^8.8.0" }, + "engines": { + "node": "^16.13 || >=18" + } + }, + "node_modules/webdriver/node_modules/@types/ws": { + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", + "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/webdriverio": { + "version": "8.40.6", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.40.6.tgz", + "integrity": "sha512-hMFYRjVU5Nnk2e9Mi8kDx/IVFMWGaVyDCDpv/SeXXCP17DT9jAZtOWlwGhRaLVikN5JYYuHavHyatVa7gj6QTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "^22.2.0", + "@wdio/config": "8.40.6", + "@wdio/logger": "8.38.0", + "@wdio/protocols": "8.40.3", + "@wdio/repl": "8.40.3", + "@wdio/types": "8.40.6", + "@wdio/utils": "8.40.6", + "archiver": "^7.0.0", + "aria-query": "^5.0.0", + "css-shorthand-properties": "^1.1.1", + "css-value": "^0.0.1", + "devtools-protocol": "^0.0.1359167", + "grapheme-splitter": "^1.0.2", + "import-meta-resolve": "^4.0.0", + "is-plain-obj": "^4.1.0", + "jszip": "^3.10.1", + "lodash.clonedeep": "^4.5.0", + "lodash.zip": "^4.2.0", + "minimatch": "^9.0.0", + "puppeteer-core": "^21.11.0", + "query-selector-shadow-dom": "^1.0.0", + "resq": "^1.9.1", + "rgb2hex": "0.2.5", + "serialize-error": "^11.0.1", + "webdriver": "8.40.6" + }, + "engines": { + "node": "^16.13 || >=18" + }, + "peerDependencies": { + "devtools": "^8.14.0" + }, + "peerDependenciesMeta": { + "devtools": { + "optional": true + } + } + }, + "node_modules/webdriverio/node_modules/@puppeteer/browsers": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.9.1.tgz", + "integrity": "sha512-PuvK6xZzGhKPvlx3fpfdM2kYY3P/hB1URtK8wA7XUJ6prn6pp22zvJHu48th0SGcHL9SutbPHrFuQgfXTFobWA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "4.3.4", + "extract-zip": "2.0.1", + "progress": "2.0.3", + "proxy-agent": "6.3.1", + "tar-fs": "3.0.4", + "unbzip2-stream": "1.4.3", + "yargs": "17.7.2" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" + }, + "engines": { + "node": ">=16.3.0" + } + }, + "node_modules/webdriverio/node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/webdriverio/node_modules/chromium-bidi": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.5.8.tgz", + "integrity": "sha512-blqh+1cEQbHBKmok3rVJkBlBxt9beKBgOsxbFgs7UJcoVbbeZ+K7+6liAsjgpc8l1Xd55cQUy14fXZdGSb4zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "mitt": "3.0.1", + "urlpattern-polyfill": "10.0.0" + }, + "peerDependencies": { + "devtools-protocol": "*" + } + }, + "node_modules/webdriverio/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/webdriverio/node_modules/devtools-protocol": { + "version": "0.0.1359167", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1359167.tgz", + "integrity": "sha512-f/9PeTaSH3weS/WAwrQb5/s9R3KMOeTGe+Jkhg5952yInub7iDPjdlzRdrDgpLZfxHbTrBuG9aUkAMM+ocVkXQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/webdriverio/node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/webdriverio/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "license": "ISC", "engines": { "node": ">=12" } }, - "node_modules/whatwg-encoding/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "node_modules/webdriverio/node_modules/proxy-agent": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.1.tgz", + "integrity": "sha512-Rb5RVBy1iyqOtNl15Cw/llpeLH8bsb37gM1FUfKQ+Wck6xHlbAhWGUFiTRHtkjqGTA5pSHz6+0hrPW/oECihPQ==", "dev": true, + "license": "MIT", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.1", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.2" }, "engines": { - "node": ">=0.10.0" + "node": ">= 14" + } + }, + "node_modules/webdriverio/node_modules/puppeteer-core": { + "version": "21.11.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-21.11.0.tgz", + "integrity": "sha512-ArbnyA3U5SGHokEvkfWjW+O8hOxV1RSJxOgriX/3A4xZRqixt9ZFHD0yPgZQF05Qj0oAqi8H/7stDorjoHY90Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@puppeteer/browsers": "1.9.1", + "chromium-bidi": "0.5.8", + "cross-fetch": "4.0.0", + "debug": "4.3.4", + "devtools-protocol": "0.0.1232444", + "ws": "8.16.0" + }, + "engines": { + "node": ">=16.13.2" + } + }, + "node_modules/webdriverio/node_modules/puppeteer-core/node_modules/devtools-protocol": { + "version": "0.0.1232444", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1232444.tgz", + "integrity": "sha512-pM27vqEfxSxRkTMnF+XCmxSEb6duO5R+t8A9DEEJgy4Wz2RVanje2mmj99B6A3zv2r/qGfYlOvYznUhuokizmg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/webdriverio/node_modules/tar-fs": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", + "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + } + }, + "node_modules/webdriverio/node_modules/ws": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz", + "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" } }, "node_modules/which": { - "version": "1.2.14", - "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz", - "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "dependencies": { "isexe": "^2.0.0" }, "bin": { - "which": "bin/which" + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, "node_modules/which-boxed-primitive": { @@ -7221,53 +12132,68 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "node_modules/wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "node_modules/which-builtin-type": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.4.tgz", + "integrity": "sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==", "dev": true, + "peer": true, "dependencies": { - "string-width": "^1.0.2 || 2" - } - }, - "node_modules/wide-align/node_modules/ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/wide-align/node_modules/string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.0.5", + "is-finalizationregistry": "^1.0.2", + "is-generator-function": "^1.0.10", + "is-regex": "^1.1.4", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.15" }, "engines": { - "node": ">=4" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/wide-align/node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, "dependencies": { - "ansi-regex": "^3.0.0" + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" }, "engines": { - "node": ">=4" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/word-wrap": { @@ -7279,76 +12205,76 @@ "node": ">=0.10.0" } }, + "node_modules/wordwrapjs": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-5.1.0.tgz", + "integrity": "sha512-JNjcULU2e4KJwUNv6CHgI46UvDGitb6dGryHajXTDiLgg1/RiGoPSDw4kZfYnwGtEXf2ZMeIewDQgFGzkCB2Sg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.17" + } + }, "node_modules/workerpool": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", - "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", "dev": true }, "node_modules/wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "dependencies": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, + "license": "MIT", "dependencies": { - "color-convert": "^1.9.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=4" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "dependencies": { - "ansi-regex": "^4.1.0" + "node": ">=10" }, - "engines": { - "node": ">=6" + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, "node_modules/ws": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { @@ -7366,43 +12292,39 @@ "dev": true }, "node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } }, "node_modules/yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "dependencies": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" } }, "node_modules/yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "engines": { + "node": ">=12" } }, "node_modules/yargs-unparser": { @@ -7420,100 +12342,46 @@ "node": ">=10" } }, - "node_modules/yargs-unparser/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yargs-unparser/node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yargs/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "dev": true, + "license": "MIT", "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" } }, - "node_modules/yargs/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "node_modules/yauzl/node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "dev": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, + "license": "MIT", "engines": { - "node": ">=6" + "node": "*" } }, - "node_modules/yargs/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/ylru": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ylru/-/ylru-1.4.0.tgz", + "integrity": "sha512-2OQsPNEmBCvXuFlIni/a+Rn+R2pHW9INm0BxXJ4hVDA8TirqMj+J/Rp9ItLatT/5pZqWwefVrTQcHpixsxnVlA==", "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, + "license": "MIT", "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 4.0.0" } }, - "node_modules/yargs/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true, "engines": { "node": ">=6" } }, - "node_modules/yargs/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -7525,5827 +12393,31 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } - } - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", - "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", - "dev": true, - "requires": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } }, - "@babel/generator": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", - "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", - "dev": true, - "requires": { - "@babel/types": "^7.23.6", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - } - }, - "@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "dev": true - }, - "@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dev": true, - "requires": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-string-parser": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", - "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true - }, - "@babel/highlight": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", - "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/parser": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.9.tgz", - "integrity": "sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==", - "dev": true - }, - "@babel/runtime": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.4.tgz", - "integrity": "sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA==", - "dev": true, - "peer": true, - "requires": { - "regenerator-runtime": "^0.13.4" - } - }, - "@babel/runtime-corejs3": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.19.4.tgz", - "integrity": "sha512-HzjQ8+dzdx7dmZy4DQ8KV8aHi/74AjEbBGTFutBmg/pd3dY5/q1sfuOGPTFGEytlQhWoeVXqcK5BwMgIkRkNDQ==", - "dev": true, - "peer": true, - "requires": { - "core-js-pure": "^3.25.1", - "regenerator-runtime": "^0.13.4" - } - }, - "@babel/template": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.23.9.tgz", - "integrity": "sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.23.5", - "@babel/parser": "^7.23.9", - "@babel/types": "^7.23.9" - } - }, - "@babel/traverse": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.9.tgz", - "integrity": "sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.9", - "@babel/types": "^7.23.9", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "@babel/types": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz", - "integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" - } - }, - "@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", - "dev": true - }, - "@eslint/eslintrc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", - "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.4.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true - }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true - }, - "@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true - }, - "@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" - } - }, - "@mattiasbuelens/web-streams-adapter": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@mattiasbuelens/web-streams-adapter/-/web-streams-adapter-0.1.0.tgz", - "integrity": "sha512-oV4PyZfwJNtmFWhvlJLqYIX1Nn22ML8FZpS16ZUKv0hg7414xV1fjsGqxQzLT2dyK92TKxsJSwMOd7VNHAtPmA==", - "dev": true - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@openpgp/asmcrypto.js": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@openpgp/asmcrypto.js/-/asmcrypto.js-2.3.2.tgz", - "integrity": "sha512-CEb3I/Tqg+i5NgEnhYj3fi6XsT5JTuvYdwbMq+STGxlZ8uYSWmYFmVyz9vQgtNwCll/FbB6eR1opa4hoeHGceQ==", - "dev": true - }, - "@openpgp/elliptic": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@openpgp/elliptic/-/elliptic-6.5.1.tgz", - "integrity": "sha512-VR20QWndMXoZTAzCUqauDT4dLrHO4RTnyVV3szuRHllQSU/JZToLvWtFxpEQth4XWyqlxHPwq7tljE5V97+n1g==", - "dev": true, - "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" - } - }, - "@openpgp/jsdoc": { - "version": "3.6.11", - "resolved": "https://registry.npmjs.org/@openpgp/jsdoc/-/jsdoc-3.6.11.tgz", - "integrity": "sha512-mwvKQrW9raTU4CM3Oa93deRPh3TknFL0PUaGJWXwk3Inqf5nT8x4Z867ctqKYPekqw9FdDGyTaGb4rkbyPl9fA==", - "dev": true, - "requires": { - "@babel/parser": "^7.9.4", - "@types/markdown-it": "^12.2.3", - "bluebird": "^3.7.2", - "catharsis": "^0.9.0", - "escape-string-regexp": "^2.0.0", - "js2xmlparser": "^4.0.2", - "klaw": "^3.0.0", - "markdown-it": "^12.3.2", - "markdown-it-anchor": "^8.4.1", - "marked": "^4.0.10", - "mkdirp": "^1.0.4", - "requizzle": "^0.2.3", - "strip-json-comments": "^3.1.0", - "taffydb": "2.6.2", - "underscore": "~1.13.2" - }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - } - } - }, - "@openpgp/pako": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@openpgp/pako/-/pako-1.0.12.tgz", - "integrity": "sha512-r3+UotSXn4j2snQIuIYjsvA9xzx/5hEPi4vAqHhjhtQ+Q/XyLxYwfJpeIAdJvud6dKp57h48lDQpMkGda4MBQw==", - "dev": true - }, - "@openpgp/seek-bzip": { - "version": "1.0.5-git", - "resolved": "https://registry.npmjs.org/@openpgp/seek-bzip/-/seek-bzip-1.0.5-git.tgz", - "integrity": "sha512-1493w5yzXmAss9GEbNPYwX9UjROLfCTB8vjiTlT/HNzSH9b1FL4kJFH5iBV/+v6Ur5wlbCatGHinvqPeY0BwCw==", - "dev": true, - "requires": { - "commander": "~2.8.1" - } - }, - "@openpgp/tweetnacl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@openpgp/tweetnacl/-/tweetnacl-1.0.3.tgz", - "integrity": "sha512-KGXNhU/mRg+uTsLGva55V340jwbX2pC8LndjOVI2oQ8vewPVTS2KnDOIXQ8O6KyT/c9Qy16KUQ5mwewe72m1Yw==", - "dev": true - }, - "@openpgp/web-stream-tools": { - "version": "0.0.11-patch-1", - "resolved": "https://registry.npmjs.org/@openpgp/web-stream-tools/-/web-stream-tools-0.0.11-patch-1.tgz", - "integrity": "sha512-sZkx4FsHGFPcGrEBmBLvg0PcFBgR7KWe+NXo3SI/e+gpVoK3rPzPgv4TpI3UFKiXrohaJyY/klf24tNbJCutBA==", - "dev": true, - "requires": { - "@mattiasbuelens/web-streams-adapter": "~0.1.0", - "web-streams-polyfill": "~3.0.3" - }, - "dependencies": { - "web-streams-polyfill": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.0.3.tgz", - "integrity": "sha512-d2H/t0eqRNM4w2WvmTdoeIvzAUSpK7JmATB8Nr2lb7nQ9BTIJVjbQ/TRFVEh2gUH1HwclPdoPtfMoFfetXaZnA==", - "dev": true - } - } - }, - "@rollup/plugin-commonjs": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-11.1.0.tgz", - "integrity": "sha512-Ycr12N3ZPN96Fw2STurD21jMqzKwL9QuFhms3SD7KKRK7oaXUsBU9Zt0jL/rOPHiPYisI21/rXGO3jr9BnLHUA==", - "dev": true, - "requires": { - "@rollup/pluginutils": "^3.0.8", - "commondir": "^1.0.1", - "estree-walker": "^1.0.1", - "glob": "^7.1.2", - "is-reference": "^1.1.2", - "magic-string": "^0.25.2", - "resolve": "^1.11.0" - } - }, - "@rollup/plugin-node-resolve": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-7.1.3.tgz", - "integrity": "sha512-RxtSL3XmdTAE2byxekYLnx+98kEUOrPHF/KRVjLH+DEIHy6kjIw7YINQzn+NXiH/NTrQLAwYs0GWB+csWygA9Q==", - "dev": true, - "requires": { - "@rollup/pluginutils": "^3.0.8", - "@types/resolve": "0.0.8", - "builtin-modules": "^3.1.0", - "is-module": "^1.0.0", - "resolve": "^1.14.2" - }, - "dependencies": { - "builtin-modules": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz", - "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==", - "dev": true - } - } - }, - "@rollup/plugin-replace": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.3.2.tgz", - "integrity": "sha512-KEEL7V2tMNOsbAoNMKg91l1sNXBDoiP31GFlqXVOuV5691VQKzKBh91+OKKOG4uQWYqcFskcjFyh1d5YnZd0Zw==", - "dev": true, - "requires": { - "@rollup/pluginutils": "^3.0.8", - "magic-string": "^0.25.5" - } - }, - "@rollup/pluginutils": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.0.9.tgz", - "integrity": "sha512-TLZavlfPAZYI7v33wQh4mTP6zojne14yok3DNSLcjoG/Hirxfkonn6icP5rrNWRn8nZsirJBFFpijVOJzkUHDg==", - "dev": true, - "requires": { - "@types/estree": "0.0.39", - "estree-walker": "^1.0.1", - "micromatch": "^4.0.2" - } - }, - "@sinonjs/commons": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.7.1.tgz", - "integrity": "sha512-Debi3Baff1Qu1Unc3mjJ96MgpbwTn43S1+9yJ0llWygPwDNu2aaWBD6yc9y/Z8XDRNhx7U+u2UDg2OGQXkclUQ==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - }, - "dependencies": { - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - } - } - }, - "@sinonjs/formatio": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz", - "integrity": "sha512-ls6CAMA6/5gG+O/IdsBcblvnd8qcO/l1TYoNeAzp3wcISOxlPXQEus0mLcdwazEkWjaBdaJ3TaxmNgCLWwvWzg==", - "dev": true, - "requires": { - "samsam": "1.3.0" - } - }, - "@sinonjs/samsam": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.3.tgz", - "integrity": "sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.3.0", - "array-from": "^2.1.1", - "lodash": "^4.17.15" - } - }, - "@sinonjs/text-encoding": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", - "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", - "dev": true - }, - "@socket.io/component-emitter": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", - "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", - "dev": true - }, - "@types/chai": { - "version": "4.2.14", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.14.tgz", - "integrity": "sha512-G+ITQPXkwTrslfG5L/BksmbLUA0M1iybEsmCWPqzSxsRRhJZimBKJkoMi8fr/CPygPTj4zO5pJH7I2/cm9M7SQ==", - "dev": true - }, - "@types/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", - "dev": true - }, - "@types/cors": { - "version": "2.8.13", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", - "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/estree": { - "version": "0.0.39", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", - "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", - "dev": true - }, - "@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, - "@types/linkify-it": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.5.tgz", - "integrity": "sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==", - "dev": true - }, - "@types/markdown-it": { - "version": "12.2.3", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", - "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", - "dev": true, - "requires": { - "@types/linkify-it": "*", - "@types/mdurl": "*" - } - }, - "@types/mdurl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.5.tgz", - "integrity": "sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==", - "dev": true - }, - "@types/node": { - "version": "13.13.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.2.tgz", - "integrity": "sha512-LB2R1Oyhpg8gu4SON/mfforE525+Hi/M1ineICEDftqNVTyFg1aRIeGuTvXAoWHc4nbrFncWtJgMmoyRvuGh7A==", - "dev": true - }, - "@types/resolve": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.8.tgz", - "integrity": "sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true - }, - "accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dev": true, - "requires": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - } - }, - "acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", - "dev": true - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} - }, - "agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "dev": true, - "requires": { - "es6-promisify": "^5.0.0" - } - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - }, - "dependencies": { - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - } - } - }, - "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "append-transform": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", - "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", - "dev": true, - "requires": { - "default-require-extensions": "^2.0.0" - } - }, - "archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", - "dev": true - }, - "argparse": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", - "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "aria-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", - "dev": true, - "peer": true, - "requires": { - "@babel/runtime": "^7.10.2", - "@babel/runtime-corejs3": "^7.10.2" - } - }, - "array-from": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", - "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=", - "dev": true - }, - "array-includes": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", - "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", - "is-string": "^1.0.7" - } - }, - "array.prototype.flat": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", - "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" - } - }, - "array.prototype.flatmap": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", - "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" - } - }, - "asn1.js": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.0.0.tgz", - "integrity": "sha512-Y+FKviD0uyIWWo/xE0XkUl0x1allKFhzEVJ+//2Dgqpy+n+B77MlPNqvyk7Vx50M9XyVzjnRhDqJAEAsyivlbA==", - "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true - }, - "ast-types-flow": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", - "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==", - "dev": true, - "peer": true - }, - "async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", - "dev": true, - "requires": { - "lodash": "^4.17.14" - } - }, - "axe-core": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.4.3.tgz", - "integrity": "sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w==", - "dev": true, - "peer": true - }, - "axobject-query": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", - "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==", - "dev": true, - "peer": true - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", - "dev": true - }, - "basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", - "dev": true, - "requires": { - "safe-buffer": "5.1.2" - } - }, - "benchmark": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz", - "integrity": "sha1-CfPeMckWQl1JjMLuVloOvzwqVik=", - "dev": true, - "requires": { - "lodash": "^4.17.4", - "platform": "^1.3.3" - } - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, - "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" - }, - "body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", - "dev": true, - "requires": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.8.1", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.9.7", - "raw-body": "2.4.3", - "type-is": "~1.6.18" - }, - "dependencies": { - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==", - "dev": true - } - } - }, - "brace-expansion": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", - "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", - "dev": true - }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "browserstack": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.5.3.tgz", - "integrity": "sha512-AO+mECXsW4QcqC9bxwM29O7qWa7bJT94uBFzeb5brylIQwawuEziwq20dPYbins95GlWzOawgyDNdjYAo32EKg==", - "dev": true, - "requires": { - "https-proxy-agent": "^2.2.1" - } - }, - "browserstack-local": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/browserstack-local/-/browserstack-local-1.4.8.tgz", - "integrity": "sha512-s+mc3gTOJwELdLWi4qFVKtGwMbb5JWsR+JxKlMaJkRJxoZ0gg3WREgPxAN0bm6iU5+S4Bi0sz0oxBRZT8BiNsQ==", - "dev": true, - "requires": { - "https-proxy-agent": "^4.0.0", - "is-running": "^2.1.0", - "ps-tree": "=1.2.0", - "temp-fs": "^0.9.9" - }, - "dependencies": { - "agent-base": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz", - "integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==", - "dev": true - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "https-proxy-agent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz", - "integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==", - "dev": true, - "requires": { - "agent-base": "5", - "debug": "4" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "dev": true - }, - "bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true - }, - "caching-transform": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-3.0.2.tgz", - "integrity": "sha512-Mtgcv3lh3U0zRii/6qVgQODdPA4G3zhG+jtbCWj39RXuUFTMzH0vcdMtaJS1jPowd+It2Pqr6y3NJMQqOqCE2w==", - "dev": true, - "requires": { - "hasha": "^3.0.0", - "make-dir": "^2.0.0", - "package-hash": "^3.0.0", - "write-file-atomic": "^2.4.2" - }, - "dependencies": { - "write-file-atomic": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", - "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" - } - } - } - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "catharsis": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", - "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", - "dev": true, - "requires": { - "lodash": "^4.17.15" - } - }, - "chai": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", - "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", - "dev": true, - "requires": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^3.0.1", - "get-func-name": "^2.0.0", - "loupe": "^2.3.1", - "pathval": "^1.1.1", - "type-detect": "^4.0.5" - } - }, - "chai-as-promised": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", - "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", - "dev": true, - "requires": { - "check-error": "^1.0.2" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "dev": true - }, - "chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", - "dev": true, - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.3.1", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" - } - }, - "ci-info": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", - "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", - "dev": true - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "color-convert": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", - "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", - "dev": true, - "requires": { - "color-name": "^1.1.1" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "commander": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", - "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", - "dev": true, - "requires": { - "graceful-readlink": ">= 1.0.0" - } - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "confusing-browser-globals": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", - "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", - "dev": true - }, - "connect": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", - "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", - "dev": true, - "requires": { - "debug": "2.6.9", - "finalhandler": "1.1.2", - "parseurl": "~1.3.3", - "utils-merge": "1.0.1" - } - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "dev": true - }, - "convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "dev": true - }, - "core-js-pure": { - "version": "3.25.5", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.25.5.tgz", - "integrity": "sha512-oml3M22pHM+igfWHDfdLVq2ShWmjM2V4L+dQEBs0DWVIqEm9WHCwGAlZ6BmyBQGy5sFrJmcx+856D9lVKyGWYg==", - "dev": true, - "peer": true - }, - "cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "dev": true, - "requires": { - "object-assign": "^4", - "vary": "^1" - } - }, - "corser": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", - "integrity": "sha1-jtolLsqrWEDc2XXOuQ2TcMgZ/4c=", - "dev": true - }, - "cp-file": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/cp-file/-/cp-file-6.2.0.tgz", - "integrity": "sha512-fmvV4caBnofhPe8kOcitBwSn2f39QLjnAnGq3gO9dfd75mUytzKNZB1hde6QHunW2Rt+OwuBOMc3i1tNElbszA==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "make-dir": "^2.0.0", - "nested-error-stacks": "^2.0.0", - "pify": "^4.0.1", - "safe-buffer": "^5.0.1" - }, - "dependencies": { - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - } - } - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "dependencies": { - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "custom-event": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", - "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", - "dev": true - }, - "damerau-levenshtein": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", - "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", - "dev": true, - "peer": true - }, - "date-format": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.3.tgz", - "integrity": "sha512-7P3FyqDcfeznLZp2b+OMitV9Sz2lUnsT87WaTat9nVwqsBkTzPG3lPLNwW3en6F4pHUiWzr6vb8CLhjdK9bcxQ==", - "dev": true - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "dev": true, - "requires": { - "type-detect": "^4.0.0" - } - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "default-require-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", - "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", - "dev": true, - "requires": { - "strip-bom": "^3.0.0" - } - }, - "define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", - "dev": true, - "requires": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - } - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "dev": true - }, - "di": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", - "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", - "dev": true - }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true - }, - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "dom-serialize": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", - "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", - "dev": true, - "requires": { - "custom-event": "~1.0.0", - "ent": "~2.2.0", - "extend": "^3.0.0", - "void-elements": "^2.0.0" - } - }, - "duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "dev": true - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", - "dev": true - }, - "email-addresses": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.1.0.tgz", - "integrity": "sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg==", - "dev": true - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "dev": true - }, - "engine.io": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.2.tgz", - "integrity": "sha512-FKn/3oMiJjrOEOeUub2WCox6JhxBXq/Zn3fZOMCBxKnNYtsdKjxhl7yR3fZhM9PV+rdE75SU5SYMc+2PGzo+Tg==", - "dev": true, - "requires": { - "@types/cookie": "^0.4.1", - "@types/cors": "^2.8.12", - "@types/node": ">=10.0.0", - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.4.1", - "cors": "~2.8.5", - "debug": "~4.3.1", - "engine.io-parser": "~5.0.3", - "ws": "~8.11.0" - }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "engine.io-parser": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", - "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==", - "dev": true - }, - "ent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", - "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", - "dev": true - }, - "entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", - "dev": true - }, - "error-ex": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", - "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-abstract": { - "version": "1.20.4", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz", - "integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.1.3", - "get-symbol-description": "^1.0.0", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.2", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "safe-regex-test": "^1.0.0", - "string.prototype.trimend": "^1.0.5", - "string.prototype.trimstart": "^1.0.5", - "unbox-primitive": "^1.0.2" - } - }, - "es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true - }, - "es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", - "dev": true - }, - "es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", - "dev": true, - "requires": { - "es6-promise": "^4.0.3" - } - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "eslint": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.34.0.tgz", - "integrity": "sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg==", - "dev": true, - "requires": { - "@eslint/eslintrc": "^1.4.1", - "@humanwhocodes/config-array": "^0.11.8", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" - }, - "dependencies": { - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true - }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "eslint-config-airbnb": { - "version": "19.0.4", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz", - "integrity": "sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==", - "dev": true, - "requires": { - "eslint-config-airbnb-base": "^15.0.0", - "object.assign": "^4.1.2", - "object.entries": "^1.1.5" - } - }, - "eslint-config-airbnb-base": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", - "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", - "dev": true, - "requires": { - "confusing-browser-globals": "^1.0.10", - "object.assign": "^4.1.2", - "object.entries": "^1.1.5", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "eslint-import-resolver-node": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", - "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", - "dev": true, - "requires": { - "debug": "^3.2.7", - "is-core-module": "^2.11.0", - "resolve": "^1.22.1" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - } - } - }, - "eslint-module-utils": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", - "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", - "dev": true, - "requires": { - "debug": "^3.2.7" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - } - } - }, - "eslint-plugin-chai-friendly": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-chai-friendly/-/eslint-plugin-chai-friendly-0.7.2.tgz", - "integrity": "sha512-LOIfGx5sZZ5FwM1shr2GlYAWV9Omdi+1/3byuVagvQNoGUuU0iHhp7AfjA1uR+4dJ4Isfb4+FwBJgQajIw9iAg==", - "dev": true, - "requires": {} - }, - "eslint-plugin-import": { - "version": "2.27.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", - "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", - "dev": true, - "requires": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "array.prototype.flatmap": "^1.3.1", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.7", - "eslint-module-utils": "^2.7.4", - "has": "^1.0.3", - "is-core-module": "^2.11.0", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.values": "^1.1.6", - "resolve": "^1.22.1", - "semver": "^6.3.0", - "tsconfig-paths": "^3.14.1" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "eslint-plugin-jsx-a11y": { - "version": "6.6.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.1.tgz", - "integrity": "sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q==", - "dev": true, - "peer": true, - "requires": { - "@babel/runtime": "^7.18.9", - "aria-query": "^4.2.2", - "array-includes": "^3.1.5", - "ast-types-flow": "^0.0.7", - "axe-core": "^4.4.3", - "axobject-query": "^2.2.0", - "damerau-levenshtein": "^1.0.8", - "emoji-regex": "^9.2.2", - "has": "^1.0.3", - "jsx-ast-utils": "^3.3.2", - "language-tags": "^1.0.5", - "minimatch": "^3.1.2", - "semver": "^6.3.0" - }, - "dependencies": { - "emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "peer": true - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "peer": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "peer": true - } - } - }, - "eslint-plugin-react": { - "version": "7.31.10", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.31.10.tgz", - "integrity": "sha512-e4N/nc6AAlg4UKW/mXeYWd3R++qUano5/o+t+wnWxIf+bLsOaH3a4q74kX3nDjYym3VBN4HyO9nEn1GcAqgQOA==", - "dev": true, - "peer": true, - "requires": { - "array-includes": "^3.1.5", - "array.prototype.flatmap": "^1.3.0", - "doctrine": "^2.1.0", - "estraverse": "^5.3.0", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.5", - "object.fromentries": "^2.0.5", - "object.hasown": "^1.1.1", - "object.values": "^1.1.5", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.3", - "semver": "^6.3.0", - "string.prototype.matchall": "^4.0.7" - }, - "dependencies": { - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "peer": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "resolve": { - "version": "2.0.0-next.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", - "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", - "dev": true, - "peer": true, - "requires": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "peer": true - } - } - }, - "eslint-plugin-react-hooks": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", - "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", - "dev": true, - "peer": true, - "requires": {} - }, - "eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - } - } - }, - "esm": { - "version": "3.2.25", - "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", - "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", - "dev": true - }, - "espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", - "dev": true, - "requires": { - "acorn": "^8.8.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true - } - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.2.tgz", - "integrity": "sha512-JVSoLdTlTDkmjFmab7H/9SL9qGSyjElT3myyKp7krqjVFQCDLmj1QFaCLRFBszBKI0XVZaiiXvuPIX3ZwHe1Ng==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "estree-walker": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", - "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", - "dev": true - }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true - }, - "event-stream": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", - "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", - "dev": true, - "requires": { - "duplexer": "~0.1.1", - "from": "~0", - "map-stream": "~0.1.0", - "pause-stream": "0.0.11", - "split": "0.3", - "stream-combiner": "~0.0.4", - "through": "~2.3.1" - } - }, - "eventemitter3": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz", - "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==", - "dev": true - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "file-entry-cache": { + "node_modules/zip-stream": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz", + "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==", "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - }, + "license": "MIT", "dependencies": { - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - } - } - }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "dev": true, - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - } - }, - "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" + "archiver-utils": "^5.0.0", + "compress-commons": "^6.0.2", + "readable-stream": "^4.0.0" }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "requires": { - "find-up": "^3.0.0" - } - } + "engines": { + "node": ">= 14" } }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" } - }, - "flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", - "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", - "dev": true - }, - "follow-redirects": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", - "dev": true - }, - "foreground-child": { - "version": "1.5.6", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-1.5.6.tgz", - "integrity": "sha1-T9ca0t/elnibmApcCilZN8svXOk=", - "dev": true, - "requires": { - "cross-spawn": "^4", - "signal-exit": "^3.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", - "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "which": "^1.2.9" - } - }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - } - } - }, - "from": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", - "dev": true - }, - "fs-extra": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz", - "integrity": "sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "dependencies": { - "graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", - "dev": true - } - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" - } - }, - "functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-func-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", - "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", - "dev": true - }, - "get-intrinsic": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", - "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - } - }, - "get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - } - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "dependencies": { - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true - }, - "graceful-readlink": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", - "dev": true - }, - "grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true - }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.1" - } - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true - }, - "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, - "hash.js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", - "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.0" - } - }, - "hasha": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-3.0.0.tgz", - "integrity": "sha1-UqMvq4Vp1BymmmH/GiFPjrfIvTk=", - "dev": true, - "requires": { - "is-stream": "^1.0.1" - } - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dev": true, - "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "html-encoding-sniffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", - "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", - "dev": true, - "requires": { - "whatwg-encoding": "^2.0.0" - } - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "http-errors": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", - "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", - "dev": true, - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.1" - }, - "dependencies": { - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - } - } - }, - "http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "dev": true, - "requires": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - } - }, - "http-server": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz", - "integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==", - "dev": true, - "requires": { - "basic-auth": "^2.0.1", - "chalk": "^4.1.2", - "corser": "^2.0.1", - "he": "^1.2.0", - "html-encoding-sniffer": "^3.0.0", - "http-proxy": "^1.18.1", - "mime": "^1.6.0", - "minimist": "^1.2.6", - "opener": "^1.5.1", - "portfinder": "^1.0.28", - "secure-compare": "3.0.1", - "union": "~0.5.0", - "url-join": "^4.0.1" - }, - "dependencies": { - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true - } - } - }, - "https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", - "dev": true, - "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - } - } - }, - "ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "requires": { - "has-bigints": "^1.0.1" - } - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-builtin-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", - "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", - "dev": true, - "requires": { - "builtin-modules": "^1.0.0" - } - }, - "is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true - }, - "is-ci": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", - "dev": true, - "requires": { - "ci-info": "^3.2.0" - } - }, - "is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", - "dev": true - }, - "is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true - }, - "is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true - }, - "is-reference": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.1.4.tgz", - "integrity": "sha512-uJA/CDPO3Tao3GTrxYn6AwkM4nUPJiGGYu5+cB8qbC7WGFlrKZbiRo7SFKxUAEpFUfiHofWCXBUNhvYJMh+6zw==", - "dev": true, - "requires": { - "@types/estree": "0.0.39" - } - }, - "is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-running": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-running/-/is-running-2.1.0.tgz", - "integrity": "sha1-MKc/9cw4VOT8JUkICen1q/jeCeA=", - "dev": true - }, - "is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, - "is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, - "is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "requires": { - "is-docker": "^2.0.0" - } - }, - "isbinaryfile": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.8.tgz", - "integrity": "sha512-53h6XFniq77YdW+spoRrebh0mnmTxRPTlcuIArO57lmMdq4uBKFKaeTjnb92oYWrSn/LVL+LT+Hap2tFQj8V+w==", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", - "dev": true - }, - "istanbul-lib-hook": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz", - "integrity": "sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA==", - "dev": true, - "requires": { - "append-transform": "^1.0.0" - } - }, - "istanbul-lib-instrument": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", - "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", - "dev": true, - "requires": { - "@babel/generator": "^7.4.0", - "@babel/parser": "^7.4.3", - "@babel/template": "^7.4.0", - "@babel/traverse": "^7.4.3", - "@babel/types": "^7.4.0", - "istanbul-lib-coverage": "^2.0.5", - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "istanbul-lib-report": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", - "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "supports-color": "^6.1.0" - }, - "dependencies": { - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "istanbul-lib-source-maps": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", - "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "rimraf": "^2.6.3", - "source-map": "^0.6.1" - }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "istanbul-reports": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.7.tgz", - "integrity": "sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0" - } - }, - "jest-worker": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", - "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", - "dev": true, - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" - } - }, - "js-sdsl": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", - "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", - "dev": true - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "js2xmlparser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", - "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", - "dev": true, - "requires": { - "xmlcreate": "^2.0.4" - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, - "jsx-ast-utils": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", - "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==", - "dev": true, - "peer": true, - "requires": { - "array-includes": "^3.1.5", - "object.assign": "^4.1.3" - } - }, - "karma": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.1.tgz", - "integrity": "sha512-Cj57NKOskK7wtFWSlMvZf459iX+kpYIPXmkNUzP2WAFcA7nhr/ALn5R7sw3w+1udFDcpMx/tuB8d5amgm3ijaA==", - "dev": true, - "requires": { - "@colors/colors": "1.5.0", - "body-parser": "^1.19.0", - "braces": "^3.0.2", - "chokidar": "^3.5.1", - "connect": "^3.7.0", - "di": "^0.0.1", - "dom-serialize": "^2.2.1", - "glob": "^7.1.7", - "graceful-fs": "^4.2.6", - "http-proxy": "^1.18.1", - "isbinaryfile": "^4.0.8", - "lodash": "^4.17.21", - "log4js": "^6.4.1", - "mime": "^2.5.2", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.5", - "qjobs": "^1.2.0", - "range-parser": "^1.2.1", - "rimraf": "^3.0.2", - "socket.io": "^4.4.1", - "source-map": "^0.6.1", - "tmp": "^0.2.1", - "ua-parser-js": "^0.7.30", - "yargs": "^16.1.1" - }, - "dependencies": { - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "requires": { - "rimraf": "^3.0.0" - } - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - }, - "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true - } - } - }, - "karma-browserstack-launcher": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/karma-browserstack-launcher/-/karma-browserstack-launcher-1.6.0.tgz", - "integrity": "sha512-Y/UWPdHZkHIVH2To4GWHCTzmrsB6H7PBWy6pw+TWz5sr4HW2mcE+Uj6qWgoVNxvQU1Pfn5LQQzI6EQ65p8QbiQ==", - "dev": true, - "requires": { - "browserstack": "~1.5.1", - "browserstack-local": "^1.3.7", - "q": "~1.5.0" - } - }, - "karma-chrome-launcher": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.1.tgz", - "integrity": "sha512-hsIglcq1vtboGPAN+DGCISCFOxW+ZVnIqhDQcCMqqCp+4dmJ0Qpq5QAjkbA0X2L9Mi6OBkHi2Srrbmm7pUKkzQ==", - "dev": true, - "requires": { - "which": "^1.2.1" - } - }, - "karma-firefox-launcher": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/karma-firefox-launcher/-/karma-firefox-launcher-2.1.2.tgz", - "integrity": "sha512-VV9xDQU1QIboTrjtGVD4NCfzIH7n01ZXqy/qpBhnOeGVOkG5JYPEm8kuSd7psHE6WouZaQ9Ool92g8LFweSNMA==", - "dev": true, - "requires": { - "is-wsl": "^2.2.0", - "which": "^2.0.1" - }, - "dependencies": { - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "karma-mocha": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/karma-mocha/-/karma-mocha-2.0.1.tgz", - "integrity": "sha512-Tzd5HBjm8his2OA4bouAsATYEpZrp9vC7z5E5j4C5Of5Rrs1jY67RAwXNcVmd/Bnk1wgvQRou0zGVLey44G4tQ==", - "dev": true, - "requires": { - "minimist": "^1.2.3" - } - }, - "karma-mocha-reporter": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/karma-mocha-reporter/-/karma-mocha-reporter-2.2.5.tgz", - "integrity": "sha1-FRIAlejtgZGG5HoLAS8810GJVWA=", - "dev": true, - "requires": { - "chalk": "^2.1.0", - "log-symbols": "^2.1.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "karma-webkit-launcher": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/karma-webkit-launcher/-/karma-webkit-launcher-2.1.0.tgz", - "integrity": "sha512-S5eqhH0DIcuJFi27nC6eBxZ3MTrPnYybPthDU2Q8dfG0yFrXx8FqNDKSbRZsFFvAKJ55QVtYH1bbArd3ddI5Sg==", - "dev": true, - "requires": { - "is-ci": "^3.0.1", - "uuid": "^9.0.0" - }, - "dependencies": { - "uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", - "dev": true - } - } - }, - "klaw": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", - "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.9" - } - }, - "language-subtag-registry": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", - "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==", - "dev": true, - "peer": true - }, - "language-tags": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", - "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==", - "dev": true, - "peer": true, - "requires": { - "language-subtag-registry": "~0.3.2" - } - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "linkify-it": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", - "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", - "dev": true, - "requires": { - "uc.micro": "^1.0.1" - } - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", - "dev": true - }, - "lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", - "dev": true - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "log-symbols": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", - "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", - "dev": true, - "requires": { - "chalk": "^2.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "log4js": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.4.1.tgz", - "integrity": "sha512-iUiYnXqAmNKiIZ1XSAitQ4TmNs8CdZYTAWINARF3LjnsLN8tY5m0vRwd6uuWj/yNY0YHxeZodnbmxKFUOM2rMg==", - "dev": true, - "requires": { - "date-format": "^4.0.3", - "debug": "^4.3.3", - "flatted": "^3.2.4", - "rfdc": "^1.3.0", - "streamroller": "^3.0.2" - }, - "dependencies": { - "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "lolex": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.3.2.tgz", - "integrity": "sha512-A5pN2tkFj7H0dGIAM6MFvHKMJcPnjZsOMvR7ujCjfgW5TbV6H9vb1PgxLtHvjqNZTHsUolz+6/WEO0N1xNx2ng==", - "dev": true - }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, - "peer": true, - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "loupe": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", - "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", - "dev": true, - "requires": { - "get-func-name": "^2.0.0" - } - }, - "magic-string": { - "version": "0.25.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", - "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", - "dev": true, - "requires": { - "sourcemap-codec": "^1.4.4" - } - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "dependencies": { - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - } - } - }, - "map-stream": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", - "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", - "dev": true - }, - "markdown-it": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", - "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", - "dev": true, - "requires": { - "argparse": "^2.0.1", - "entities": "~2.1.0", - "linkify-it": "^3.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" - }, - "dependencies": { - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - } - } - }, - "markdown-it-anchor": { - "version": "8.6.7", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", - "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==", - "dev": true, - "requires": {} - }, - "marked": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", - "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", - "dev": true - }, - "mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", - "dev": true - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", - "dev": true - }, - "merge-source-map": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", - "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", - "dev": true, - "requires": { - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - } - }, - "mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true - }, - "mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", - "dev": true - }, - "mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", - "dev": true, - "requires": { - "mime-db": "1.51.0" - } - }, - "minimalistic-assert": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", - "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=" - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "mocha": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", - "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", - "dev": true, - "requires": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.1", - "debug": "4.3.1", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.1.6", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "4.0.0", - "log-symbols": "4.0.0", - "minimatch": "3.0.4", - "ms": "2.1.3", - "nanoid": "3.1.20", - "serialize-javascript": "5.0.1", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "which": "2.0.2", - "wide-align": "1.1.3", - "workerpool": "6.1.0", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "dependencies": { - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "dependencies": { - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "js-yaml": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", - "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "log-symbols": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", - "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", - "dev": true, - "requires": { - "chalk": "^4.0.0" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "serialize-javascript": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", - "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - }, - "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true - } - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "nanoid": { - "version": "3.1.20", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", - "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true - }, - "nested-error-stacks": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz", - "integrity": "sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==", - "dev": true - }, - "nise": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.3.tgz", - "integrity": "sha512-Ymbac/94xeIrMf59REBPOv0thr+CJVFMhrlAkW/gjCIE58BGQdCj0x7KRCb3yz+Ga2Rz3E9XXSvUyyxqqhjQAQ==", - "dev": true, - "requires": { - "@sinonjs/formatio": "^3.2.1", - "@sinonjs/text-encoding": "^0.7.1", - "just-extend": "^4.0.2", - "lolex": "^5.0.1", - "path-to-regexp": "^1.7.0" - }, - "dependencies": { - "@sinonjs/formatio": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.2.tgz", - "integrity": "sha512-B8SEsgd8gArBLMD6zpRw3juQ2FVSsmdd7qlevyDqzS9WTCtvF55/gAL+h6gue8ZvPYcdiPdvueM/qm//9XzyTQ==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1", - "@sinonjs/samsam": "^3.1.0" - } - }, - "just-extend": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.1.0.tgz", - "integrity": "sha512-ApcjaOdVTJ7y4r08xI5wIqpvwS48Q0PBG4DJROcEkH1f8MdAiNFyFxz3xoL0LWAVwjrwPYZdVHHxhRHcx/uGLA==", - "dev": true - }, - "lolex": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz", - "integrity": "sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - } - } - }, - "normalize-package-data": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "is-builtin-module": "^1.0.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "nyc": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-14.1.1.tgz", - "integrity": "sha512-OI0vm6ZGUnoGZv/tLdZ2esSVzDwUC88SNs+6JoSOMVxA+gKMB8Tk7jBwgemLx4O40lhhvZCVw1C+OYLOBOPXWw==", - "dev": true, - "requires": { - "archy": "^1.0.0", - "caching-transform": "^3.0.2", - "convert-source-map": "^1.6.0", - "cp-file": "^6.2.0", - "find-cache-dir": "^2.1.0", - "find-up": "^3.0.0", - "foreground-child": "^1.5.6", - "glob": "^7.1.3", - "istanbul-lib-coverage": "^2.0.5", - "istanbul-lib-hook": "^2.0.7", - "istanbul-lib-instrument": "^3.3.0", - "istanbul-lib-report": "^2.0.8", - "istanbul-lib-source-maps": "^3.0.6", - "istanbul-reports": "^2.2.4", - "js-yaml": "^3.13.1", - "make-dir": "^2.1.0", - "merge-source-map": "^1.1.0", - "resolve-from": "^4.0.0", - "rimraf": "^2.6.3", - "signal-exit": "^3.0.2", - "spawn-wrap": "^1.4.2", - "test-exclude": "^5.2.3", - "uuid": "^3.3.2", - "yargs": "^13.2.2", - "yargs-parser": "^13.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", - "dev": true - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - } - }, - "object.entries": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", - "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" - } - }, - "object.fromentries": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz", - "integrity": "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==", - "dev": true, - "peer": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" - } - }, - "object.hasown": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.1.tgz", - "integrity": "sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A==", - "dev": true, - "peer": true, - "requires": { - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" - } - }, - "object.values": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", - "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dev": true, - "requires": { - "ee-first": "1.1.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "opener": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.1.tgz", - "integrity": "sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==", - "dev": true - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "package-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-3.0.0.tgz", - "integrity": "sha512-lOtmukMDVvtkL84rJHI7dpTYq+0rli8N2wlnqUcBuDWCfVhRUfOmnR9SsoHFMLpACvEV60dX7rd0rFaYDZI+FA==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.15", - "hasha": "^3.0.0", - "lodash.flattendeep": "^4.4.0", - "release-zalgo": "^1.0.0" - }, - "dependencies": { - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - } - } - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dev": true - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "path-to-regexp": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", - "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", - "dev": true, - "requires": { - "isarray": "0.0.1" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - } - } - }, - "pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true - }, - "pause-stream": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", - "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", - "dev": true, - "requires": { - "through": "~2.3" - } - }, - "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", - "dev": true - }, - "platform": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", - "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==", - "dev": true - }, - "playwright": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.30.0.tgz", - "integrity": "sha512-ENbW5o75HYB3YhnMTKJLTErIBExrSlX2ZZ1C/FzmHjUYIfxj/UnI+DWpQr992m+OQVSg0rCExAOlRwB+x+yyIg==", - "dev": true, - "requires": { - "playwright-core": "1.30.0" - } - }, - "playwright-core": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.30.0.tgz", - "integrity": "sha512-7AnRmTCf+GVYhHbLJsGUtskWTE33SwMZkybJ0v6rqR1boxq2x36U7p1vDRV7HO2IwTZgmycracLxPEJI49wu4g==", - "dev": true - }, - "portfinder": { - "version": "1.0.28", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", - "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", - "dev": true, - "requires": { - "async": "^2.6.2", - "debug": "^3.1.1", - "mkdirp": "^0.5.5" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - } - } - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, - "peer": true, - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "ps-tree": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", - "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", - "dev": true, - "requires": { - "event-stream": "=3.3.4" - } - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, - "punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "dev": true - }, - "q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", - "dev": true - }, - "qjobs": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", - "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", - "dev": true - }, - "qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dev": true, - "requires": { - "side-channel": "^1.0.4" - } - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "dev": true - }, - "raw-body": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", - "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", - "dev": true, - "requires": { - "bytes": "3.1.2", - "http-errors": "1.8.1", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "dependencies": { - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - } - } - }, - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true, - "peer": true - }, - "readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", - "dev": true, - "peer": true - }, - "regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" - } - }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, - "release-zalgo": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", - "dev": true, - "requires": { - "es6-error": "^4.0.1" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", - "dev": true - }, - "requizzle": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz", - "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==", - "dev": true, - "requires": { - "lodash": "^4.17.14" - } - }, - "resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "dev": true, - "requires": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "rfdc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "rollup": { - "version": "2.38.5", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.38.5.tgz", - "integrity": "sha512-VoWt8DysFGDVRGWuHTqZzT02J0ASgjVq/hPs9QcBOGMd7B+jfTr/iqMVEyOi901rE3xq+Deq66GzIT1yt7sGwQ==", - "dev": true, - "requires": { - "fsevents": "~2.3.1" - } - }, - "rollup-plugin-terser": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", - "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "jest-worker": "^26.2.1", - "serialize-javascript": "^4.0.0", - "terser": "^5.0.0" - } - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "samsam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz", - "integrity": "sha512-1HwIYD/8UlOtFS3QO3w7ey+SdSDFE4HRNLZoZRYVQefrOY3l17epswImeB1ijgJFQJodIaHcwkp3r/myBjFVbg==", - "dev": true - }, - "secure-compare": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", - "integrity": "sha1-8aAymzCLIh+uN7mXTz1XjQypmeM=", - "dev": true - }, - "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true - }, - "serialize-javascript": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", - "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "sinon": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-4.3.0.tgz", - "integrity": "sha512-pmf05hFgEZUS52AGJcsVjOjqAyJW2yo14cOwVYvzCyw7+inv06YXkLyW75WG6X6p951lzkoKh51L2sNbR9CDvw==", - "dev": true, - "requires": { - "@sinonjs/formatio": "^2.0.0", - "diff": "^3.1.0", - "lodash.get": "^4.4.2", - "lolex": "^2.2.0", - "nise": "^1.2.0", - "supports-color": "^5.1.0", - "type-detect": "^4.0.5" - }, - "dependencies": { - "supports-color": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", - "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "socket.io": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.0.tgz", - "integrity": "sha512-b65bp6INPk/BMMrIgVvX12x3Q+NqlGqSlTuvKQWt0BUJ3Hyy3JangBl7fEoWZTXbOKlCqNPbQ6MbWgok/km28w==", - "dev": true, - "requires": { - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "debug": "~4.3.2", - "engine.io": "~6.4.0", - "socket.io-adapter": "~2.5.2", - "socket.io-parser": "~4.2.1" - }, - "dependencies": { - "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "socket.io-adapter": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", - "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", - "dev": true, - "requires": { - "ws": "~8.11.0" - } - }, - "socket.io-parser": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", - "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", - "dev": true, - "requires": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1" - }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "dev": true - }, - "spawn-wrap": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.3.tgz", - "integrity": "sha512-IgB8md0QW/+tWqcavuFgKYR/qIRvJkRLPJDFaoXtLLUaVcCDK0+HeFTkmQHj3eprcYhc+gOl0aEA1w7qZlYezw==", - "dev": true, - "requires": { - "foreground-child": "^1.5.6", - "mkdirp": "^0.5.0", - "os-homedir": "^1.0.1", - "rimraf": "^2.6.2", - "signal-exit": "^3.0.2", - "which": "^1.3.0" - }, - "dependencies": { - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "spdx-correct": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", - "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=", - "dev": true, - "requires": { - "spdx-license-ids": "^1.0.2" - } - }, - "spdx-expression-parse": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz", - "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=", - "dev": true - }, - "spdx-license-ids": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz", - "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=", - "dev": true - }, - "split": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", - "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", - "dev": true, - "requires": { - "through": "2" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "dev": true - }, - "stream-combiner": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", - "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", - "dev": true, - "requires": { - "duplexer": "~0.1.1" - } - }, - "streamroller": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.0.2.tgz", - "integrity": "sha512-ur6y5S5dopOaRXBuRIZ1u6GC5bcEXHRZKgfBjfCglMhmIf+roVCECjvkEYzNQOXIN2/JPnkMPW/8B3CZoKaEPA==", - "dev": true, - "requires": { - "date-format": "^4.0.3", - "debug": "^4.1.1", - "fs-extra": "^10.0.0" - }, - "dependencies": { - "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "string.prototype.matchall": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", - "integrity": "sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==", - "dev": true, - "peer": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1", - "get-intrinsic": "^1.1.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.4.1", - "side-channel": "^1.0.4" - } - }, - "string.prototype.trimend": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", - "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" - } - }, - "string.prototype.trimstart": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", - "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - } - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true - }, - "taffydb": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", - "integrity": "sha512-y3JaeRSplks6NYQuCOj3ZFMO3j60rTwbuKCvZxsAraGYH2epusatvZ0baZYA01WsGqJBq/Dl6vOrMUJqyMj8kA==", - "dev": true - }, - "temp-fs": { - "version": "0.9.9", - "resolved": "https://registry.npmjs.org/temp-fs/-/temp-fs-0.9.9.tgz", - "integrity": "sha1-gHFzBDeHByDpQxUy/igUNk+IA9c=", - "dev": true, - "requires": { - "rimraf": "~2.5.2" - }, - "dependencies": { - "rimraf": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz", - "integrity": "sha1-loAAk8vxoMhr2VtGJUZ1NcKd+gQ=", - "dev": true, - "requires": { - "glob": "^7.0.5" - } - } - } - }, - "terser": { - "version": "5.16.4", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.4.tgz", - "integrity": "sha512-5yEGuZ3DZradbogeYQ1NaGz7rXVBDWujWlx1PT8efXO6Txn+eWbfKqB2bTDVmFXmePFkoLU6XI8UektMIEA0ug==", - "dev": true, - "requires": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "dependencies": { - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - } - } - }, - "test-exclude": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", - "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", - "dev": true, - "requires": { - "glob": "^7.1.3", - "minimatch": "^3.0.4", - "read-pkg-up": "^4.0.0", - "require-main-filename": "^2.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "dev": true, - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - } - }, - "read-pkg-up": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", - "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", - "dev": true, - "requires": { - "find-up": "^3.0.0", - "read-pkg": "^3.0.0" - } - } - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - }, - "toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true - }, - "tsconfig-paths": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", - "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", - "dev": true, - "requires": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-detect": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.5.tgz", - "integrity": "sha512-N9IvkQslUGYGC24RkJk1ba99foK6TkwC2FHAEBlQFBP0RxQZS8ZpJuAZcwiY/w9ZJHFQb1aOXBI60OdxhTrwEQ==", - "dev": true - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dev": true, - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "typescript": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.2.tgz", - "integrity": "sha512-thGloWsGH3SOxv1SoY7QojKi0tc+8FnOmiarEGMbd/lar7QOEd3hvlx3Fp5y6FlDUGl9L+pd4n2e+oToGMmhRQ==", - "dev": true - }, - "ua-parser-js": { - "version": "0.7.33", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.33.tgz", - "integrity": "sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw==", - "dev": true - }, - "uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", - "dev": true - }, - "unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - } - }, - "underscore": { - "version": "1.13.6", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", - "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", - "dev": true - }, - "union": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", - "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", - "dev": true, - "requires": { - "qs": "^6.4.0" - } - }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "dev": true - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "url-join": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", - "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", - "dev": true - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "dev": true - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true - }, - "validate-npm-package-license": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", - "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", - "dev": true, - "requires": { - "spdx-correct": "~1.0.0", - "spdx-expression-parse": "~1.0.0" - } - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "dev": true - }, - "void-elements": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", - "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", - "dev": true - }, - "web-streams-polyfill": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz", - "integrity": "sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA==", - "dev": true - }, - "whatwg-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", - "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", - "dev": true, - "requires": { - "iconv-lite": "0.6.3" - }, - "dependencies": { - "iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - } - } - }, - "which": { - "version": "1.2.14", - "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz", - "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "requires": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "requires": { - "string-width": "^1.0.2 || 2" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true - }, - "workerpool": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", - "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", - "dev": true - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "ws": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", - "dev": true, - "requires": {} - }, - "xmlcreate": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", - "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", - "dev": true - }, - "y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - }, - "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - } - } - }, - "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, - "yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "requires": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "dependencies": { - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - }, - "decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true - } - } - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true } } } diff --git a/package.json b/package.json index d66004dc..7a3810ed 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,11 @@ { "name": "openpgp", "description": "OpenPGP.js is a Javascript implementation of the OpenPGP protocol. This is defined in RFC 4880.", - "version": "5.11.2", + "version": "6.0.0-beta.3.patch.1", "license": "LGPL-3.0+", "homepage": "https://openpgpjs.org/", "engines": { - "node": ">= 8.0.0" + "node": ">= 18.0.0" }, "keywords": [ "crypto", @@ -13,13 +13,26 @@ "gpg", "openpgp" ], - "main": "dist/node/openpgp.min.js", + "main": "dist/node/openpgp.min.cjs", "module": "dist/node/openpgp.min.mjs", "browser": { - "./dist/node/openpgp.min.js": "./dist/openpgp.min.js", + "./dist/node/openpgp.min.cjs": "./dist/openpgp.min.js", "./dist/node/openpgp.min.mjs": "./dist/openpgp.min.mjs" }, + "exports": { + ".": { + "types": "./openpgp.d.ts", + "import": "./dist/node/openpgp.mjs", + "require": "./dist/node/openpgp.min.cjs", + "browser": "./dist/openpgp.min.mjs" + }, + "./lightweight": { + "types": "./openpgp.d.ts", + "browser": "./dist/lightweight/openpgp.min.mjs" + } + }, "types": "openpgp.d.ts", + "type": "module", "directories": { "lib": "src" }, @@ -28,74 +41,76 @@ "lightweight/", "openpgp.d.ts" ], - "esm": { - "cjs": { - "dedefault": true - } - }, "scripts": { "build": "rollup --config", "build-test": "npm run build --build-only=test", "prepare": "npm run build", - "test": "mocha --require esm --timeout 120000 test/unittests.js", - "test-type-definitions": "tsc test/typescript/definitions.ts && node test/typescript/definitions.js", + "test": "mocha --timeout 120000 test/unittests.js", + "test-type-definitions": "tsx test/typescript/definitions.ts", "benchmark-time": "node test/benchmarks/time.js", - "benchmark-memory-usage": "node --require esm test/benchmarks/memory_usage.js", - "start": "http-server", + "benchmark-memory-usage": "node test/benchmarks/memory_usage.js", "prebrowsertest": "npm run build-test", - "browsertest": "npm start -- -o test/unittests.html", - "test-browser": "karma start test/karma.conf.js", - "test-browserstack": "karma start test/karma.conf.js --browsers bs_safari_latest,bs_ios_14,bs_safari_13_1", - "coverage": "nyc npm test", + "browsertest": "web-test-runner --config test/web-test-runner.config.js --group local --manual --open", + "test-browser": "web-test-runner --config test/web-test-runner.config.js --group local --playwright --browsers chromium firefox webkit", + "test-browser:ci": "web-test-runner --config test/web-test-runner.config.js --group headless:ci", + "test-browserstack": "web-test-runner --config test/web-test-runner.browserstack.config.js", + "coverage": "c8 npm test", "lint": "eslint .", - "docs": "jsdoc --configure .jsdocrc.js --destination docs --recurse README.md src && printf '%s' 'docs.openpgpjs.org' > docs/CNAME", + "docs": "jsdoc --configure .jsdocrc.cjs --destination docs --recurse README.md src && printf '%s' 'docs.openpgpjs.org' > docs/CNAME", "preversion": "rm -rf dist docs node_modules && npm ci && npm test", "version": "npm run docs && git add -A docs", "postversion": "git push && git push --tags && npm publish" }, "devDependencies": { - "@openpgp/asmcrypto.js": "^2.3.2", - "@openpgp/elliptic": "^6.5.1", - "@openpgp/jsdoc": "^3.6.4", - "@openpgp/pako": "^1.0.12", + "@noble/ciphers": "^1.0.0", + "@noble/curves": "^1.6.0", + "@noble/hashes": "^1.5.0", + "@openpgp/jsdoc": "^3.6.11", "@openpgp/seek-bzip": "^1.0.5-git", - "@openpgp/tweetnacl": "^1.0.3", - "@openpgp/web-stream-tools": "0.0.11-patch-1", - "@rollup/plugin-commonjs": "^11.1.0", - "@rollup/plugin-node-resolve": "^7.1.3", - "@rollup/plugin-replace": "^2.3.2", - "@types/chai": "^4.2.14", + "@openpgp/tweetnacl": "^1.0.4-1", + "@openpgp/web-stream-tools": "~0.1.3", + "@rollup/plugin-alias": "^5.1.1", + "@rollup/plugin-commonjs": "^25.0.8", + "@rollup/plugin-node-resolve": "^15.3.0", + "@rollup/plugin-replace": "^5.0.7", + "@rollup/plugin-terser": "^0.4.4", + "@rollup/plugin-typescript": "^11.1.6", + "@rollup/plugin-wasm": "^6.2.2", + "@types/chai": "^4.3.19", + "@types/sinon": "^17.0.3", + "@typescript-eslint/parser": "^7.18.0", + "@web/test-runner": "^0.19.0", + "@web/test-runner-browserstack": "^0.7.2", + "@web/test-runner-mocha": "^0.9.0", + "@web/test-runner-playwright": "^0.11.0", + "argon2id": "^1.0.1", "benchmark": "^2.1.4", - "bn.js": "^4.11.8", - "chai": "^4.3.6", - "chai-as-promised": "^7.1.1", - "email-addresses": "3.1.0", - "eslint": "^8.34.0", + "bn.js": "^5.2.1", + "c8": "^8.0.1", + "chai": "^4.4.1", + "chai-as-promised": "^7.1.2", + "eckey-utils": "^0.7.14", + "eslint": "^8.57.1", "eslint-config-airbnb": "^19.0.4", "eslint-config-airbnb-base": "^15.0.0", - "eslint-plugin-chai-friendly": "^0.7.2", - "eslint-plugin-import": "^2.27.5", - "esm": "^3.2.25", - "hash.js": "^1.1.3", - "http-server": "^14.1.1", - "karma": "^6.4.0", - "karma-browserstack-launcher": "^1.6.0", - "karma-chrome-launcher": "^3.1.1", - "karma-firefox-launcher": "^2.1.2", - "karma-mocha": "^2.0.1", - "karma-mocha-reporter": "^2.2.5", - "karma-webkit-launcher": "^2.1.0", - "mocha": "^8.4.0", - "nyc": "^14.1.1", - "playwright": "^1.30.0", - "rollup": "^2.38.5", - "rollup-plugin-terser": "^7.0.2", - "sinon": "^4.3.0", - "typescript": "^4.1.2", - "web-streams-polyfill": "^3.2.0" + "eslint-config-airbnb-typescript": "^18.0.0", + "eslint-import-resolver-typescript": "^3.6.3", + "eslint-plugin-chai-friendly": "^0.7.4", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-unicorn": "^48.0.1", + "fflate": "^0.7.4", + "mocha": "^10.7.3", + "playwright": "^1.48.2", + "rollup": "^4.24.2", + "sinon": "^18.0.1", + "ts-node": "^10.9.2", + "tslib": "^2.8.0", + "tsx": "^4.19.2", + "typescript": "^5.6.3", + "web-streams-polyfill": "^4.0.0" }, - "dependencies": { - "asn1.js": "^5.0.0" + "overrides": { + "@web/dev-server-core": "npm:@openpgp/wtr-dev-server-core@0.7.3-patch.1" }, "repository": { "type": "git", diff --git a/rollup.config.js b/rollup.config.js index 6e1b5104..f61c53cc 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,15 +1,43 @@ /* eslint-disable no-process-env */ import { builtinModules } from 'module'; +import { readFileSync } from 'fs'; +import alias from '@rollup/plugin-alias'; import resolve from '@rollup/plugin-node-resolve'; import commonjs from '@rollup/plugin-commonjs'; import replace from '@rollup/plugin-replace'; -import { terser } from 'rollup-plugin-terser'; +import terser from '@rollup/plugin-terser'; +import { wasm } from '@rollup/plugin-wasm'; +import typescript from '@rollup/plugin-typescript'; -import pkg from './package.json'; +// ESlint does not support JSON module imports yet, see https://github.com/eslint/eslint/discussions/15305 +// import pkg from './package.json' assert { type: 'json' }; +const pkg = JSON.parse(readFileSync('./package.json')); -const nodeDependencies = Object.keys(pkg.dependencies); +const nodeDependencies = Object.keys(pkg.dependencies || {}); +const nodeBuiltinModules = builtinModules.concat(['module']); + +const wasmOptions = { + node: { targetEnv: 'node' }, + browser: { targetEnv: 'browser', maxFileSize: undefined } // always inlline (our wasm files are small) +}; + +const getChunkFileName = (chunkInfo, extension) => `[name].${extension}`; + +/** + * Dynamically imported modules which expose an index file as entrypoint end up with a chunk named `index` + * by default. We want to preserve the module name instead. + */ +const setManualChunkName = chunkId => { + if (chunkId.includes('seek-bzip')) { + return 'seek-bzip'; + } else if (chunkId.includes('argon2id')) { + return 'argon2id'; + } else { + return undefined; + } +}; const banner = `/*! OpenPGP.js v${pkg.version} - ` + @@ -29,90 +57,120 @@ const terserOptions = { } }; +const nodeBuild = { + input: 'src/index.js', + external: nodeBuiltinModules.concat(nodeDependencies), + output: [ + { file: 'dist/node/openpgp.cjs', format: 'cjs', name: pkg.name, banner, intro }, + { file: 'dist/node/openpgp.min.cjs', format: 'cjs', name: pkg.name, banner, intro, plugins: [terser(terserOptions)], sourcemap: true }, + { file: 'dist/node/openpgp.mjs', format: 'es', banner, intro }, + { file: 'dist/node/openpgp.min.mjs', format: 'es', banner, intro, plugins: [terser(terserOptions)], sourcemap: true } + ].map(options => ({ ...options, inlineDynamicImports: true })), + plugins: [ + resolve({ + exportConditions: ['node'] // needed for resolution of noble-curves import of '@noble/crypto' in Node 18 + }), + typescript({ + compilerOptions: { outDir: './dist/tmp-ts' } + }), + commonjs(), + replace({ + 'OpenPGP.js VERSION': `OpenPGP.js ${pkg.version}` + }), + wasm(wasmOptions.node) + ] +}; + +const fullBrowserBuild = { + input: 'src/index.js', + external: nodeBuiltinModules.concat(nodeDependencies), + output: [ + { file: 'dist/openpgp.js', format: 'iife', name: pkg.name, banner, intro }, + { file: 'dist/openpgp.min.js', format: 'iife', name: pkg.name, banner, intro, plugins: [terser(terserOptions)], sourcemap: true }, + { file: 'dist/openpgp.mjs', format: 'es', banner, intro }, + { file: 'dist/openpgp.min.mjs', format: 'es', banner, intro, plugins: [terser(terserOptions)], sourcemap: true } + ].map(options => ({ ...options, inlineDynamicImports: true })), + plugins: [ + resolve({ + browser: true + }), + typescript({ + compilerOptions: { outDir: './dist/tmp-ts' } // to avoid js files being overwritten + }), + commonjs({ + ignore: nodeBuiltinModules.concat(nodeDependencies) + }), + replace({ + 'OpenPGP.js VERSION': `OpenPGP.js ${pkg.version}`, + "import { createRequire } from 'module';": 'const createRequire = () => () => {}', + delimiters: ['', ''] + }), + wasm(wasmOptions.browser) + ] +}; + +const lightweightBrowserBuild = { + input: 'src/index.js', + external: nodeBuiltinModules.concat(nodeDependencies), + output: [ + { entryFileNames: 'openpgp.mjs', chunkFileNames: chunkInfo => getChunkFileName(chunkInfo, 'mjs') }, + { entryFileNames: 'openpgp.min.mjs', chunkFileNames: chunkInfo => getChunkFileName(chunkInfo, 'min.mjs'), plugins: [terser(terserOptions)], sourcemap: true } + ].map(options => ({ ...options, dir: 'dist/lightweight', manualChunks: setManualChunkName, format: 'es', banner, intro })), + preserveEntrySignatures: 'exports-only', + plugins: [ + resolve({ + browser: true + }), + typescript({ + compilerOptions: { outDir: './dist/lightweight/tmp-ts' } + }), + commonjs({ + ignore: nodeBuiltinModules.concat(nodeDependencies) + }), + replace({ + 'OpenPGP.js VERSION': `OpenPGP.js ${pkg.version}`, + "import { createRequire } from 'module';": 'const createRequire = () => () => {}', + delimiters: ['', ''] + }), + wasm(wasmOptions.browser) + ] +}; + +const testBuild = { + input: 'test/unittests.js', + output: [ + { file: 'test/lib/unittests-bundle.js', format: 'es', intro, sourcemap: true, inlineDynamicImports: true } + ], + external: nodeBuiltinModules.concat(nodeDependencies), + plugins: [ + alias({ + entries: { + openpgp: `./dist/${process.env.npm_config_lightweight ? 'lightweight/' : ''}openpgp.mjs` + } + }), + resolve({ + browser: true + }), + typescript({ + compilerOptions: { outDir: './test/lib/tmp-ts' } + }), + commonjs({ + ignore: nodeBuiltinModules.concat(nodeDependencies), + requireReturnsDefault: 'preferred' + }), + replace({ + "import { createRequire } from 'module';": 'const createRequire = () => () => {}', + delimiters: ['', ''] + }), + wasm(wasmOptions.browser) + ] +}; + export default Object.assign([ - { - input: 'src/index.js', - output: [ - { file: 'dist/openpgp.js', format: 'iife', name: pkg.name, banner, intro }, - { file: 'dist/openpgp.min.js', format: 'iife', name: pkg.name, banner, intro, plugins: [terser(terserOptions)], sourcemap: true }, - { file: 'dist/openpgp.mjs', format: 'es', banner, intro }, - { file: 'dist/openpgp.min.mjs', format: 'es', banner, intro, plugins: [terser(terserOptions)], sourcemap: true } - ], - inlineDynamicImports: true, - plugins: [ - resolve({ - browser: true - }), - commonjs({ - ignore: builtinModules.concat(nodeDependencies) - }), - replace({ - 'OpenPGP.js VERSION': `OpenPGP.js ${pkg.version}`, - 'require(': 'void(', - delimiters: ['', ''] - }) - ] - }, - { - input: 'src/index.js', - inlineDynamicImports: true, - external: builtinModules.concat(nodeDependencies), - output: [ - { file: 'dist/node/openpgp.js', format: 'cjs', name: pkg.name, banner, intro }, - { file: 'dist/node/openpgp.min.js', format: 'cjs', name: pkg.name, banner, intro, plugins: [terser(terserOptions)], sourcemap: true }, - { file: 'dist/node/openpgp.mjs', format: 'es', banner, intro }, - { file: 'dist/node/openpgp.min.mjs', format: 'es', banner, intro, plugins: [terser(terserOptions)], sourcemap: true } - ], - plugins: [ - resolve(), - commonjs(), - replace({ - 'OpenPGP.js VERSION': `OpenPGP.js ${pkg.version}` - }) - ] - }, - { - input: 'src/index.js', - output: [ - { dir: 'dist/lightweight', entryFileNames: 'openpgp.mjs', chunkFileNames: '[name].mjs', format: 'es', banner, intro }, - { dir: 'dist/lightweight', entryFileNames: 'openpgp.min.mjs', chunkFileNames: '[name].min.mjs', format: 'es', banner, intro, plugins: [terser(terserOptions)], sourcemap: true } - ], - preserveEntrySignatures: 'allow-extension', - plugins: [ - resolve({ - browser: true - }), - commonjs({ - ignore: builtinModules.concat(nodeDependencies) - }), - replace({ - 'OpenPGP.js VERSION': `OpenPGP.js ${pkg.version}`, - 'require(': 'void(', - delimiters: ['', ''] - }) - ] - }, - { - input: 'test/unittests.js', - output: [ - { file: 'test/lib/unittests-bundle.js', format: 'es', intro, sourcemap: true } - ], - inlineDynamicImports: true, - external: ['../..', '../../..'], - plugins: [ - resolve({ - browser: true - }), - commonjs({ - ignore: builtinModules.concat(nodeDependencies) - }), - replace({ - "import openpgpjs from '../../..';": `import * as openpgpjs from '/dist/${process.env.npm_config_lightweight ? 'lightweight/' : ''}openpgp.mjs'; window.openpgp = openpgpjs;`, - 'require(': 'void(', - delimiters: ['', ''] - }) - ] - } + nodeBuild, + fullBrowserBuild, + lightweightBrowserBuild, + testBuild ].filter(config => { config.output = config.output.filter(output => { return (output.file || output.dir + '/' + output.entryFileNames).includes( diff --git a/src/biginteger/bn.interface.js b/src/biginteger/bn.interface.js deleted file mode 100644 index b8d5b79e..00000000 --- a/src/biginteger/bn.interface.js +++ /dev/null @@ -1,334 +0,0 @@ -import BN from 'bn.js'; - -/** - * @fileoverview - * BigInteger implementation of basic operations - * Wrapper of bn.js library (wwww.github.com/indutny/bn.js) - * @module biginteger/bn - * @private - */ - -/** - * @private - */ -export default class BigInteger { - /** - * Get a BigInteger (input must be big endian for strings and arrays) - * @param {Number|String|Uint8Array} n - Value to convert - * @throws {Error} on undefined input - */ - constructor(n) { - if (n === undefined) { - throw new Error('Invalid BigInteger input'); - } - - this.value = new BN(n); - } - - clone() { - const clone = new BigInteger(null); - this.value.copy(clone.value); - return clone; - } - - /** - * BigInteger increment in place - */ - iinc() { - this.value.iadd(new BN(1)); - return this; - } - - /** - * BigInteger increment - * @returns {BigInteger} this + 1. - */ - inc() { - return this.clone().iinc(); - } - - /** - * BigInteger decrement in place - */ - idec() { - this.value.isub(new BN(1)); - return this; - } - - /** - * BigInteger decrement - * @returns {BigInteger} this - 1. - */ - dec() { - return this.clone().idec(); - } - - - /** - * BigInteger addition in place - * @param {BigInteger} x - Value to add - */ - iadd(x) { - this.value.iadd(x.value); - return this; - } - - /** - * BigInteger addition - * @param {BigInteger} x - Value to add - * @returns {BigInteger} this + x. - */ - add(x) { - return this.clone().iadd(x); - } - - /** - * BigInteger subtraction in place - * @param {BigInteger} x - Value to subtract - */ - isub(x) { - this.value.isub(x.value); - return this; - } - - /** - * BigInteger subtraction - * @param {BigInteger} x - Value to subtract - * @returns {BigInteger} this - x. - */ - sub(x) { - return this.clone().isub(x); - } - - /** - * BigInteger multiplication in place - * @param {BigInteger} x - Value to multiply - */ - imul(x) { - this.value.imul(x.value); - return this; - } - - /** - * BigInteger multiplication - * @param {BigInteger} x - Value to multiply - * @returns {BigInteger} this * x. - */ - mul(x) { - return this.clone().imul(x); - } - - /** - * Compute value modulo m, in place - * @param {BigInteger} m - Modulo - */ - imod(m) { - this.value = this.value.umod(m.value); - return this; - } - - /** - * Compute value modulo m - * @param {BigInteger} m - Modulo - * @returns {BigInteger} this mod m. - */ - mod(m) { - return this.clone().imod(m); - } - - /** - * Compute modular exponentiation - * Much faster than this.exp(e).mod(n) - * @param {BigInteger} e - Exponent - * @param {BigInteger} n - Modulo - * @returns {BigInteger} this ** e mod n. - */ - modExp(e, n) { - // We use either Montgomery or normal reduction context - // Montgomery requires coprime n and R (montogmery multiplier) - // bn.js picks R as power of 2, so n must be odd - const nred = n.isEven() ? BN.red(n.value) : BN.mont(n.value); - const x = this.clone(); - x.value = x.value.toRed(nred).redPow(e.value).fromRed(); - return x; - } - - /** - * Compute the inverse of this value modulo n - * Note: this and and n must be relatively prime - * @param {BigInteger} n - Modulo - * @returns {BigInteger} x such that this*x = 1 mod n - * @throws {Error} if the inverse does not exist - */ - modInv(n) { - // invm returns a wrong result if the inverse does not exist - if (!this.gcd(n).isOne()) { - throw new Error('Inverse does not exist'); - } - return new BigInteger(this.value.invm(n.value)); - } - - /** - * Compute greatest common divisor between this and n - * @param {BigInteger} n - Operand - * @returns {BigInteger} gcd - */ - gcd(n) { - return new BigInteger(this.value.gcd(n.value)); - } - - /** - * Shift this to the left by x, in place - * @param {BigInteger} x - Shift value - */ - ileftShift(x) { - this.value.ishln(x.value.toNumber()); - return this; - } - - /** - * Shift this to the left by x - * @param {BigInteger} x - Shift value - * @returns {BigInteger} this << x. - */ - leftShift(x) { - return this.clone().ileftShift(x); - } - - /** - * Shift this to the right by x, in place - * @param {BigInteger} x - Shift value - */ - irightShift(x) { - this.value.ishrn(x.value.toNumber()); - return this; - } - - /** - * Shift this to the right by x - * @param {BigInteger} x - Shift value - * @returns {BigInteger} this >> x. - */ - rightShift(x) { - return this.clone().irightShift(x); - } - - /** - * Whether this value is equal to x - * @param {BigInteger} x - * @returns {Boolean} - */ - equal(x) { - return this.value.eq(x.value); - } - - /** - * Whether this value is less than x - * @param {BigInteger} x - * @returns {Boolean} - */ - lt(x) { - return this.value.lt(x.value); - } - - /** - * Whether this value is less than or equal to x - * @param {BigInteger} x - * @returns {Boolean} - */ - lte(x) { - return this.value.lte(x.value); - } - - /** - * Whether this value is greater than x - * @param {BigInteger} x - * @returns {Boolean} - */ - gt(x) { - return this.value.gt(x.value); - } - - /** - * Whether this value is greater than or equal to x - * @param {BigInteger} x - * @returns {Boolean} - */ - gte(x) { - return this.value.gte(x.value); - } - - isZero() { - return this.value.isZero(); - } - - isOne() { - return this.value.eq(new BN(1)); - } - - isNegative() { - return this.value.isNeg(); - } - - isEven() { - return this.value.isEven(); - } - - abs() { - const res = this.clone(); - res.value = res.value.abs(); - return res; - } - - /** - * Get this value as a string - * @returns {String} this value. - */ - toString() { - return this.value.toString(); - } - - /** - * Get this value as an exact Number (max 53 bits) - * Fails if this value is too large - * @returns {Number} - */ - toNumber() { - return this.value.toNumber(); - } - - /** - * Get value of i-th bit - * @param {Number} i - Bit index - * @returns {Number} Bit value. - */ - getBit(i) { - return this.value.testn(i) ? 1 : 0; - } - - /** - * Compute bit length - * @returns {Number} Bit length. - */ - bitLength() { - return this.value.bitLength(); - } - - /** - * Compute byte length - * @returns {Number} Byte length. - */ - byteLength() { - return this.value.byteLength(); - } - - /** - * Get Uint8Array representation of this number - * @param {String} endian - Endianess of output array (defaults to 'be') - * @param {Number} length - Of output array - * @returns {Uint8Array} - */ - toUint8Array(endian = 'be', length) { - return this.value.toArrayLike(Uint8Array, endian, length); - } -} diff --git a/src/biginteger/index.js b/src/biginteger/index.js deleted file mode 100644 index cf687b44..00000000 --- a/src/biginteger/index.js +++ /dev/null @@ -1,14 +0,0 @@ -import BigInteger from './native.interface'; - -const detectBigInt = () => typeof BigInt !== 'undefined'; - -async function getBigInteger() { - if (detectBigInt()) { - return BigInteger; - } else { - const { default: BigInteger } = await import('./bn.interface'); - return BigInteger; - } -} - -export { getBigInteger }; diff --git a/src/biginteger/native.interface.js b/src/biginteger/native.interface.js deleted file mode 100644 index a524006c..00000000 --- a/src/biginteger/native.interface.js +++ /dev/null @@ -1,453 +0,0 @@ -/* eslint-disable new-cap */ - -/** - * @fileoverview - * BigInteger implementation of basic operations - * that wraps the native BigInt library. - * Operations are not constant time, - * but we try and limit timing leakage where we can - * @module biginteger/native - * @private - */ - -/** - * @private - */ -export default class BigInteger { - /** - * Get a BigInteger (input must be big endian for strings and arrays) - * @param {Number|String|Uint8Array} n - Value to convert - * @throws {Error} on null or undefined input - */ - constructor(n) { - if (n === undefined) { - throw new Error('Invalid BigInteger input'); - } - - if (n instanceof Uint8Array) { - const bytes = n; - const hex = new Array(bytes.length); - for (let i = 0; i < bytes.length; i++) { - const hexByte = bytes[i].toString(16); - hex[i] = (bytes[i] <= 0xF) ? ('0' + hexByte) : hexByte; - } - this.value = BigInt('0x0' + hex.join('')); - } else { - this.value = BigInt(n); - } - } - - clone() { - return new BigInteger(this.value); - } - - /** - * BigInteger increment in place - */ - iinc() { - this.value++; - return this; - } - - /** - * BigInteger increment - * @returns {BigInteger} this + 1. - */ - inc() { - return this.clone().iinc(); - } - - /** - * BigInteger decrement in place - */ - idec() { - this.value--; - return this; - } - - /** - * BigInteger decrement - * @returns {BigInteger} this - 1. - */ - dec() { - return this.clone().idec(); - } - - /** - * BigInteger addition in place - * @param {BigInteger} x - Value to add - */ - iadd(x) { - this.value += x.value; - return this; - } - - /** - * BigInteger addition - * @param {BigInteger} x - Value to add - * @returns {BigInteger} this + x. - */ - add(x) { - return this.clone().iadd(x); - } - - /** - * BigInteger subtraction in place - * @param {BigInteger} x - Value to subtract - */ - isub(x) { - this.value -= x.value; - return this; - } - - /** - * BigInteger subtraction - * @param {BigInteger} x - Value to subtract - * @returns {BigInteger} this - x. - */ - sub(x) { - return this.clone().isub(x); - } - - /** - * BigInteger multiplication in place - * @param {BigInteger} x - Value to multiply - */ - imul(x) { - this.value *= x.value; - return this; - } - - /** - * BigInteger multiplication - * @param {BigInteger} x - Value to multiply - * @returns {BigInteger} this * x. - */ - mul(x) { - return this.clone().imul(x); - } - - /** - * Compute value modulo m, in place - * @param {BigInteger} m - Modulo - */ - imod(m) { - this.value %= m.value; - if (this.isNegative()) { - this.iadd(m); - } - return this; - } - - /** - * Compute value modulo m - * @param {BigInteger} m - Modulo - * @returns {BigInteger} this mod m. - */ - mod(m) { - return this.clone().imod(m); - } - - /** - * Compute modular exponentiation using square and multiply - * @param {BigInteger} e - Exponent - * @param {BigInteger} n - Modulo - * @returns {BigInteger} this ** e mod n. - */ - modExp(e, n) { - if (n.isZero()) throw Error('Modulo cannot be zero'); - if (n.isOne()) return new BigInteger(0); - if (e.isNegative()) throw Error('Unsopported negative exponent'); - - let exp = e.value; - let x = this.value; - - x %= n.value; - let r = BigInt(1); - while (exp > BigInt(0)) { - const lsb = exp & BigInt(1); - exp >>= BigInt(1); // e / 2 - // Always compute multiplication step, to reduce timing leakage - const rx = (r * x) % n.value; - // Update r only if lsb is 1 (odd exponent) - r = lsb ? rx : r; - x = (x * x) % n.value; // Square - } - return new BigInteger(r); - } - - - /** - * Compute the inverse of this value modulo n - * Note: this and and n must be relatively prime - * @param {BigInteger} n - Modulo - * @returns {BigInteger} x such that this*x = 1 mod n - * @throws {Error} if the inverse does not exist - */ - modInv(n) { - const { gcd, x } = this._egcd(n); - if (!gcd.isOne()) { - throw new Error('Inverse does not exist'); - } - return x.add(n).mod(n); - } - - /** - * Extended Eucleadian algorithm (http://anh.cs.luc.edu/331/notes/xgcd.pdf) - * Given a = this and b, compute (x, y) such that ax + by = gdc(a, b) - * @param {BigInteger} b - Second operand - * @returns {{ gcd, x, y: BigInteger }} - */ - _egcd(b) { - let x = BigInt(0); - let y = BigInt(1); - let xPrev = BigInt(1); - let yPrev = BigInt(0); - - let a = this.value; - b = b.value; - - while (b !== BigInt(0)) { - const q = a / b; - let tmp = x; - x = xPrev - q * x; - xPrev = tmp; - - tmp = y; - y = yPrev - q * y; - yPrev = tmp; - - tmp = b; - b = a % b; - a = tmp; - } - - return { - x: new BigInteger(xPrev), - y: new BigInteger(yPrev), - gcd: new BigInteger(a) - }; - } - - /** - * Compute greatest common divisor between this and n - * @param {BigInteger} b - Operand - * @returns {BigInteger} gcd - */ - gcd(b) { - let a = this.value; - b = b.value; - while (b !== BigInt(0)) { - const tmp = b; - b = a % b; - a = tmp; - } - return new BigInteger(a); - } - - /** - * Shift this to the left by x, in place - * @param {BigInteger} x - Shift value - */ - ileftShift(x) { - this.value <<= x.value; - return this; - } - - /** - * Shift this to the left by x - * @param {BigInteger} x - Shift value - * @returns {BigInteger} this << x. - */ - leftShift(x) { - return this.clone().ileftShift(x); - } - - /** - * Shift this to the right by x, in place - * @param {BigInteger} x - Shift value - */ - irightShift(x) { - this.value >>= x.value; - return this; - } - - /** - * Shift this to the right by x - * @param {BigInteger} x - Shift value - * @returns {BigInteger} this >> x. - */ - rightShift(x) { - return this.clone().irightShift(x); - } - - /** - * Whether this value is equal to x - * @param {BigInteger} x - * @returns {Boolean} - */ - equal(x) { - return this.value === x.value; - } - - /** - * Whether this value is less than x - * @param {BigInteger} x - * @returns {Boolean} - */ - lt(x) { - return this.value < x.value; - } - - /** - * Whether this value is less than or equal to x - * @param {BigInteger} x - * @returns {Boolean} - */ - lte(x) { - return this.value <= x.value; - } - - /** - * Whether this value is greater than x - * @param {BigInteger} x - * @returns {Boolean} - */ - gt(x) { - return this.value > x.value; - } - - /** - * Whether this value is greater than or equal to x - * @param {BigInteger} x - * @returns {Boolean} - */ - gte(x) { - return this.value >= x.value; - } - - isZero() { - return this.value === BigInt(0); - } - - isOne() { - return this.value === BigInt(1); - } - - isNegative() { - return this.value < BigInt(0); - } - - isEven() { - return !(this.value & BigInt(1)); - } - - abs() { - const res = this.clone(); - if (this.isNegative()) { - res.value = -res.value; - } - return res; - } - - /** - * Get this value as a string - * @returns {String} this value. - */ - toString() { - return this.value.toString(); - } - - /** - * Get this value as an exact Number (max 53 bits) - * Fails if this value is too large - * @returns {Number} - */ - toNumber() { - const number = Number(this.value); - if (number > Number.MAX_SAFE_INTEGER) { - // We throw and error to conform with the bn.js implementation - throw new Error('Number can only safely store up to 53 bits'); - } - return number; - } - - /** - * Get value of i-th bit - * @param {Number} i - Bit index - * @returns {Number} Bit value. - */ - getBit(i) { - const bit = (this.value >> BigInt(i)) & BigInt(1); - return (bit === BigInt(0)) ? 0 : 1; - } - - /** - * Compute bit length - * @returns {Number} Bit length. - */ - bitLength() { - const zero = new BigInteger(0); - const one = new BigInteger(1); - const negOne = new BigInteger(-1); - - // -1n >> -1n is -1n - // 1n >> 1n is 0n - const target = this.isNegative() ? negOne : zero; - let bitlen = 1; - const tmp = this.clone(); - while (!tmp.irightShift(one).equal(target)) { - bitlen++; - } - return bitlen; - } - - /** - * Compute byte length - * @returns {Number} Byte length. - */ - byteLength() { - const zero = new BigInteger(0); - const negOne = new BigInteger(-1); - - const target = this.isNegative() ? negOne : zero; - const eight = new BigInteger(8); - let len = 1; - const tmp = this.clone(); - while (!tmp.irightShift(eight).equal(target)) { - len++; - } - return len; - } - - /** - * Get Uint8Array representation of this number - * @param {String} endian - Endianess of output array (defaults to 'be') - * @param {Number} length - Of output array - * @returns {Uint8Array} - */ - toUint8Array(endian = 'be', length) { - // we get and parse the hex string (https://coolaj86.com/articles/convert-js-bigints-to-typedarrays/) - // this is faster than shift+mod iterations - let hex = this.value.toString(16); - if (hex.length % 2 === 1) { - hex = '0' + hex; - } - - const rawLength = hex.length / 2; - const bytes = new Uint8Array(length || rawLength); - // parse hex - const offset = length ? (length - rawLength) : 0; - let i = 0; - while (i < rawLength) { - bytes[i + offset] = parseInt(hex.slice(2 * i, 2 * i + 2), 16); - i++; - } - - if (endian !== 'be') { - bytes.reverse(); - } - - return bytes; - } -} diff --git a/src/cleartext.js b/src/cleartext.js index a99155e3..61136c28 100644 --- a/src/cleartext.js +++ b/src/cleartext.js @@ -59,20 +59,22 @@ export class CleartextMessage { /** * Sign the cleartext message - * @param {Array} privateKeys - private keys with decrypted secret key data for signing + * @param {Array} signingKeys - private keys with decrypted secret key data for signing + * @param {Array} recipientKeys - recipient keys to get the signing preferences from * @param {Signature} [signature] - Any existing detached signature * @param {Array} [signingKeyIDs] - Array of key IDs to use for signing. Each signingKeyIDs[i] corresponds to privateKeys[i] * @param {Date} [date] - The creation time of the signature that should be created - * @param {Array} [userIDs] - User IDs to sign with, e.g. [{ name:'Steve Sender', email:'steve@openpgp.org' }] + * @param {Array} [signingKeyIDs] - User IDs to sign with, e.g. [{ name:'Steve Sender', email:'steve@openpgp.org' }] + * @param {Array} [recipientUserIDs] - User IDs associated with `recipientKeys` to get the signing preferences from * @param {Array} [notations] - Notation Data to add to the signatures, e.g. [{ name: 'test@example.org', value: new TextEncoder().encode('test'), humanReadable: true, critical: false }] * @param {Object} [config] - Full configuration, defaults to openpgp.config * @returns {Promise} New cleartext message with signed content. * @async */ - async sign(privateKeys, signature = null, signingKeyIDs = [], date = new Date(), userIDs = [], notations = [], config = defaultConfig) { + async sign(signingKeys, recipientKeys = [], signature = null, signingKeyIDs = [], date = new Date(), signingUserIDs = [], recipientUserIDs = [], notations = [], config = defaultConfig) { const literalDataPacket = new LiteralDataPacket(); literalDataPacket.setText(this.text); - const newSignature = new Signature(await createSignaturePackets(literalDataPacket, privateKeys, signature, signingKeyIDs, date, userIDs, notations, true, config)); + const newSignature = new Signature(await createSignaturePackets(literalDataPacket, signingKeys, recipientKeys, signature, signingKeyIDs, date, signingUserIDs, recipientUserIDs, notations, true, config)); return new CleartextMessage(this.text, newSignature); } @@ -111,16 +113,22 @@ export class CleartextMessage { * @returns {String | ReadableStream} ASCII armor. */ armor(config = defaultConfig) { - let hashes = this.signature.packets.map(function(packet) { - return enums.read(enums.hash, packet.hashAlgorithm).toUpperCase(); - }); - hashes = hashes.filter(function(item, i, ar) { return ar.indexOf(item) === i; }); + // emit header and checksum if one of the signatures has a version not 6 + const emitHeaderAndChecksum = this.signature.packets.some(packet => packet.version !== 6); + const hash = emitHeaderAndChecksum ? + Array.from(new Set(this.signature.packets.map( + packet => enums.read(enums.hash, packet.hashAlgorithm).toUpperCase() + ))).join() : + null; + const body = { - hash: hashes.join(), + hash, text: this.text, data: this.signature.packets.write() }; - return armor(enums.armor.signed, body, undefined, undefined, undefined, config); + + // An ASCII-armored sequence of Signature packets that only includes v6 Signature packets MUST NOT contain a CRC24 footer. + return armor(enums.armor.signed, body, undefined, undefined, undefined, emitHeaderAndChecksum, config); } } @@ -171,30 +179,27 @@ function verifyHeaders(headers, packetlist) { return true; }; - let oneHeader = null; - let hashAlgos = []; - headers.forEach(function(header) { - oneHeader = header.match(/^Hash: (.+)$/); // get header value - if (oneHeader) { - oneHeader = oneHeader[1].replace(/\s/g, ''); // remove whitespace - oneHeader = oneHeader.split(','); - oneHeader = oneHeader.map(function(hash) { - hash = hash.toLowerCase(); - try { - return enums.write(enums.hash, hash); - } catch (e) { - throw new Error('Unknown hash algorithm in armor header: ' + hash); - } - }); - hashAlgos = hashAlgos.concat(oneHeader); + const hashAlgos = []; + headers.forEach(header => { + const hashHeader = header.match(/^Hash: (.+)$/); // get header value + if (hashHeader) { + const parsedHashIDs = hashHeader[1] + .replace(/\s/g, '') // remove whitespace + .split(',') + .map(hashName => { + try { + return enums.write(enums.hash, hashName.toLowerCase()); + } catch (e) { + throw new Error('Unknown hash algorithm in armor header: ' + hashName.toLowerCase()); + } + }); + hashAlgos.push(...parsedHashIDs); } else { throw new Error('Only "Hash" header allowed in cleartext signed message'); } }); - if (!hashAlgos.length && !checkHashAlgos([enums.hash.md5])) { - throw new Error('If no "Hash" header in cleartext signed message, then only MD5 signatures allowed'); - } else if (hashAlgos.length && !checkHashAlgos(hashAlgos)) { + if (hashAlgos.length && !checkHashAlgos(hashAlgos)) { throw new Error('Hash algorithm mismatch in armor header and signature'); } } diff --git a/src/config/config.js b/src/config/config.js index ccfee7e1..9e77710f 100644 --- a/src/config/config.js +++ b/src/config/config.js @@ -26,7 +26,7 @@ export default { * @memberof module:config * @property {Integer} preferredHashAlgorithm Default hash algorithm {@link module:enums.hash} */ - preferredHashAlgorithm: enums.hash.sha256, + preferredHashAlgorithm: enums.hash.sha512, /** * @memberof module:config * @property {Integer} preferredSymmetricAlgorithm Default encryption cipher {@link module:enums.symmetric} @@ -37,28 +37,34 @@ export default { * @property {Integer} compression Default compression algorithm {@link module:enums.compression} */ preferredCompressionAlgorithm: enums.compression.uncompressed, - /** - * @memberof module:config - * @property {Integer} deflateLevel Default zip/zlib compression level, between 1 and 9 - */ - deflateLevel: 6, - /** * Use Authenticated Encryption with Additional Data (AEAD) protection for symmetric encryption. + * This option is applicable to: + * - key generation (encryption key preferences), + * - password-based message encryption, and + * - private key encryption. + * In the case of message encryption using public keys, the encryption key preferences are respected instead. * Note: not all OpenPGP implementations are compatible with this option. - * **FUTURE OPENPGP.JS VERSIONS MAY BREAK COMPATIBILITY WHEN USING THIS OPTION** - * @see {@link https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-07|RFC4880bis-07} + * @see {@link https://tools.ietf.org/html/draft-ietf-openpgp-crypto-refresh-10.html|draft-crypto-refresh-10} * @memberof module:config * @property {Boolean} aeadProtect */ aeadProtect: false, + /** + * When reading OpenPGP v4 private keys (e.g. those generated in OpenPGP.js when not setting `config.v5Keys = true`) + * which were encrypted by OpenPGP.js v5 (or older) using `config.aeadProtect = true`, + * this option must be set, otherwise key parsing and/or key decryption will fail. + * Note: only set this flag if you know that the keys are of the legacy type, as non-legacy keys + * will be processed incorrectly. + */ + parseAEADEncryptedV4KeysAsLegacy: false, /** * Default Authenticated Encryption with Additional Data (AEAD) encryption mode * Only has an effect when aeadProtect is set to true. * @memberof module:config * @property {Integer} preferredAEADAlgorithm Default AEAD mode {@link module:enums.aead} */ - preferredAEADAlgorithm: enums.aead.eax, + preferredAEADAlgorithm: enums.aead.gcm, /** * Chunk Size Byte for Authenticated Encryption with Additional Data (AEAD) mode * Only has an effect when aeadProtect is set to true. @@ -68,20 +74,57 @@ export default { */ aeadChunkSizeByte: 12, /** - * Use V5 keys. + * Use v6 keys. * Note: not all OpenPGP implementations are compatible with this option. * **FUTURE OPENPGP.JS VERSIONS MAY BREAK COMPATIBILITY WHEN USING THIS OPTION** * @memberof module:config - * @property {Boolean} v5Keys + * @property {Boolean} v6Keys */ - v5Keys: false, + v6Keys: false, /** - * {@link https://tools.ietf.org/html/rfc4880#section-3.7.1.3|RFC4880 3.7.1.3}: - * Iteration Count Byte for S2K (String to Key) + * Enable parsing v5 keys and v5 signatures (which is different from the AEAD-encrypted SEIPDv2 packet). + * These are non-standard entities, which in the crypto-refresh have been superseded + * by v6 keys and v6 signatures, respectively. + * However, generation of v5 entities was supported behind config flag in OpenPGP.js v5, and some other libraries, + * hence parsing them might be necessary in some cases. + */ + enableParsingV5Entities: false, + /** + * S2K (String to Key) type, used for key derivation in the context of secret key encryption + * and password-encrypted data. Weaker s2k options are not allowed. + * Note: Argon2 is the strongest option but not all OpenPGP implementations are compatible with it + * (pending standardisation). + * @memberof module:config + * @property {enums.s2k.argon2|enums.s2k.iterated} s2kType {@link module:enums.s2k} + */ + s2kType: enums.s2k.iterated, + /** + * {@link https://tools.ietf.org/html/rfc4880#section-3.7.1.3| RFC4880 3.7.1.3}: + * Iteration Count Byte for Iterated and Salted S2K (String to Key). + * Only relevant if `config.s2kType` is set to `enums.s2k.iterated`. + * Note: this is the exponent value, not the final number of iterations (refer to specs for more details). * @memberof module:config * @property {Integer} s2kIterationCountByte */ s2kIterationCountByte: 224, + /** + * {@link https://tools.ietf.org/html/draft-ietf-openpgp-crypto-refresh-07.html#section-3.7.1.4| draft-crypto-refresh 3.7.1.4}: + * Argon2 parameters for S2K (String to Key). + * Only relevant if `config.s2kType` is set to `enums.s2k.argon2`. + * Default settings correspond to the second recommendation from RFC9106 ("uniformly safe option"), + * to ensure compatibility with memory-constrained environments. + * For more details on the choice of parameters, see https://tools.ietf.org/html/rfc9106#section-4. + * @memberof module:config + * @property {Object} params + * @property {Integer} params.passes - number of iterations t + * @property {Integer} params.parallelism - degree of parallelism p + * @property {Integer} params.memoryExponent - one-octet exponent indicating the memory size, which will be: 2**memoryExponent kibibytes. + */ + s2kArgon2Params: { + passes: 3, + parallelism: 4, // lanes + memoryExponent: 16 // 64 MiB of RAM + }, /** * Allow decryption of messages without integrity protection. * This is an **insecure** setting: @@ -96,16 +139,16 @@ export default { * process large streams while limiting memory usage by releasing the decrypted chunks as soon as possible * and deferring checking their integrity until the decrypted stream has been read in full. * - * This setting is **insecure** if the partially decrypted message is processed further or displayed to the user. + * This setting is **insecure** if the encrypted data has been corrupted by a malicious entity: + * - if the partially decrypted message is processed further or displayed to the user, it opens up the possibility of attacks such as EFAIL + * (see https://efail.de/). + * - an attacker with access to traces or timing info of internal processing errors could learn some info about the data. + * + * NB: this setting does not apply to AEAD-encrypted data, where the AEAD data chunk is never released until integrity is confirmed. * @memberof module:config * @property {Boolean} allowUnauthenticatedStream */ allowUnauthenticatedStream: false, - /** - * @memberof module:config - * @property {Boolean} checksumRequired Do not throw error when armor is missing a checksum - */ - checksumRequired: false, /** * Minimum RSA key size allowed for key generation and message signing, verification and encryption. * The default is 2047 since due to a bug, previous versions of OpenPGP.js could generate 2047-bit keys instead of 2048-bit ones. @@ -120,11 +163,6 @@ export default { * @property {Boolean} passwordCollisionCheck */ passwordCollisionCheck: false, - /** - * @memberof module:config - * @property {Boolean} revocationsExpire If true, expired revocation signatures are ignored - */ - revocationsExpire: false, /** * Allow decryption using RSA keys without `encrypt` flag. * This setting is potentially insecure, but it is needed to get around an old openpgpjs bug @@ -142,7 +180,14 @@ export default { * @property {Boolean} allowInsecureDecryptionWithSigningKeys */ allowInsecureVerificationWithReformattedKeys: false, - + /** + * Allow using keys that do not have any key flags set. + * Key flags are needed to restrict key usage to specific purposes: for instance, a signing key could only be allowed to certify other keys, and not sign messages + * (see https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#section-5.2.3.29). + * Some older keys do not declare any key flags, which means they are not allowed to be used for any operation. + * This setting allows using such keys for any operation for which they are compatible, based on their public key algorithm. + */ + allowMissingKeyFlags: false, /** * Enable constant-time decryption of RSA- and ElGamal-encrypted session keys, to hinder Bleichenbacher-like attacks (https://link.springer.com/chapter/10.1007/BFb0055716). * This setting has measurable performance impact and it is only helpful in application scenarios where both of the following conditions apply: @@ -161,12 +206,6 @@ export default { * @property {Set} constantTimePKCS1DecryptionSupportedSymmetricAlgorithms {@link module:enums.symmetric} */ constantTimePKCS1DecryptionSupportedSymmetricAlgorithms: new Set([enums.symmetric.aes128, enums.symmetric.aes192, enums.symmetric.aes256]), - - /** - * @memberof module:config - * @property {Integer} minBytesForWebCrypto The minimum amount of bytes for which to use native WebCrypto APIs when available - */ - minBytesForWebCrypto: 1000, /** * @memberof module:config * @property {Boolean} ignoreUnsupportedPackets Ignore unsupported/unrecognizable packets on parsing instead of throwing an error @@ -220,13 +259,20 @@ export default { */ knownNotations: [], /** - * Whether to use the indutny/elliptic library for curves (other than Curve25519) that are not supported by the available native crypto API. - * When false, certain standard curves will not be supported (depending on the platform). - * Note: the indutny/elliptic curve library is not designed to be constant time. - * @memberof module:config - * @property {Boolean} useIndutnyElliptic + * If true, a salt notation is used to randomize signatures generated by v4 and v5 keys (v6 signatures are always non-deterministic, by design). + * This protects EdDSA signatures from potentially leaking the secret key in case of faults (i.e. bitflips) which, in principle, could occur + * during the signing computation. It is added to signatures of any algo for simplicity, and as it may also serve as protection in case of + * weaknesses in the hash algo, potentially hindering e.g. some chosen-prefix attacks. + * NOTE: the notation is interoperable, but will reveal that the signature has been generated using OpenPGP.js, which may not be desirable in some cases. */ - useIndutnyElliptic: true, + nonDeterministicSignaturesViaNotation: true, + /** + * Whether to use the the noble-curves library for curves (other than Curve25519) that are not supported by the available native crypto API. + * When false, certain standard curves will not be supported (depending on the platform). + * @memberof module:config + * @property {Boolean} useEllipticFallback + */ + useEllipticFallback: true, /** * Reject insecure hash algorithms * @memberof module:config diff --git a/src/crypto/aes_kw.js b/src/crypto/aes_kw.js index d9bbbb3e..0ef54f78 100644 --- a/src/crypto/aes_kw.js +++ b/src/crypto/aes_kw.js @@ -19,129 +19,79 @@ * @fileoverview Implementation of RFC 3394 AES Key Wrap & Key Unwrap funcions * @see module:crypto/public_key/elliptic/ecdh * @module crypto/aes_kw - * @private */ -import * as cipher from './cipher'; +import { aeskw as nobleAesKW } from '@noble/ciphers/aes'; +import { getCipherParams } from './cipher'; import util from '../util'; +const webCrypto = util.getWebCrypto(); /** * AES key wrap - * @function - * @param {Uint8Array} key - * @param {Uint8Array} data - * @returns {Uint8Array} + * @param {enums.symmetric.aes128|enums.symmetric.aes256|enums.symmetric.aes192} algo - AES algo + * @param {Uint8Array} key - wrapping key + * @param {Uint8Array} dataToWrap + * @returns {Uint8Array} wrapped key */ -export function wrap(key, data) { - const aes = new cipher['aes' + (key.length * 8)](key); - const IV = new Uint32Array([0xA6A6A6A6, 0xA6A6A6A6]); - const P = unpack(data); - let A = IV; - const R = P; - const n = P.length / 2; - const t = new Uint32Array([0, 0]); - let B = new Uint32Array(4); - for (let j = 0; j <= 5; ++j) { - for (let i = 0; i < n; ++i) { - t[1] = n * j + (1 + i); - // B = A - B[0] = A[0]; - B[1] = A[1]; - // B = A || R[i] - B[2] = R[2 * i]; - B[3] = R[2 * i + 1]; - // B = AES(K, B) - B = unpack(aes.encrypt(pack(B))); - // A = MSB(64, B) ^ t - A = B.subarray(0, 2); - A[0] ^= t[0]; - A[1] ^= t[1]; - // R[i] = LSB(64, B) - R[2 * i] = B[2]; - R[2 * i + 1] = B[3]; - } +export async function wrap(algo, key, dataToWrap) { + const { keySize } = getCipherParams(algo); + // sanity checks, since WebCrypto does not use the `algo` input + if (!util.isAES(algo) || key.length !== keySize) { + throw new Error('Unexpected algorithm or key size'); } - return pack(A, R); + + try { + const wrappingKey = await webCrypto.importKey('raw', key, { name: 'AES-KW' }, false, ['wrapKey']); + // Import data as HMAC key, as it has no key length requirements + const keyToWrap = await webCrypto.importKey('raw', dataToWrap, { name: 'HMAC', hash: 'SHA-256' }, true, ['sign']); + const wrapped = await webCrypto.wrapKey('raw', keyToWrap, wrappingKey, { name: 'AES-KW' }); + return new Uint8Array(wrapped); + } catch (err) { + // no 192 bit support in Chromium, which throws `OperationError`, see: https://www.chromium.org/blink/webcrypto#TOC-AES-support + if (err.name !== 'NotSupportedError' && + !(key.length === 24 && err.name === 'OperationError')) { + throw err; + } + util.printDebugError('Browser did not support operation: ' + err.message); + } + + return nobleAesKW(key).encrypt(dataToWrap); } /** * AES key unwrap - * @function - * @param {String} key - * @param {String} data - * @returns {Uint8Array} - * @throws {Error} + * @param {enums.symmetric.aes128|enums.symmetric.aes256|enums.symmetric.aes192} algo - AES algo + * @param {Uint8Array} key - wrapping key + * @param {Uint8Array} wrappedData + * @returns {Uint8Array} unwrapped data */ -export function unwrap(key, data) { - const aes = new cipher['aes' + (key.length * 8)](key); - const IV = new Uint32Array([0xA6A6A6A6, 0xA6A6A6A6]); - const C = unpack(data); - let A = C.subarray(0, 2); - const R = C.subarray(2); - const n = C.length / 2 - 1; - const t = new Uint32Array([0, 0]); - let B = new Uint32Array(4); - for (let j = 5; j >= 0; --j) { - for (let i = n - 1; i >= 0; --i) { - t[1] = n * j + (i + 1); - // B = A ^ t - B[0] = A[0] ^ t[0]; - B[1] = A[1] ^ t[1]; - // B = (A ^ t) || R[i] - B[2] = R[2 * i]; - B[3] = R[2 * i + 1]; - // B = AES-1(B) - B = unpack(aes.decrypt(pack(B))); - // A = MSB(64, B) - A = B.subarray(0, 2); - // R[i] = LSB(64, B) - R[2 * i] = B[2]; - R[2 * i + 1] = B[3]; - } +export async function unwrap(algo, key, wrappedData) { + const { keySize } = getCipherParams(algo); + // sanity checks, since WebCrypto does not use the `algo` input + if (!util.isAES(algo) || key.length !== keySize) { + throw new Error('Unexpected algorithm or key size'); } - if (A[0] === IV[0] && A[1] === IV[1]) { - return pack(R); - } - throw new Error('Key Data Integrity failed'); -} -function createArrayBuffer(data) { - if (util.isString(data)) { - const { length } = data; - const buffer = new ArrayBuffer(length); - const view = new Uint8Array(buffer); - for (let j = 0; j < length; ++j) { - view[j] = data.charCodeAt(j); + let wrappingKey; + try { + wrappingKey = await webCrypto.importKey('raw', key, { name: 'AES-KW' }, false, ['unwrapKey']); + } catch (err) { + // no 192 bit support in Chromium, which throws `OperationError`, see: https://www.chromium.org/blink/webcrypto#TOC-AES-support + if (err.name !== 'NotSupportedError' && + !(key.length === 24 && err.name === 'OperationError')) { + throw err; } - return buffer; + util.printDebugError('Browser did not support operation: ' + err.message); + return nobleAesKW(key).decrypt(wrappedData); } - return new Uint8Array(data).buffer; -} -function unpack(data) { - const { length } = data; - const buffer = createArrayBuffer(data); - const view = new DataView(buffer); - const arr = new Uint32Array(length / 4); - for (let i = 0; i < length / 4; ++i) { - arr[i] = view.getUint32(4 * i); - } - return arr; -} - -function pack() { - let length = 0; - for (let k = 0; k < arguments.length; ++k) { - length += 4 * arguments[k].length; - } - const buffer = new ArrayBuffer(length); - const view = new DataView(buffer); - let offset = 0; - for (let i = 0; i < arguments.length; ++i) { - for (let j = 0; j < arguments[i].length; ++j) { - view.setUint32(offset + 4 * j, arguments[i][j]); + try { + const unwrapped = await webCrypto.unwrapKey('raw', wrappedData, wrappingKey, { name: 'AES-KW' }, { name: 'HMAC', hash: 'SHA-256' }, true, ['sign']); + return new Uint8Array(await webCrypto.exportKey('raw', unwrapped)); + } catch (err) { + if (err.name === 'OperationError') { + throw new Error('Key Data Integrity failed'); } - offset += 4 * arguments[i].length; + throw err; } - return new Uint8Array(buffer); } diff --git a/src/crypto/biginteger.ts b/src/crypto/biginteger.ts new file mode 100644 index 00000000..ba8c82c4 --- /dev/null +++ b/src/crypto/biginteger.ts @@ -0,0 +1,216 @@ +// Operations are not constant time, but we try and limit timing leakage where we can + +const _0n = BigInt(0); +const _1n = BigInt(1); + +export function uint8ArrayToBigInt(bytes: Uint8Array) { + const hexAlphabet = '0123456789ABCDEF'; + let s = ''; + bytes.forEach(v => { + s += hexAlphabet[v >> 4] + hexAlphabet[v & 15]; + }); + return BigInt('0x0' + s); +} + +export function mod(a: bigint, m: bigint) { + const reduced = a % m; + return reduced < _0n ? reduced + m : reduced; +} + +/** + * Compute modular exponentiation using square and multiply + * @param {BigInt} a - Base + * @param {BigInt} e - Exponent + * @param {BigInt} n - Modulo + * @returns {BigInt} b ** e mod n. + */ +export function modExp(b: bigint, e: bigint, n: bigint) { + if (n === _0n) throw Error('Modulo cannot be zero'); + if (n === _1n) return BigInt(0); + if (e < _0n) throw Error('Unsopported negative exponent'); + + let exp = e; + let x = b; + + x %= n; + let r = BigInt(1); + while (exp > _0n) { + const lsb = exp & _1n; + exp >>= _1n; // e / 2 + // Always compute multiplication step, to reduce timing leakage + const rx = (r * x) % n; + // Update r only if lsb is 1 (odd exponent) + r = lsb ? rx : r; + x = (x * x) % n; // Square + } + return r; +} + + +function abs(x: bigint) { + return x >= _0n ? x : -x; +} + +/** + * Extended Eucleadian algorithm (http://anh.cs.luc.edu/331/notes/xgcd.pdf) + * Given a and b, compute (x, y) such that ax + by = gdc(a, b). + * Negative numbers are also supported. + * @param {BigInt} a - First operand + * @param {BigInt} b - Second operand + * @returns {{ gcd, x, y: bigint }} + */ +function _egcd(aInput: bigint, bInput: bigint) { + let x = BigInt(0); + let y = BigInt(1); + let xPrev = BigInt(1); + let yPrev = BigInt(0); + + // Deal with negative numbers: run algo over absolute values, + // and "move" the sign to the returned x and/or y. + // See https://math.stackexchange.com/questions/37806/extended-euclidean-algorithm-with-negative-numbers + let a = abs(aInput); + let b = abs(bInput); + const aNegated = aInput < _0n; + const bNegated = bInput < _0n; + + while (b !== _0n) { + const q = a / b; + let tmp = x; + x = xPrev - q * x; + xPrev = tmp; + + tmp = y; + y = yPrev - q * y; + yPrev = tmp; + + tmp = b; + b = a % b; + a = tmp; + } + + return { + x: aNegated ? -xPrev : xPrev, + y: bNegated ? -yPrev : yPrev, + gcd: a + }; +} + +/** + * Compute the inverse of `a` modulo `n` + * Note: `a` and and `n` must be relatively prime + * @param {BigInt} a + * @param {BigInt} n - Modulo + * @returns {BigInt} x such that a*x = 1 mod n + * @throws {Error} if the inverse does not exist + */ +export function modInv(a: bigint, n: bigint) { + const { gcd, x } = _egcd(a, n); + if (gcd !== _1n) { + throw new Error('Inverse does not exist'); + } + return mod(x + n, n); +} + +/** + * Compute greatest common divisor between this and n + * @param {BigInt} aInput - Operand + * @param {BigInt} bInput - Operand + * @returns {BigInt} gcd + */ +export function gcd(aInput: bigint, bInput: bigint) { + let a = aInput; + let b = bInput; + while (b !== _0n) { + const tmp = b; + b = a % b; + a = tmp; + } + return a; +} + +/** + * Get this value as an exact Number (max 53 bits) + * Fails if this value is too large + * @returns {Number} + */ +export function bigIntToNumber(x: bigint) { + const number = Number(x); + if (number > Number.MAX_SAFE_INTEGER) { + // We throw and error to conform with the bn.js implementation + throw new Error('Number can only safely store up to 53 bits'); + } + return number; +} + +/** + * Get value of i-th bit + * @param {BigInt} x + * @param {Number} i - Bit index + * @returns {Number} Bit value. + */ +export function getBit(x:bigint, i: number) { + const bit = (x >> BigInt(i)) & _1n; + return bit === _0n ? 0 : 1; +} + +/** + * Compute bit length + */ +export function bitLength(x: bigint) { + // -1n >> -1n is -1n + // 1n >> 1n is 0n + const target = x < _0n ? BigInt(-1) : _0n; + let bitlen = 1; + let tmp = x; + // eslint-disable-next-line no-cond-assign + while ((tmp >>= _1n) !== target) { + bitlen++; + } + return bitlen; +} + +/** + * Compute byte length + */ +export function byteLength(x: bigint) { + const target = x < _0n ? BigInt(-1) : _0n; + const _8n = BigInt(8); + let len = 1; + let tmp = x; + // eslint-disable-next-line no-cond-assign + while ((tmp >>= _8n) !== target) { + len++; + } + return len; +} + +/** + * Get Uint8Array representation of this number + * @param {String} endian - Endianess of output array (defaults to 'be') + * @param {Number} length - Of output array + * @returns {Uint8Array} + */ +export function bigIntToUint8Array(x: bigint, endian = 'be', length: number) { + // we get and parse the hex string (https://coolaj86.com/articles/convert-js-bigints-to-typedarrays/) + // this is faster than shift+mod iterations + let hex = x.toString(16); + if (hex.length % 2 === 1) { + hex = '0' + hex; + } + + const rawLength = hex.length / 2; + const bytes = new Uint8Array(length || rawLength); + // parse hex + const offset = length ? length - rawLength : 0; + let i = 0; + while (i < rawLength) { + bytes[i + offset] = parseInt(hex.slice(2 * i, 2 * i + 2), 16); + i++; + } + + if (endian !== 'be') { + bytes.reverse(); + } + + return bytes; +} diff --git a/src/crypto/cipher/aes.js b/src/crypto/cipher/aes.js deleted file mode 100644 index 8a985079..00000000 --- a/src/crypto/cipher/aes.js +++ /dev/null @@ -1,26 +0,0 @@ -import { AES_ECB } from '@openpgp/asmcrypto.js/dist_es8/aes/ecb'; - -/** - * Javascript AES implementation. - * This is used as fallback if the native Crypto APIs are not available. - */ -function aes(length) { - const C = function(key) { - const aesECB = new AES_ECB(key); - - this.encrypt = function(block) { - return aesECB.encrypt(block); - }; - - this.decrypt = function(block) { - return aesECB.decrypt(block); - }; - }; - - C.blockSize = C.prototype.blockSize = 16; - C.keySize = C.prototype.keySize = length / 8; - - return C; -} - -export default aes; diff --git a/src/crypto/cipher/getCipher.js b/src/crypto/cipher/getCipher.js deleted file mode 100644 index a74ef75a..00000000 --- a/src/crypto/cipher/getCipher.js +++ /dev/null @@ -1,13 +0,0 @@ -import * as cipher from '.'; -import enums from '../../enums'; - -/** - * Get implementation of the given cipher - * @param {enums.symmetric} algo - * @returns {Object} - * @throws {Error} on invalid algo - */ -export default function getCipher(algo) { - const algoName = enums.read(enums.symmetric, algo); - return cipher[algoName]; -} diff --git a/src/crypto/cipher/index.js b/src/crypto/cipher/index.js index 4f96ef38..88c1b9cb 100644 --- a/src/crypto/cipher/index.js +++ b/src/crypto/cipher/index.js @@ -1,81 +1,72 @@ -/** - * @fileoverview Symmetric cryptography functions - * @module crypto/cipher - * @private - */ +import enums from '../../enums'; -import aes from './aes'; -import { DES, TripleDES } from './des'; -import CAST5 from './cast5'; -import TF from './twofish'; -import BF from './blowfish'; +export async function getLegacyCipher(algo) { + switch (algo) { + case enums.symmetric.aes128: + case enums.symmetric.aes192: + case enums.symmetric.aes256: + throw new Error('Not a legacy cipher'); + case enums.symmetric.cast5: + case enums.symmetric.blowfish: + case enums.symmetric.twofish: + case enums.symmetric.tripledes: { + const { legacyCiphers } = await import('./legacy_ciphers'); + const cipher = legacyCiphers.get(algo); + if (!cipher) { + throw new Error('Unsupported cipher algorithm'); + } + return cipher; + } + default: + throw new Error('Unsupported cipher algorithm'); + } +} /** - * AES-128 encryption and decryption (ID 7) - * @function - * @param {String} key - 128-bit key - * @see {@link https://github.com/asmcrypto/asmcrypto.js|asmCrypto} - * @see {@link https://csrc.nist.gov/publications/fips/fips197/fips-197.pdf|NIST FIPS-197} - * @returns {Object} + * Get block size for given cipher algo + * @param {module:enums.symmetric} algo - alrogithm identifier */ -export const aes128 = aes(128); +function getCipherBlockSize(algo) { + switch (algo) { + case enums.symmetric.aes128: + case enums.symmetric.aes192: + case enums.symmetric.aes256: + case enums.symmetric.twofish: + return 16; + case enums.symmetric.blowfish: + case enums.symmetric.cast5: + case enums.symmetric.tripledes: + return 8; + default: + throw new Error('Unsupported cipher'); + } +} + /** - * AES-128 Block Cipher (ID 8) - * @function - * @param {String} key - 192-bit key - * @see {@link https://github.com/asmcrypto/asmcrypto.js|asmCrypto} - * @see {@link https://csrc.nist.gov/publications/fips/fips197/fips-197.pdf|NIST FIPS-197} - * @returns {Object} + * Get key size for given cipher algo + * @param {module:enums.symmetric} algo - alrogithm identifier */ -export const aes192 = aes(192); +function getCipherKeySize(algo) { + switch (algo) { + case enums.symmetric.aes128: + case enums.symmetric.blowfish: + case enums.symmetric.cast5: + return 16; + case enums.symmetric.aes192: + case enums.symmetric.tripledes: + return 24; + case enums.symmetric.aes256: + case enums.symmetric.twofish: + return 32; + default: + throw new Error('Unsupported cipher'); + } +} + /** - * AES-128 Block Cipher (ID 9) - * @function - * @param {String} key - 256-bit key - * @see {@link https://github.com/asmcrypto/asmcrypto.js|asmCrypto} - * @see {@link https://csrc.nist.gov/publications/fips/fips197/fips-197.pdf|NIST FIPS-197} - * @returns {Object} + * Get block and key size for given cipher algo + * @param {module:enums.symmetric} algo - alrogithm identifier */ -export const aes256 = aes(256); -// Not in OpenPGP specifications -export const des = DES; -/** - * Triple DES Block Cipher (ID 2) - * @function - * @param {String} key - 192-bit key - * @see {@link https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-67r2.pdf|NIST SP 800-67} - * @returns {Object} - */ -export const tripledes = TripleDES; -/** - * CAST-128 Block Cipher (ID 3) - * @function - * @param {String} key - 128-bit key - * @see {@link https://tools.ietf.org/html/rfc2144|The CAST-128 Encryption Algorithm} - * @returns {Object} - */ -export const cast5 = CAST5; -/** - * Twofish Block Cipher (ID 10) - * @function - * @param {String} key - 256-bit key - * @see {@link https://tools.ietf.org/html/rfc4880#ref-TWOFISH|TWOFISH} - * @returns {Object} - */ -export const twofish = TF; -/** - * Blowfish Block Cipher (ID 4) - * @function - * @param {String} key - 128-bit key - * @see {@link https://tools.ietf.org/html/rfc4880#ref-BLOWFISH|BLOWFISH} - * @returns {Object} - */ -export const blowfish = BF; -/** - * Not implemented - * @function - * @throws {Error} - */ -export const idea = function() { - throw new Error('IDEA symmetric-key algorithm not implemented'); -}; +export function getCipherParams(algo) { + return { keySize: getCipherKeySize(algo), blockSize: getCipherBlockSize(algo) }; +} diff --git a/src/crypto/cipher/legacy_ciphers.js b/src/crypto/cipher/legacy_ciphers.js new file mode 100644 index 00000000..f091d471 --- /dev/null +++ b/src/crypto/cipher/legacy_ciphers.js @@ -0,0 +1,17 @@ +/** + * This file is needed to dynamic import the legacy ciphers. + * Separate dynamic imports are not convenient as they result in multiple chunks. + */ + +import { TripleDES } from './des'; +import CAST5 from './cast5'; +import TwoFish from './twofish'; +import BlowFish from './blowfish'; +import enums from '../../enums'; + +export const legacyCiphers = new Map([ + [enums.symmetric.tripledes, TripleDES], + [enums.symmetric.cast5, CAST5], + [enums.symmetric.blowfish, BlowFish], + [enums.symmetric.twofish, TwoFish] +]); diff --git a/src/crypto/cmac.js b/src/crypto/cmac.js index c183a70a..6159cdf7 100644 --- a/src/crypto/cmac.js +++ b/src/crypto/cmac.js @@ -2,10 +2,9 @@ * @fileoverview This module implements AES-CMAC on top of * native AES-CBC using either the WebCrypto API or Node.js' crypto API. * @module crypto/cmac - * @private */ -import { AES_CBC } from '@openpgp/asmcrypto.js/dist_es8/aes/cbc'; +import { cbc as nobleAesCbc } from '@noble/ciphers/aes'; import util from '../util'; const webCrypto = util.getWebCrypto(); @@ -73,13 +72,6 @@ export default async function CMAC(key) { } async function CBC(key) { - if (util.getWebCrypto() && key.length !== 24) { // WebCrypto (no 192 bit support) see: https://www.chromium.org/blink/webcrypto#TOC-AES-support - key = await webCrypto.importKey('raw', key, { name: 'AES-CBC', length: key.length * 8 }, false, ['encrypt']); - return async function(pt) { - const ct = await webCrypto.encrypt({ name: 'AES-CBC', iv: zeroBlock, length: blockLength * 8 }, key, pt); - return new Uint8Array(ct).subarray(0, ct.byteLength - blockLength); - }; - } if (util.getNodeCrypto()) { // Node crypto library return async function(pt) { const en = new nodeCrypto.createCipheriv('aes-' + (key.length * 8) + '-cbc', key, zeroBlock); @@ -87,8 +79,25 @@ async function CBC(key) { return new Uint8Array(ct); }; } - // asm.js fallback + + if (util.getWebCrypto()) { + try { + key = await webCrypto.importKey('raw', key, { name: 'AES-CBC', length: key.length * 8 }, false, ['encrypt']); + return async function(pt) { + const ct = await webCrypto.encrypt({ name: 'AES-CBC', iv: zeroBlock, length: blockLength * 8 }, key, pt); + return new Uint8Array(ct).subarray(0, ct.byteLength - blockLength); + }; + } catch (err) { + // no 192 bit support in Chromium, which throws `OperationError`, see: https://www.chromium.org/blink/webcrypto#TOC-AES-support + if (err.name !== 'NotSupportedError' && + !(key.length === 24 && err.name === 'OperationError')) { + throw err; + } + util.printDebugError('Browser did not support operation: ' + err.message); + } + } + return async function(pt) { - return AES_CBC.encrypt(pt, key, false, zeroBlock); + return nobleAesCbc(key, zeroBlock, { disablePadding: true }).encrypt(pt); }; } diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js index 0cb86be1..d0c0fa7a 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -21,19 +21,17 @@ * @fileoverview Provides functions for asymmetric encryption and decryption as * well as key generation and parameter handling for all public-key cryptosystems. * @module crypto/crypto - * @private */ import publicKey from './public_key'; import mode from './mode'; import { getRandomBytes } from './random'; -import getCipher from './cipher/getCipher'; +import { getCipherParams } from './cipher'; import ECDHSymkey from '../type/ecdh_symkey'; import KDFParams from '../type/kdf_params'; import enums from '../enums'; import util from '../util'; import OID from '../type/oid'; -import { CurveWithOID } from './public_key/elliptic/oid_curves'; import { UnsupportedError } from '../packet/packet'; import ECDHXSymmetricKey from '../type/ecdh_x_symkey'; @@ -41,7 +39,7 @@ import ECDHXSymmetricKey from '../type/ecdh_x_symkey'; * Encrypts data using specified algorithm and public key parameters. * See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1} for public key algorithms. * @param {module:enums.publicKey} keyAlgo - Public key algorithm - * @param {module:enums.symmetric} symmetricAlgo - Cipher algorithm + * @param {module:enums.symmetric|null} symmetricAlgo - Cipher algorithm (v3 only) * @param {Object} publicParams - Algorithm-specific public key parameters * @param {Uint8Array} data - Session key data to be encrypted * @param {Uint8Array} fingerprint - Recipient fingerprint @@ -66,10 +64,11 @@ export async function publicKeyEncrypt(keyAlgo, symmetricAlgo, publicParams, dat oid, kdfParams, data, Q, fingerprint); return { V, C: new ECDHSymkey(C) }; } - case enums.publicKey.x25519: { - if (!util.isAES(symmetricAlgo)) { + case enums.publicKey.x25519: + case enums.publicKey.x448: { + if (symmetricAlgo && !util.isAES(symmetricAlgo)) { // see https://gitlab.com/openpgp-wg/rfc4880bis/-/merge_requests/276 - throw new Error('X25519 keys can only encrypt AES session keys'); + throw new Error('X25519 and X448 keys can only encrypt AES session keys'); } const { A } = publicParams; const { ephemeralPublicKey, wrappedKey } = await publicKey.elliptic.ecdhX.encrypt( @@ -118,11 +117,12 @@ export async function publicKeyDecrypt(algo, publicKeyParams, privateKeyParams, return publicKey.elliptic.ecdh.decrypt( oid, kdfParams, V, C.data, Q, d, fingerprint); } - case enums.publicKey.x25519: { + case enums.publicKey.x25519: + case enums.publicKey.x448: { const { A } = publicKeyParams; const { k } = privateKeyParams; const { ephemeralPublicKey, C } = sessionKeyParams; - if (!util.isAES(C.algorithm)) { + if (C.algorithm !== null && !util.isAES(C.algorithm)) { throw new Error('AES session key expected'); } return publicKey.elliptic.ecdhX.decrypt( @@ -171,6 +171,9 @@ export function parsePublicKeyParams(algo, bytes) { case enums.publicKey.eddsaLegacy: { const oid = new OID(); read += oid.read(bytes); checkSupportedCurve(oid); + if (oid.getName() !== enums.curve.ed25519Legacy) { + throw new Error('Unexpected OID for eddsaLegacy'); + } let Q = util.readMPI(bytes.subarray(read)); read += Q.length + 2; Q = util.leftPad(Q, 33); return { read: read, publicParams: { oid, Q } }; @@ -183,8 +186,10 @@ export function parsePublicKeyParams(algo, bytes) { return { read: read, publicParams: { oid, Q, kdfParams } }; } case enums.publicKey.ed25519: - case enums.publicKey.x25519: { - const A = bytes.subarray(read, read + 32); read += A.length; + case enums.publicKey.ed448: + case enums.publicKey.x25519: + case enums.publicKey.x448: { + const A = util.readExactSubarray(bytes, read, read + getCurvePayloadSize(algo)); read += A.length; return { read, publicParams: { A } }; } default: @@ -218,23 +223,30 @@ export function parsePrivateKeyParams(algo, bytes, publicParams) { } case enums.publicKey.ecdsa: case enums.publicKey.ecdh: { - const curve = new CurveWithOID(publicParams.oid); + const payloadSize = getCurvePayloadSize(algo, publicParams.oid); let d = util.readMPI(bytes.subarray(read)); read += d.length + 2; - d = util.leftPad(d, curve.payloadSize); + d = util.leftPad(d, payloadSize); return { read, privateParams: { d } }; } case enums.publicKey.eddsaLegacy: { - const curve = new CurveWithOID(publicParams.oid); + const payloadSize = getCurvePayloadSize(algo, publicParams.oid); + if (publicParams.oid.getName() !== enums.curve.ed25519Legacy) { + throw new Error('Unexpected OID for eddsaLegacy'); + } let seed = util.readMPI(bytes.subarray(read)); read += seed.length + 2; - seed = util.leftPad(seed, curve.payloadSize); + seed = util.leftPad(seed, payloadSize); return { read, privateParams: { seed } }; } - case enums.publicKey.ed25519: { - const seed = bytes.subarray(read, read + 32); read += seed.length; + case enums.publicKey.ed25519: + case enums.publicKey.ed448: { + const payloadSize = getCurvePayloadSize(algo); + const seed = util.readExactSubarray(bytes, read, read + payloadSize); read += seed.length; return { read, privateParams: { seed } }; } - case enums.publicKey.x25519: { - const k = bytes.subarray(read, read + 32); read += k.length; + case enums.publicKey.x25519: + case enums.publicKey.x448: { + const payloadSize = getCurvePayloadSize(algo); + const k = util.readExactSubarray(bytes, read, read + payloadSize); read += k.length; return { read, privateParams: { k } }; } default: @@ -274,13 +286,15 @@ export function parseEncSessionKeyParams(algo, bytes) { const C = new ECDHSymkey(); C.read(bytes.subarray(read)); return { V, C }; } - // Algorithm-Specific Fields for X25519 encrypted session keys: - // - 32 octets representing an ephemeral X25519 public key. + // Algorithm-Specific Fields for X25519 or X448 encrypted session keys: + // - 32 octets representing an ephemeral X25519 public key (or 57 octets for X448). // - A one-octet size of the following fields. // - The one-octet algorithm identifier, if it was passed (in the case of a v3 PKESK packet). // - The encrypted session key. - case enums.publicKey.x25519: { - const ephemeralPublicKey = bytes.subarray(read, read + 32); read += ephemeralPublicKey.length; + case enums.publicKey.x25519: + case enums.publicKey.x448: { + const pointSize = getCurvePayloadSize(algo); + const ephemeralPublicKey = util.readExactSubarray(bytes, read, read + pointSize); read += ephemeralPublicKey.length; const C = new ECDHXSymmetricKey(); C.read(bytes.subarray(read)); return { ephemeralPublicKey, C }; } @@ -297,7 +311,12 @@ export function parseEncSessionKeyParams(algo, bytes) { */ export function serializeParams(algo, params) { // Some algorithms do not rely on MPIs to store the binary params - const algosWithNativeRepresentation = new Set([enums.publicKey.ed25519, enums.publicKey.x25519]); + const algosWithNativeRepresentation = new Set([ + enums.publicKey.ed25519, + enums.publicKey.x25519, + enums.publicKey.ed448, + enums.publicKey.x448 + ]); const orderedParams = Object.keys(params).map(name => { const param = params[name]; if (!util.isUint8Array(param)) return param.write(); @@ -318,12 +337,11 @@ export function generateParams(algo, bits, oid) { switch (algo) { case enums.publicKey.rsaEncrypt: case enums.publicKey.rsaEncryptSign: - case enums.publicKey.rsaSign: { + case enums.publicKey.rsaSign: return publicKey.rsa.generate(bits, 65537).then(({ n, e, d, p, q, u }) => ({ privateParams: { d, p, q, u }, publicParams: { n, e } })); - } case enums.publicKey.ecdsa: return publicKey.elliptic.generate(oid).then(({ oid, Q, secret }) => ({ privateParams: { d: secret }, @@ -344,11 +362,13 @@ export function generateParams(algo, bits, oid) { } })); case enums.publicKey.ed25519: + case enums.publicKey.ed448: return publicKey.elliptic.eddsa.generate(algo).then(({ A, seed }) => ({ privateParams: { seed }, publicParams: { A } })); case enums.publicKey.x25519: + case enums.publicKey.x448: return publicKey.elliptic.ecdhX.generate(algo).then(({ A, k }) => ({ privateParams: { k }, publicParams: { A } @@ -403,12 +423,14 @@ export async function validateParams(algo, publicParams, privateParams) { const { seed } = privateParams; return publicKey.elliptic.eddsaLegacy.validateParams(oid, Q, seed); } - case enums.publicKey.ed25519: { + case enums.publicKey.ed25519: + case enums.publicKey.ed448: { const { A } = publicParams; const { seed } = privateParams; return publicKey.elliptic.eddsa.validateParams(algo, A, seed); } - case enums.publicKey.x25519: { + case enums.publicKey.x25519: + case enums.publicKey.x448: { const { A } = publicParams; const { k } = privateParams; return publicKey.elliptic.ecdhX.validateParams(algo, A, k); @@ -426,7 +448,7 @@ export async function validateParams(algo, publicParams, privateParams) { * @async */ export async function getPrefixRandom(algo) { - const { blockSize } = getCipher(algo); + const { blockSize } = getCipherParams(algo); const prefixrandom = await getRandomBytes(blockSize); const repeat = new Uint8Array([prefixrandom[prefixrandom.length - 2], prefixrandom[prefixrandom.length - 1]]); return util.concat([prefixrandom, repeat]); @@ -439,7 +461,7 @@ export async function getPrefixRandom(algo) { * @returns {Uint8Array} Random bytes as a string to be used as a key. */ export function generateSessionKey(algo) { - const { keySize } = getCipher(algo); + const { keySize } = getCipherParams(algo); return getRandomBytes(keySize); } @@ -454,8 +476,6 @@ export function getAEADMode(algo) { return mode[algoName]; } -export { getCipher }; - /** * Check whether the given curve OID is supported * @param {module:type/oid} oid - EC object identifier @@ -470,7 +490,29 @@ function checkSupportedCurve(oid) { } /** - * Get preferred hash algo for a given elliptic algo + * Get encoded secret size for a given elliptic algo + * @param {module:enums.publicKey} algo - alrogithm identifier + * @param {module:type/oid} [oid] - curve OID if needed by algo + */ +export function getCurvePayloadSize(algo, oid) { + switch (algo) { + case enums.publicKey.ecdsa: + case enums.publicKey.ecdh: + case enums.publicKey.eddsaLegacy: + return new publicKey.elliptic.CurveWithOID(oid).payloadSize; + case enums.publicKey.ed25519: + case enums.publicKey.ed448: + return publicKey.elliptic.eddsa.getPayloadSize(algo); + case enums.publicKey.x25519: + case enums.publicKey.x448: + return publicKey.elliptic.ecdhX.getPayloadSize(algo); + default: + throw new Error('Unknown elliptic algo'); + } +} + +/** + * Get preferred signing hash algo for a given elliptic algo * @param {module:enums.publicKey} algo - alrogithm identifier * @param {module:type/oid} [oid] - curve OID if needed by algo */ @@ -480,8 +522,12 @@ export function getPreferredCurveHashAlgo(algo, oid) { case enums.publicKey.eddsaLegacy: return publicKey.elliptic.getPreferredHashAlgo(oid); case enums.publicKey.ed25519: + case enums.publicKey.ed448: return publicKey.elliptic.eddsa.getPreferredHashAlgo(algo); default: throw new Error('Unknown elliptic signing algo'); } } + + +export { getCipherParams }; diff --git a/src/crypto/hash/index.js b/src/crypto/hash/index.js index 0765881e..3332a1ce 100644 --- a/src/crypto/hash/index.js +++ b/src/crypto/hash/index.js @@ -3,19 +3,11 @@ * @see {@link https://github.com/asmcrypto/asmcrypto.js|asmCrypto} * @see {@link https://github.com/indutny/hash.js|hash.js} * @module crypto/hash - * @private */ -import { Sha1 } from '@openpgp/asmcrypto.js/dist_es8/hash/sha1/sha1'; -import { Sha256 } from '@openpgp/asmcrypto.js/dist_es8/hash/sha256/sha256'; -import sha224 from 'hash.js/lib/hash/sha/224'; -import sha384 from 'hash.js/lib/hash/sha/384'; -import sha512 from 'hash.js/lib/hash/sha/512'; -import { ripemd160 } from 'hash.js/lib/hash/ripemd'; import * as stream from '@openpgp/web-stream-tools'; import md5 from './md5'; import util from '../../util'; -import defaultConfig from '../../config'; import enums from '../../enums'; const webCrypto = util.getWebCrypto(); @@ -34,65 +26,47 @@ function nodeHash(type) { }; } -function hashjsHash(hash, webCryptoHash) { - return async function(data, config = defaultConfig) { - if (stream.isArrayStream(data)) { - data = await stream.readToEnd(data); - } - if (!util.isStream(data) && webCrypto && webCryptoHash && data.length >= config.minBytesForWebCrypto) { - return new Uint8Array(await webCrypto.digest(webCryptoHash, data)); - } - const hashInstance = hash(); - return stream.transform(data, value => { - hashInstance.update(value); - }, () => new Uint8Array(hashInstance.digest())); +function nobleHash(nobleHashName, webCryptoHashName) { + const getNobleHash = async () => { + const { nobleHashes } = await import('./noble_hashes'); + const hash = nobleHashes.get(nobleHashName); + if (!hash) throw new Error('Unsupported hash'); + return hash; }; -} -function asmcryptoHash(hash, webCryptoHash) { - return async function(data, config = defaultConfig) { + return async function(data) { if (stream.isArrayStream(data)) { data = await stream.readToEnd(data); } if (util.isStream(data)) { - const hashInstance = new hash(); + const hash = await getNobleHash(); + + const hashInstance = hash.create(); return stream.transform(data, value => { - hashInstance.process(value); - }, () => hashInstance.finish().result); - } else if (webCrypto && webCryptoHash && data.length >= config.minBytesForWebCrypto) { - return new Uint8Array(await webCrypto.digest(webCryptoHash, data)); + hashInstance.update(value); + }, () => hashInstance.digest()); + } else if (webCrypto && webCryptoHashName) { + return new Uint8Array(await webCrypto.digest(webCryptoHashName, data)); } else { - return hash.bytes(data); + const hash = await getNobleHash(); + + return hash(data); } }; } -const hashFunctions = { - md5: nodeHash('md5') || md5, - sha1: nodeHash('sha1') || asmcryptoHash(Sha1, 'SHA-1'), - sha224: nodeHash('sha224') || hashjsHash(sha224), - sha256: nodeHash('sha256') || asmcryptoHash(Sha256, 'SHA-256'), - sha384: nodeHash('sha384') || hashjsHash(sha384, 'SHA-384'), - sha512: nodeHash('sha512') || hashjsHash(sha512, 'SHA-512'), // asmcrypto sha512 is huge. - ripemd: nodeHash('ripemd160') || hashjsHash(ripemd160) -}; - export default { /** @see module:md5 */ - md5: hashFunctions.md5, - /** @see asmCrypto */ - sha1: hashFunctions.sha1, - /** @see hash.js */ - sha224: hashFunctions.sha224, - /** @see asmCrypto */ - sha256: hashFunctions.sha256, - /** @see hash.js */ - sha384: hashFunctions.sha384, - /** @see asmCrypto */ - sha512: hashFunctions.sha512, - /** @see hash.js */ - ripemd: hashFunctions.ripemd, + md5: nodeHash('md5') || md5, + sha1: nodeHash('sha1') || nobleHash('sha1', 'SHA-1'), + sha224: nodeHash('sha224') || nobleHash('sha224'), + sha256: nodeHash('sha256') || nobleHash('sha256', 'SHA-256'), + sha384: nodeHash('sha384') || nobleHash('sha384', 'SHA-384'), + sha512: nodeHash('sha512') || nobleHash('sha512', 'SHA-512'), + ripemd: nodeHash('ripemd160') || nobleHash('ripemd160'), + sha3_256: nodeHash('sha3-256') || nobleHash('sha3_256'), + sha3_512: nodeHash('sha3-512') || nobleHash('sha3_512'), /** * Create a hash on the specified data using the specified algorithm @@ -116,8 +90,12 @@ export default { return this.sha512(data); case enums.hash.sha224: return this.sha224(data); + case enums.hash.sha3_256: + return this.sha3_256(data); + case enums.hash.sha3_512: + return this.sha3_512(data); default: - throw new Error('Invalid hash function.'); + throw new Error('Unsupported hash function'); } }, @@ -141,6 +119,10 @@ export default { return 64; case enums.hash.sha224: return 28; + case enums.hash.sha3_256: + return 32; + case enums.hash.sha3_512: + return 64; default: throw new Error('Invalid hash algorithm.'); } diff --git a/src/crypto/hash/noble_hashes.js b/src/crypto/hash/noble_hashes.js new file mode 100644 index 00000000..0d0ce7b4 --- /dev/null +++ b/src/crypto/hash/noble_hashes.js @@ -0,0 +1,22 @@ +/** + * This file is needed to dynamic import the noble-hashes. + * Separate dynamic imports are not convenient as they result in too many chunks, + * which share a lot of code anyway. + */ + +import { sha1 } from '@noble/hashes/sha1'; +import { sha224, sha256 } from '@noble/hashes/sha256'; +import { sha384, sha512 } from '@noble/hashes/sha512'; +import { sha3_256, sha3_512 } from '@noble/hashes/sha3'; +import { ripemd160 } from '@noble/hashes/ripemd160'; + +export const nobleHashes = new Map(Object.entries({ + sha1, + sha224, + sha256, + sha384, + sha512, + sha3_256, + sha3_512, + ripemd160 +})); diff --git a/src/crypto/hkdf.js b/src/crypto/hkdf.js index a14d751c..361703b1 100644 --- a/src/crypto/hkdf.js +++ b/src/crypto/hkdf.js @@ -1,61 +1,18 @@ /** * @fileoverview This module implements HKDF using either the WebCrypto API or Node.js' crypto API. * @module crypto/hkdf - * @private */ import enums from '../enums'; import util from '../util'; const webCrypto = util.getWebCrypto(); -const nodeCrypto = util.getNodeCrypto(); -const nodeSubtleCrypto = nodeCrypto && nodeCrypto.webcrypto && nodeCrypto.webcrypto.subtle; -export default async function HKDF(hashAlgo, inputKey, salt, info, outLen) { +export default async function computeHKDF(hashAlgo, inputKey, salt, info, outLen) { const hash = enums.read(enums.webHash, hashAlgo); if (!hash) throw new Error('Hash algo not supported with HKDF'); - if (webCrypto || nodeSubtleCrypto) { - const crypto = webCrypto || nodeSubtleCrypto; - const importedKey = await crypto.importKey('raw', inputKey, 'HKDF', false, ['deriveBits']); - const bits = await crypto.deriveBits({ name: 'HKDF', hash, salt, info }, importedKey, outLen * 8); - return new Uint8Array(bits); - } - - if (nodeCrypto) { - const hashAlgoName = enums.read(enums.hash, hashAlgo); - // Node-only HKDF implementation based on https://www.rfc-editor.org/rfc/rfc5869 - - const computeHMAC = (hmacKey, hmacMessage) => nodeCrypto.createHmac(hashAlgoName, hmacKey).update(hmacMessage).digest(); - // Step 1: Extract - // PRK = HMAC-Hash(salt, IKM) - const pseudoRandomKey = computeHMAC(salt, inputKey); - - const hashLen = pseudoRandomKey.length; - - // Step 2: Expand - // HKDF-Expand(PRK, info, L) -> OKM - const n = Math.ceil(outLen / hashLen); - const outputKeyingMaterial = new Uint8Array(n * hashLen); - - // HMAC input buffer updated at each iteration - const roundInput = new Uint8Array(hashLen + info.length + 1); - // T_i and last byte are updated at each iteration, but `info` remains constant - roundInput.set(info, hashLen); - - for (let i = 0; i < n; i++) { - // T(0) = empty string (zero length) - // T(i) = HMAC-Hash(PRK, T(i-1) | info | i) - roundInput[roundInput.length - 1] = i + 1; - // t = T(i+1) - const t = computeHMAC(pseudoRandomKey, i > 0 ? roundInput : roundInput.subarray(hashLen)); - roundInput.set(t, 0); - - outputKeyingMaterial.set(t, i * hashLen); - } - - return outputKeyingMaterial.subarray(0, outLen); - } - - throw new Error('No HKDF implementation available'); + const importedKey = await webCrypto.importKey('raw', inputKey, 'HKDF', false, ['deriveBits']); + const bits = await webCrypto.deriveBits({ name: 'HKDF', hash, salt, info }, importedKey, outLen * 8); + return new Uint8Array(bits); } diff --git a/src/crypto/index.js b/src/crypto/index.js index ab6b166a..460140c1 100644 --- a/src/crypto/index.js +++ b/src/crypto/index.js @@ -7,7 +7,6 @@ * @see module:crypto/random * @see module:crypto/hash * @module crypto - * @private */ import * as cipher from './cipher'; diff --git a/src/crypto/mode/cfb.js b/src/crypto/mode/cfb.js index e16f88b6..829a0d29 100644 --- a/src/crypto/mode/cfb.js +++ b/src/crypto/mode/cfb.js @@ -19,14 +19,14 @@ /** * @module crypto/mode/cfb - * @private */ -import { AES_CFB } from '@openpgp/asmcrypto.js/dist_es8/aes/cfb'; +import { cfb as nobleAesCfb, unsafe as nobleAesHelpers } from '@noble/ciphers/aes'; + import * as stream from '@openpgp/web-stream-tools'; -import getCipher from '../cipher/getCipher'; import util from '../../util'; import enums from '../../enums'; +import { getLegacyCipher, getCipherParams } from '../cipher'; const webCrypto = util.getWebCrypto(); const nodeCrypto = util.getNodeCrypto(); @@ -61,8 +61,8 @@ export async function encrypt(algo, key, plaintext, iv, config) { return aesEncrypt(algo, key, plaintext, iv, config); } - const Cipher = getCipher(algo); - const cipherfn = new Cipher(key); + const LegacyCipher = await getLegacyCipher(algo); + const cipherfn = new LegacyCipher(key); const block_size = cipherfn.blockSize; const blockc = iv.slice(); @@ -97,15 +97,15 @@ export async function encrypt(algo, key, plaintext, iv, config) { */ export async function decrypt(algo, key, ciphertext, iv) { const algoName = enums.read(enums.symmetric, algo); - if (util.getNodeCrypto() && nodeAlgos[algoName]) { // Node crypto library. + if (nodeCrypto && nodeAlgos[algoName]) { // Node crypto library. return nodeDecrypt(algo, key, ciphertext, iv); } if (util.isAES(algo)) { return aesDecrypt(algo, key, ciphertext, iv); } - const Cipher = getCipher(algo); - const cipherfn = new Cipher(key); + const LegacyCipher = await getLegacyCipher(algo); + const cipherfn = new LegacyCipher(key); const block_size = cipherfn.blockSize; let blockp = iv; @@ -130,43 +130,231 @@ export async function decrypt(algo, key, ciphertext, iv) { return stream.transform(ciphertext, process, process); } -function aesEncrypt(algo, key, pt, iv, config) { - if ( - util.getWebCrypto() && - key.length !== 24 && // Chrome doesn't support 192 bit keys, see https://www.chromium.org/blink/webcrypto#TOC-AES-support - !util.isStream(pt) && - pt.length >= 3000 * config.minBytesForWebCrypto // Default to a 3MB minimum. Chrome is pretty slow for small messages, see: https://bugs.chromium.org/p/chromium/issues/detail?id=701188#c2 - ) { // Web Crypto - return webEncrypt(algo, key, pt, iv); +class WebCryptoEncryptor { + constructor(algo, key, iv) { + const { blockSize } = getCipherParams(algo); + this.key = key; + this.prevBlock = iv; + this.nextBlock = new Uint8Array(blockSize); + this.i = 0; // pointer inside next block + this.blockSize = blockSize; + this.zeroBlock = new Uint8Array(this.blockSize); + } + + static async isSupported(algo) { + const { keySize } = getCipherParams(algo); + return webCrypto.importKey('raw', new Uint8Array(keySize), 'aes-cbc', false, ['encrypt']) + .then(() => true, () => false); + } + + async _runCBC(plaintext, nonZeroIV) { + const mode = 'AES-CBC'; + this.keyRef = this.keyRef || await webCrypto.importKey('raw', this.key, mode, false, ['encrypt']); + const ciphertext = await webCrypto.encrypt( + { name: mode, iv: nonZeroIV || this.zeroBlock }, + this.keyRef, + plaintext + ); + return new Uint8Array(ciphertext).subarray(0, plaintext.length); + } + + async encryptChunk(value) { + const missing = this.nextBlock.length - this.i; + const added = value.subarray(0, missing); + this.nextBlock.set(added, this.i); + if ((this.i + value.length) >= (2 * this.blockSize)) { + const leftover = (value.length - missing) % this.blockSize; + const plaintext = util.concatUint8Array([ + this.nextBlock, + value.subarray(missing, value.length - leftover) + ]); + const toEncrypt = util.concatUint8Array([ + this.prevBlock, + plaintext.subarray(0, plaintext.length - this.blockSize) // stop one block "early", since we only need to xor the plaintext and pass it over as prevBlock + ]); + + const encryptedBlocks = await this._runCBC(toEncrypt); + xorMut(encryptedBlocks, plaintext); + this.prevBlock = encryptedBlocks.slice(-this.blockSize); + + // take care of leftover data + if (leftover > 0) this.nextBlock.set(value.subarray(-leftover)); + this.i = leftover; + + return encryptedBlocks; + } + + this.i += added.length; + let encryptedBlock; + if (this.i === this.nextBlock.length) { // block ready to be encrypted + const curBlock = this.nextBlock; + encryptedBlock = await this._runCBC(this.prevBlock); + xorMut(encryptedBlock, curBlock); + this.prevBlock = encryptedBlock.slice(); + this.i = 0; + + const remaining = value.subarray(added.length); + this.nextBlock.set(remaining, this.i); + this.i += remaining.length; + } else { + encryptedBlock = new Uint8Array(); + } + + return encryptedBlock; + } + + async finish() { + let result; + if (this.i === 0) { // nothing more to encrypt + result = new Uint8Array(); + } else { + this.nextBlock = this.nextBlock.subarray(0, this.i); + const curBlock = this.nextBlock; + const encryptedBlock = await this._runCBC(this.prevBlock); + xorMut(encryptedBlock, curBlock); + result = encryptedBlock.subarray(0, curBlock.length); + } + + this.clearSensitiveData(); + return result; + } + + clearSensitiveData() { + this.nextBlock.fill(0); + this.prevBlock.fill(0); + this.keyRef = null; + this.key = null; + } + + async encrypt(plaintext) { + // plaintext is internally padded to block length before encryption + const encryptedWithPadding = await this._runCBC( + util.concatUint8Array([new Uint8Array(this.blockSize), plaintext]), + this.iv + ); + // drop encrypted padding + const ct = encryptedWithPadding.subarray(0, plaintext.length); + xorMut(ct, plaintext); + this.clearSensitiveData(); + return ct; } - // asm.js fallback - const cfb = new AES_CFB(key, iv); - return stream.transform(pt, value => cfb.aes.AES_Encrypt_process(value), () => cfb.aes.AES_Encrypt_finish()); } -function aesDecrypt(algo, key, ct, iv) { - if (util.isStream(ct)) { - const cfb = new AES_CFB(key, iv); - return stream.transform(ct, value => cfb.aes.AES_Decrypt_process(value), () => cfb.aes.AES_Decrypt_finish()); +class NobleStreamProcessor { + constructor(forEncryption, algo, key, iv) { + this.forEncryption = forEncryption; + const { blockSize } = getCipherParams(algo); + this.key = nobleAesHelpers.expandKeyLE(key); + + if (iv.byteOffset % 4 !== 0) iv = iv.slice(); // aligned arrays required by noble-ciphers + this.prevBlock = getUint32Array(iv); + this.nextBlock = new Uint8Array(blockSize); + this.i = 0; // pointer inside next block + this.blockSize = blockSize; } - return AES_CFB.decrypt(ct, key, iv); + + _runCFB(src) { + const src32 = getUint32Array(src); + const dst = new Uint8Array(src.length); + const dst32 = getUint32Array(dst); + for (let i = 0; i + 4 <= dst32.length; i += 4) { + const { s0: e0, s1: e1, s2: e2, s3: e3 } = nobleAesHelpers.encrypt(this.key, this.prevBlock[0], this.prevBlock[1], this.prevBlock[2], this.prevBlock[3]); + dst32[i + 0] = src32[i + 0] ^ e0; + dst32[i + 1] = src32[i + 1] ^ e1; + dst32[i + 2] = src32[i + 2] ^ e2; + dst32[i + 3] = src32[i + 3] ^ e3; + this.prevBlock = (this.forEncryption ? dst32 : src32).slice(i, i + 4); + } + return dst; + } + + async processChunk(value) { + const missing = this.nextBlock.length - this.i; + const added = value.subarray(0, missing); + this.nextBlock.set(added, this.i); + + if ((this.i + value.length) >= (2 * this.blockSize)) { + const leftover = (value.length - missing) % this.blockSize; + const toProcess = util.concatUint8Array([ + this.nextBlock, + value.subarray(missing, value.length - leftover) + ]); + + const processedBlocks = this._runCFB(toProcess); + + // take care of leftover data + if (leftover > 0) this.nextBlock.set(value.subarray(-leftover)); + this.i = leftover; + + return processedBlocks; + } + + this.i += added.length; + + let processedBlock; + if (this.i === this.nextBlock.length) { // block ready to be encrypted + processedBlock = this._runCFB(this.nextBlock); + this.i = 0; + + const remaining = value.subarray(added.length); + this.nextBlock.set(remaining, this.i); + this.i += remaining.length; + } else { + processedBlock = new Uint8Array(); + } + + return processedBlock; + } + + async finish() { + let result; + if (this.i === 0) { // nothing more to encrypt + result = new Uint8Array(); + } else { + const processedBlock = this._runCFB(this.nextBlock); + + result = processedBlock.subarray(0, this.i); + } + + this.clearSensitiveData(); + return result; + } + + clearSensitiveData() { + this.nextBlock.fill(0); + this.prevBlock.fill(0); + this.key.fill(0); + } +} + + +async function aesEncrypt(algo, key, pt, iv) { + if (webCrypto && await WebCryptoEncryptor.isSupported(algo)) { // Chromium does not implement AES with 192-bit keys + const cfb = new WebCryptoEncryptor(algo, key, iv); + return util.isStream(pt) ? stream.transform(pt, value => cfb.encryptChunk(value), () => cfb.finish()) : cfb.encrypt(pt); + } else if (util.isStream(pt)) { // async callbacks are not accepted by stream.transform unless the input is a stream + const cfb = new NobleStreamProcessor(true, algo, key, iv); + return stream.transform(pt, value => cfb.processChunk(value), () => cfb.finish()); + } + return nobleAesCfb(key, iv).encrypt(pt); +} + +async function aesDecrypt(algo, key, ct, iv) { + if (util.isStream(ct)) { + const cfb = new NobleStreamProcessor(false, algo, key, iv); + return stream.transform(ct, value => cfb.processChunk(value), () => cfb.finish()); + } + return nobleAesCfb(key, iv).decrypt(ct); } function xorMut(a, b) { - for (let i = 0; i < a.length; i++) { + const aLength = Math.min(a.length, b.length); + for (let i = 0; i < aLength; i++) { a[i] = a[i] ^ b[i]; } } -async function webEncrypt(algo, key, pt, iv) { - const ALGO = 'AES-CBC'; - const _key = await webCrypto.importKey('raw', key, { name: ALGO }, false, ['encrypt']); - const { blockSize } = getCipher(algo); - const cbc_pt = util.concatUint8Array([new Uint8Array(blockSize), pt]); - const ct = new Uint8Array(await webCrypto.encrypt({ name: ALGO, iv }, _key, cbc_pt)).subarray(0, pt.length); - xorMut(ct, pt); - return ct; -} +const getUint32Array = arr => new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4)); function nodeEncrypt(algo, key, pt, iv) { const algoName = enums.read(enums.symmetric, algo); diff --git a/src/crypto/mode/eax.js b/src/crypto/mode/eax.js index 2dfff46e..1ea2284d 100644 --- a/src/crypto/mode/eax.js +++ b/src/crypto/mode/eax.js @@ -19,10 +19,9 @@ * @fileoverview This module implements AES-EAX en/decryption on top of * native AES-CTR using either the WebCrypto API or Node.js' crypto API. * @module crypto/mode/eax - * @private */ -import { AES_CTR } from '@openpgp/asmcrypto.js/dist_es8/aes/ctr'; +import { ctr as nobleAesCtr } from '@noble/ciphers/aes'; import CMAC from '../cmac'; import util from '../../util'; import enums from '../../enums'; @@ -48,16 +47,6 @@ async function OMAC(key) { } async function CTR(key) { - if ( - util.getWebCrypto() && - key.length !== 24 // WebCrypto (no 192 bit support) see: https://www.chromium.org/blink/webcrypto#TOC-AES-support - ) { - key = await webCrypto.importKey('raw', key, { name: 'AES-CTR', length: key.length * 8 }, false, ['encrypt']); - return async function(pt, iv) { - const ct = await webCrypto.encrypt({ name: 'AES-CTR', counter: iv, length: blockLength * 8 }, key, pt); - return new Uint8Array(ct); - }; - } if (util.getNodeCrypto()) { // Node crypto library return async function(pt, iv) { const en = new nodeCrypto.createCipheriv('aes-' + (key.length * 8) + '-ctr', key, iv); @@ -65,9 +54,26 @@ async function CTR(key) { return new Uint8Array(ct); }; } - // asm.js fallback + + if (util.getWebCrypto()) { + try { + const keyRef = await webCrypto.importKey('raw', key, { name: 'AES-CTR', length: key.length * 8 }, false, ['encrypt']); + return async function(pt, iv) { + const ct = await webCrypto.encrypt({ name: 'AES-CTR', counter: iv, length: blockLength * 8 }, keyRef, pt); + return new Uint8Array(ct); + }; + } catch (err) { + // no 192 bit support in Chromium, which throws `OperationError`, see: https://www.chromium.org/blink/webcrypto#TOC-AES-support + if (err.name !== 'NotSupportedError' && + !(key.length === 24 && err.name === 'OperationError')) { + throw err; + } + util.printDebugError('Browser did not support operation: ' + err.message); + } + } + return async function(pt, iv) { - return AES_CTR.encrypt(pt, key, iv); + return nobleAesCtr(key, iv).encrypt(pt); }; } diff --git a/src/crypto/mode/gcm.js b/src/crypto/mode/gcm.js index 25c7a059..b1d2cfe4 100644 --- a/src/crypto/mode/gcm.js +++ b/src/crypto/mode/gcm.js @@ -19,10 +19,9 @@ * @fileoverview This module wraps native AES-GCM en/decryption for both * the WebCrypto api as well as node.js' crypto api. * @module crypto/mode/gcm - * @private */ -import { AES_GCM } from '@openpgp/asmcrypto.js/dist_es8/aes/gcm'; +import { gcm as nobleAesGcm } from '@noble/ciphers/aes'; import util from '../../util'; import enums from '../../enums'; @@ -66,35 +65,52 @@ async function GCM(cipher, key) { }; } - if (util.getWebCrypto() && key.length !== 24) { // WebCrypto (no 192 bit support) see: https://www.chromium.org/blink/webcrypto#TOC-AES-support - const _key = await webCrypto.importKey('raw', key, { name: ALGO }, false, ['encrypt', 'decrypt']); + if (util.getWebCrypto()) { + try { + const _key = await webCrypto.importKey('raw', key, { name: ALGO }, false, ['encrypt', 'decrypt']); + // Safari 13 and Safari iOS 14 does not support GCM-en/decrypting empty messages + const webcryptoEmptyMessagesUnsupported = navigator.userAgent.match(/Version\/13\.\d(\.\d)* Safari/) || + navigator.userAgent.match(/Version\/(13|14)\.\d(\.\d)* Mobile\/\S* Safari/); + return { + encrypt: async function(pt, iv, adata = new Uint8Array()) { + if (webcryptoEmptyMessagesUnsupported && !pt.length) { + return nobleAesGcm(key, iv, adata).encrypt(pt); + } + const ct = await webCrypto.encrypt({ name: ALGO, iv, additionalData: adata, tagLength: tagLength * 8 }, _key, pt); + return new Uint8Array(ct); + }, - return { - encrypt: async function(pt, iv, adata = new Uint8Array()) { - if (!pt.length) { // iOS does not support GCM-en/decrypting empty messages - return AES_GCM.encrypt(pt, key, iv, adata); + decrypt: async function(ct, iv, adata = new Uint8Array()) { + if (webcryptoEmptyMessagesUnsupported && ct.length === tagLength) { + return nobleAesGcm(key, iv, adata).decrypt(ct); + } + try { + const pt = await webCrypto.decrypt({ name: ALGO, iv, additionalData: adata, tagLength: tagLength * 8 }, _key, ct); + return new Uint8Array(pt); + } catch (e) { + if (e.name === 'OperationError') { + throw new Error('Authentication tag mismatch'); + } + } } - const ct = await webCrypto.encrypt({ name: ALGO, iv, additionalData: adata, tagLength: tagLength * 8 }, _key, pt); - return new Uint8Array(ct); - }, - - decrypt: async function(ct, iv, adata = new Uint8Array()) { - if (ct.length === tagLength) { // iOS does not support GCM-en/decrypting empty messages - return AES_GCM.decrypt(ct, key, iv, adata); - } - const pt = await webCrypto.decrypt({ name: ALGO, iv, additionalData: adata, tagLength: tagLength * 8 }, _key, ct); - return new Uint8Array(pt); + }; + } catch (err) { + // no 192 bit support in Chromium, which throws `OperationError`, see: https://www.chromium.org/blink/webcrypto#TOC-AES-support + if (err.name !== 'NotSupportedError' && + !(key.length === 24 && err.name === 'OperationError')) { + throw err; } - }; + util.printDebugError('Browser did not support operation: ' + err.message); + } } return { encrypt: async function(pt, iv, adata) { - return AES_GCM.encrypt(pt, key, iv, adata); + return nobleAesGcm(key, iv, adata).encrypt(pt); }, decrypt: async function(ct, iv, adata) { - return AES_GCM.decrypt(ct, key, iv, adata); + return nobleAesGcm(key, iv, adata).decrypt(ct); } }; } diff --git a/src/crypto/mode/index.js b/src/crypto/mode/index.js index c1ae364f..beeeca55 100644 --- a/src/crypto/mode/index.js +++ b/src/crypto/mode/index.js @@ -1,7 +1,6 @@ /** * @fileoverview Cipher modes * @module crypto/mode - * @private */ import * as cfb from './cfb'; diff --git a/src/crypto/mode/ocb.js b/src/crypto/mode/ocb.js index 868b0ebc..e8b8cabf 100644 --- a/src/crypto/mode/ocb.js +++ b/src/crypto/mode/ocb.js @@ -18,12 +18,11 @@ /** * @fileoverview This module implements AES-OCB en/decryption. * @module crypto/mode/ocb - * @private */ -import * as ciphers from '../cipher'; +import { cbc as nobleAesCbc } from '@noble/ciphers/aes'; +import { getCipherParams } from '../cipher'; import util from '../../util'; -import enums from '../../enums'; const blockLength = 16; const ivLength = 15; @@ -63,20 +62,25 @@ const one = new Uint8Array([1]); * @param {Uint8Array} key - The encryption key */ async function OCB(cipher, key) { + const { keySize } = getCipherParams(cipher); + // sanity checks + if (!util.isAES(cipher) || key.length !== keySize) { + throw new Error('Unexpected algorithm or key size'); + } let maxNtz = 0; - let encipher; - let decipher; + + // `encipher` and `decipher` cannot be async, since `crypt` shares state across calls, + // hence its execution cannot be broken up. + // As a result, WebCrypto cannot currently be used for `encipher`. + const aes = nobleAesCbc(key, zeroBlock, { disablePadding: true }); + const encipher = block => aes.encrypt(block); + const decipher = block => aes.decrypt(block); let mask; constructKeyVariables(cipher, key); - function constructKeyVariables(cipher, key) { - const cipherName = enums.read(enums.symmetric, cipher); - const aes = new ciphers[cipherName](key); - encipher = aes.encrypt.bind(aes); - decipher = aes.decrypt.bind(aes); - + function constructKeyVariables() { const mask_x = encipher(zeroBlock); const mask_$ = util.double(mask_x); mask = []; diff --git a/src/crypto/pkcs1.js b/src/crypto/pkcs1.js index db981322..d888017d 100644 --- a/src/crypto/pkcs1.js +++ b/src/crypto/pkcs1.js @@ -21,7 +21,6 @@ * @see module:crypto/public_key/elliptic/ecdh * @see PublicKeyEncryptedSessionKeyPacket * @module crypto/pkcs1 - * @private */ import { getRandomBytes } from './random'; @@ -133,7 +132,7 @@ export function emeDecode(encoded, randomPayload) { * @param {Integer} emLen - Intended length in octets of the encoded message * @returns {Uint8Array} Encoded message. */ -export async function emsaEncode(algo, hashed, emLen) { +export function emsaEncode(algo, hashed, emLen) { let i; if (hashed.length !== hash.getHashByteLength(algo)) { throw new Error('Invalid hash length'); diff --git a/src/crypto/public_key/dsa.js b/src/crypto/public_key/dsa.js index 7fa69649..c01e3185 100644 --- a/src/crypto/public_key/dsa.js +++ b/src/crypto/public_key/dsa.js @@ -18,11 +18,11 @@ /** * @fileoverview A Digital signature algorithm implementation * @module crypto/public_key/dsa - * @private */ import { getRandomBigInteger } from '../random'; import util from '../../util'; import { isProbablePrime } from './prime'; +import { bigIntToUint8Array, bitLength, byteLength, mod, modExp, modInv, uint8ArrayToBigInt } from '../biginteger'; /* TODO regarding the hash function, read: @@ -30,6 +30,9 @@ import { isProbablePrime } from './prime'; https://tools.ietf.org/html/rfc4880#section-14 */ +const _0n = BigInt(0); +const _1n = BigInt(1); + /** * DSA Sign function * @param {Integer} hashAlgo @@ -42,25 +45,24 @@ import { isProbablePrime } from './prime'; * @async */ export async function sign(hashAlgo, hashed, g, p, q, x) { - const BigInteger = await util.getBigInteger(); - const one = new BigInteger(1); - p = new BigInteger(p); - q = new BigInteger(q); - g = new BigInteger(g); - x = new BigInteger(x); + const _0n = BigInt(0); + p = uint8ArrayToBigInt(p); + q = uint8ArrayToBigInt(q); + g = uint8ArrayToBigInt(g); + x = uint8ArrayToBigInt(x); let k; let r; let s; let t; - g = g.mod(p); - x = x.mod(q); + g = mod(g, p); + x = mod(x, q); // If the output size of the chosen hash is larger than the number of // bits of q, the hash result is truncated to fit by taking the number // of leftmost bits equal to the number of bits of q. This (possibly // truncated) hash function result is treated as a number and used // directly in the DSA signature algorithm. - const h = new BigInteger(hashed.subarray(0, q.byteLength())).mod(q); + const h = mod(uint8ArrayToBigInt(hashed.subarray(0, byteLength(q))), q); // FIPS-186-4, section 4.6: // The values of r and s shall be checked to determine if r = 0 or s = 0. // If either r = 0 or s = 0, a new value of k shall be generated, and the @@ -68,22 +70,22 @@ export async function sign(hashAlgo, hashed, g, p, q, x) { // or s = 0 if signatures are generated properly. while (true) { // See Appendix B here: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf - k = await getRandomBigInteger(one, q); // returns in [1, q-1] - r = g.modExp(k, p).imod(q); // (g**k mod p) mod q - if (r.isZero()) { + k = getRandomBigInteger(_1n, q); // returns in [1, q-1] + r = mod(modExp(g, k, p), q); // (g**k mod p) mod q + if (r === _0n) { continue; } - const xr = x.mul(r).imod(q); - t = h.add(xr).imod(q); // H(m) + x*r mod q - s = k.modInv(q).imul(t).imod(q); // k**-1 * (H(m) + x*r) mod q - if (s.isZero()) { + const xr = mod(x * r, q); + t = mod(h + xr, q); // H(m) + x*r mod q + s = mod(modInv(k, q) * t, q); // k**-1 * (H(m) + x*r) mod q + if (s === _0n) { continue; } break; } return { - r: r.toUint8Array('be', q.byteLength()), - s: s.toUint8Array('be', q.byteLength()) + r: bigIntToUint8Array(r, 'be', byteLength(p)), + s: bigIntToUint8Array(s, 'be', byteLength(p)) }; } @@ -101,36 +103,34 @@ export async function sign(hashAlgo, hashed, g, p, q, x) { * @async */ export async function verify(hashAlgo, r, s, hashed, g, p, q, y) { - const BigInteger = await util.getBigInteger(); - const zero = new BigInteger(0); - r = new BigInteger(r); - s = new BigInteger(s); + r = uint8ArrayToBigInt(r); + s = uint8ArrayToBigInt(s); - p = new BigInteger(p); - q = new BigInteger(q); - g = new BigInteger(g); - y = new BigInteger(y); + p = uint8ArrayToBigInt(p); + q = uint8ArrayToBigInt(q); + g = uint8ArrayToBigInt(g); + y = uint8ArrayToBigInt(y); - if (r.lte(zero) || r.gte(q) || - s.lte(zero) || s.gte(q)) { + if (r <= _0n || r >= q || + s <= _0n || s >= q) { util.printDebug('invalid DSA Signature'); return false; } - const h = new BigInteger(hashed.subarray(0, q.byteLength())).imod(q); - const w = s.modInv(q); // s**-1 mod q - if (w.isZero()) { + const h = mod(uint8ArrayToBigInt(hashed.subarray(0, byteLength(q))), q); + const w = modInv(s, q); // s**-1 mod q + if (w === _0n) { util.printDebug('invalid DSA Signature'); return false; } - g = g.mod(p); - y = y.mod(p); - const u1 = h.mul(w).imod(q); // H(m) * w mod q - const u2 = r.mul(w).imod(q); // r * w mod q - const t1 = g.modExp(u1, p); // g**u1 mod p - const t2 = y.modExp(u2, p); // y**u2 mod p - const v = t1.mul(t2).imod(p).imod(q); // (g**u1 * y**u2 mod p) mod q - return v.equal(r); + g = mod(g, p); + y = mod(y, p); + const u1 = mod(h * w, q); // H(m) * w mod q + const u2 = mod(r * w, q); // r * w mod q + const t1 = modExp(g, u1, p); // g**u1 mod p + const t2 = modExp(y, u2, p); // y**u2 mod p + const v = mod(mod(t1 * t2, p), q); // (g**u1 * y**u2 mod p) mod q + return v === r; } /** @@ -144,21 +144,19 @@ export async function verify(hashAlgo, r, s, hashed, g, p, q, y) { * @async */ export async function validateParams(p, q, g, y, x) { - const BigInteger = await util.getBigInteger(); - p = new BigInteger(p); - q = new BigInteger(q); - g = new BigInteger(g); - y = new BigInteger(y); - const one = new BigInteger(1); + p = uint8ArrayToBigInt(p); + q = uint8ArrayToBigInt(q); + g = uint8ArrayToBigInt(g); + y = uint8ArrayToBigInt(y); // Check that 1 < g < p - if (g.lte(one) || g.gte(p)) { + if (g <= _1n || g >= p) { return false; } /** * Check that subgroup order q divides p-1 */ - if (!p.dec().mod(q).isZero()) { + if (mod(p - _1n, q) !== _0n) { return false; } @@ -166,16 +164,16 @@ export async function validateParams(p, q, g, y, x) { * g has order q * Check that g ** q = 1 mod p */ - if (!g.modExp(q, p).isOne()) { + if (modExp(g, q, p) !== _1n) { return false; } /** * Check q is large and probably prime (we mainly want to avoid small factors) */ - const qSize = new BigInteger(q.bitLength()); - const n150 = new BigInteger(150); - if (qSize.lt(n150) || !(await isProbablePrime(q, null, 32))) { + const qSize = BigInt(bitLength(q)); + const _150n = BigInt(150); + if (qSize < _150n || !isProbablePrime(q, null, 32)) { return false; } @@ -185,11 +183,11 @@ export async function validateParams(p, q, g, y, x) { * * Blinded exponentiation computes g**{rq + x} to compare to y */ - x = new BigInteger(x); - const two = new BigInteger(2); - const r = await getRandomBigInteger(two.leftShift(qSize.dec()), two.leftShift(qSize)); // draw r of same size as q - const rqx = q.mul(r).add(x); - if (!y.equal(g.modExp(rqx, p))) { + x = uint8ArrayToBigInt(x); + const _2n = BigInt(2); + const r = getRandomBigInteger(_2n << (qSize - _1n), _2n << qSize); // draw r of same size as q + const rqx = q * r + x; + if (y !== modExp(g, rqx, p)) { return false; } diff --git a/src/crypto/public_key/elgamal.js b/src/crypto/public_key/elgamal.js index bcd41efe..6bd4641e 100644 --- a/src/crypto/public_key/elgamal.js +++ b/src/crypto/public_key/elgamal.js @@ -18,12 +18,12 @@ /** * @fileoverview ElGamal implementation * @module crypto/public_key/elgamal - * @private */ - -import util from '../../util'; import { getRandomBigInteger } from '../random'; import { emeEncode, emeDecode } from '../pkcs1'; +import { bigIntToUint8Array, bitLength, byteLength, mod, modExp, modInv, uint8ArrayToBigInt } from '../biginteger'; + +const _1n = BigInt(1); /** * ElGamal Encryption function @@ -36,20 +36,19 @@ import { emeEncode, emeDecode } from '../pkcs1'; * @async */ export async function encrypt(data, p, g, y) { - const BigInteger = await util.getBigInteger(); - p = new BigInteger(p); - g = new BigInteger(g); - y = new BigInteger(y); + p = uint8ArrayToBigInt(p); + g = uint8ArrayToBigInt(g); + y = uint8ArrayToBigInt(y); - const padded = emeEncode(data, p.byteLength()); - const m = new BigInteger(padded); + const padded = emeEncode(data, byteLength(p)); + const m = uint8ArrayToBigInt(padded); // OpenPGP uses a "special" version of ElGamal where g is generator of the full group Z/pZ* // hence g has order p-1, and to avoid that k = 0 mod p-1, we need to pick k in [1, p-2] - const k = await getRandomBigInteger(new BigInteger(1), p.dec()); + const k = getRandomBigInteger(_1n, p - _1n); return { - c1: g.modExp(k, p).toUint8Array(), - c2: y.modExp(k, p).imul(m).imod(p).toUint8Array() + c1: bigIntToUint8Array(modExp(g, k, p)), + c2: bigIntToUint8Array(mod(modExp(y, k, p) * m, p)) }; } @@ -66,14 +65,13 @@ export async function encrypt(data, p, g, y) { * @async */ export async function decrypt(c1, c2, p, x, randomPayload) { - const BigInteger = await util.getBigInteger(); - c1 = new BigInteger(c1); - c2 = new BigInteger(c2); - p = new BigInteger(p); - x = new BigInteger(x); + c1 = uint8ArrayToBigInt(c1); + c2 = uint8ArrayToBigInt(c2); + p = uint8ArrayToBigInt(p); + x = uint8ArrayToBigInt(x); - const padded = c1.modExp(x, p).modInv(p).imul(c2).imod(p); - return emeDecode(padded.toUint8Array('be', p.byteLength()), randomPayload); + const padded = mod(modInv(modExp(c1, x, p), p) * c2, p); + return emeDecode(bigIntToUint8Array(padded, 'be', byteLength(p)), randomPayload); } /** @@ -86,21 +84,19 @@ export async function decrypt(c1, c2, p, x, randomPayload) { * @async */ export async function validateParams(p, g, y, x) { - const BigInteger = await util.getBigInteger(); - p = new BigInteger(p); - g = new BigInteger(g); - y = new BigInteger(y); + p = uint8ArrayToBigInt(p); + g = uint8ArrayToBigInt(g); + y = uint8ArrayToBigInt(y); - const one = new BigInteger(1); // Check that 1 < g < p - if (g.lte(one) || g.gte(p)) { + if (g <= _1n || g >= p) { return false; } // Expect p-1 to be large - const pSize = new BigInteger(p.bitLength()); - const n1023 = new BigInteger(1023); - if (pSize.lt(n1023)) { + const pSize = BigInt(bitLength(p)); + const _1023n = BigInt(1023); + if (pSize < _1023n) { return false; } @@ -108,7 +104,7 @@ export async function validateParams(p, g, y, x) { * g should have order p-1 * Check that g ** (p-1) = 1 mod p */ - if (!g.modExp(p.dec(), p).isOne()) { + if (modExp(g, p - _1n, p) !== _1n) { return false; } @@ -119,14 +115,15 @@ export async function validateParams(p, g, y, x) { * We just check g**i != 1 for all i up to a threshold */ let res = g; - const i = new BigInteger(1); - const threshold = new BigInteger(2).leftShift(new BigInteger(17)); // we want order > threshold - while (i.lt(threshold)) { - res = res.mul(g).imod(p); - if (res.isOne()) { + let i = BigInt(1); + const _2n = BigInt(2); + const threshold = _2n << BigInt(17); // we want order > threshold + while (i < threshold) { + res = mod(res * g, p); + if (res === _1n) { return false; } - i.iinc(); + i++; } /** @@ -135,11 +132,10 @@ export async function validateParams(p, g, y, x) { * * Blinded exponentiation computes g**{r(p-1) + x} to compare to y */ - x = new BigInteger(x); - const two = new BigInteger(2); - const r = await getRandomBigInteger(two.leftShift(pSize.dec()), two.leftShift(pSize)); // draw r of same size as p-1 - const rqx = p.dec().imul(r).iadd(x); - if (!y.equal(g.modExp(rqx, p))) { + x = uint8ArrayToBigInt(x); + const r = getRandomBigInteger(_2n << (pSize - _1n), _2n << pSize); // draw r of same size as p-1 + const rqx = (p - _1n) * r + x; + if (y !== modExp(g, rqx, p)) { return false; } diff --git a/src/crypto/public_key/elliptic/brainpool/brainpoolP256r1.ts b/src/crypto/public_key/elliptic/brainpool/brainpoolP256r1.ts new file mode 100644 index 00000000..1cbdf69a --- /dev/null +++ b/src/crypto/public_key/elliptic/brainpool/brainpoolP256r1.ts @@ -0,0 +1,24 @@ +import { createCurve } from '@noble/curves/_shortw_utils'; +import { sha256 } from '@noble/hashes/sha256'; +import { Field } from '@noble/curves/abstract/modular'; + +// brainpoolP256r1: https://datatracker.ietf.org/doc/html/rfc5639#section-3.4 + +// eslint-disable-next-line new-cap +const Fp = Field(BigInt('0xa9fb57dba1eea9bc3e660a909d838d726e3bf623d52620282013481d1f6e5377')); +const CURVE_A = Fp.create(BigInt('0x7d5a0975fc2c3057eef67530417affe7fb8055c126dc5c6ce94a4b44f330b5d9')); +const CURVE_B = BigInt('0x26dc5c6ce94a4b44f330b5d9bbd77cbf958416295cf7e1ce6bccdc18ff8c07b6'); + +// prettier-ignore +export const brainpoolP256r1 = createCurve({ + a: CURVE_A, // Equation params: a, b + b: CURVE_B, + Fp, + // Curve order (q), total count of valid points in the field + n: BigInt('0xa9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e82974856a7'), + // Base (generator) point (x, y) + Gx: BigInt('0x8bd2aeb9cb7e57cb2c4b482ffc81b7afb9de27e1e3bd23c23a4453bd9ace3262'), + Gy: BigInt('0x547ef835c3dac4fd97f8461a14611dc9c27745132ded8e545c1d54c72f046997'), + h: BigInt(1), + lowS: false +} as const, sha256); diff --git a/src/crypto/public_key/elliptic/brainpool/brainpoolP384r1.ts b/src/crypto/public_key/elliptic/brainpool/brainpoolP384r1.ts new file mode 100644 index 00000000..e7cee9c3 --- /dev/null +++ b/src/crypto/public_key/elliptic/brainpool/brainpoolP384r1.ts @@ -0,0 +1,24 @@ +import { createCurve } from '@noble/curves/_shortw_utils'; +import { sha384 } from '@noble/hashes/sha512'; +import { Field } from '@noble/curves/abstract/modular'; + +// brainpoolP384 r1: https://datatracker.ietf.org/doc/html/rfc5639#section-3.6 + +// eslint-disable-next-line new-cap +const Fp = Field(BigInt('0x8cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b412b1da197fb71123acd3a729901d1a71874700133107ec53')); +const CURVE_A = Fp.create(BigInt('0x7bc382c63d8c150c3c72080ace05afa0c2bea28e4fb22787139165efba91f90f8aa5814a503ad4eb04a8c7dd22ce2826')); +const CURVE_B = BigInt('0x04a8c7dd22ce28268b39b55416f0447c2fb77de107dcd2a62e880ea53eeb62d57cb4390295dbc9943ab78696fa504c11'); + +// prettier-ignore +export const brainpoolP384r1 = createCurve({ + a: CURVE_A, // Equation params: a, b + b: CURVE_B, + Fp, + // Curve order (q), total count of valid points in the field + n: BigInt('0x8cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b31f166e6cac0425a7cf3ab6af6b7fc3103b883202e9046565'), + // Base (generator) point (x, y) + Gx: BigInt('0x1d1c64f068cf45ffa2a63a81b7c13f6b8847a3e77ef14fe3db7fcafe0cbd10e8e826e03436d646aaef87b2e247d4af1e'), + Gy: BigInt('0x8abe1d7520f9c2a45cb1eb8e95cfd55262b70b29feec5864e19c054ff99129280e4646217791811142820341263c5315'), + h: BigInt(1), + lowS: false +} as const, sha384); diff --git a/src/crypto/public_key/elliptic/brainpool/brainpoolP512r1.ts b/src/crypto/public_key/elliptic/brainpool/brainpoolP512r1.ts new file mode 100644 index 00000000..86e2b86a --- /dev/null +++ b/src/crypto/public_key/elliptic/brainpool/brainpoolP512r1.ts @@ -0,0 +1,24 @@ +import { createCurve } from '@noble/curves/_shortw_utils'; +import { sha512 } from '@noble/hashes/sha512'; +import { Field } from '@noble/curves/abstract/modular'; + +// brainpoolP512r1: https://datatracker.ietf.org/doc/html/rfc5639#section-3.7 + +// eslint-disable-next-line new-cap +const Fp = Field(BigInt('0xaadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca703308717d4d9b009bc66842aecda12ae6a380e62881ff2f2d82c68528aa6056583a48f3')); +const CURVE_A = Fp.create(BigInt('0x7830a3318b603b89e2327145ac234cc594cbdd8d3df91610a83441caea9863bc2ded5d5aa8253aa10a2ef1c98b9ac8b57f1117a72bf2c7b9e7c1ac4d77fc94ca')); +const CURVE_B = BigInt('0x3df91610a83441caea9863bc2ded5d5aa8253aa10a2ef1c98b9ac8b57f1117a72bf2c7b9e7c1ac4d77fc94cadc083e67984050b75ebae5dd2809bd638016f723'); + +// prettier-ignore +export const brainpoolP512r1 = createCurve({ + a: CURVE_A, // Equation params: a, b + b: CURVE_B, + Fp, + // Curve order (q), total count of valid points in the field + n: BigInt('0xaadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca70330870553e5c414ca92619418661197fac10471db1d381085ddaddb58796829ca90069'), + // Base (generator) point (x, y) + Gx: BigInt('0x81aee4bdd82ed9645a21322e9c4c6a9385ed9f70b5d916c1b43b62eef4d0098eff3b1f78e2d0d48d50d1687b93b97d5f7c6d5047406a5e688b352209bcb9f822'), + Gy: BigInt('0x7dde385d566332ecc0eabfa9cf7822fdf209f70024a57b1aa000c55b881f8111b2dcde494a5f485e5bca4bd88a2763aed1ca2b2fa8f0540678cd1e0f3ad80892'), + h: BigInt(1), + lowS: false +} as const, sha512); diff --git a/src/crypto/public_key/elliptic/ecdh.js b/src/crypto/public_key/elliptic/ecdh.js index 11238f71..18bdc664 100644 --- a/src/crypto/public_key/elliptic/ecdh.js +++ b/src/crypto/public_key/elliptic/ecdh.js @@ -18,20 +18,17 @@ /** * @fileoverview Key encryption and decryption for RFC 6637 ECDH * @module crypto/public_key/elliptic/ecdh - * @private */ -import nacl from '@openpgp/tweetnacl/nacl-fast-light'; -import { CurveWithOID, jwkToRawPublic, rawPublicToJWK, privateToJWK, validateStandardParams } from './oid_curves'; +import { CurveWithOID, jwkToRawPublic, rawPublicToJWK, privateToJWK, validateStandardParams, checkPublicPointEnconding } from './oid_curves'; import * as aesKW from '../../aes_kw'; -import { getRandomBytes } from '../../random'; import hash from '../../hash'; import enums from '../../../enums'; import util from '../../../util'; import { b64ToUint8Array } from '../../../encoding/base64'; import * as pkcs5 from '../../pkcs5'; -import { keyFromPublic, keyFromPrivate, getIndutnyCurve } from './indutnyKey'; -import getCipher from '../../cipher/getCipher'; +import { getCipherParams } from '../../cipher'; +import { generateEphemeralEncryptionMaterial as ecdhXGenerateEphemeralEncryptionMaterial, recomputeSharedSecret as ecdhXRecomputeSharedSecret } from './ecdh_x'; const webCrypto = util.getWebCrypto(); const nodeCrypto = util.getNodeCrypto(); @@ -55,7 +52,7 @@ function buildEcdhParam(public_algo, oid, kdfParams, fingerprint) { new Uint8Array([public_algo]), kdfParams.write(), util.stringToUint8Array('Anonymous Sender '), - fingerprint.subarray(0, 20) + fingerprint ]); } @@ -93,11 +90,9 @@ async function kdf(hashAlgo, X, length, param, stripLeading = false, stripTraili */ async function genPublicEphemeralKey(curve, Q) { switch (curve.type) { - case 'curve25519': { - const d = getRandomBytes(32); - const { secretKey, sharedKey } = await genPrivateEphemeralKey(curve, Q, null, d); - let { publicKey } = nacl.box.keyPair.fromSecretKey(secretKey); - publicKey = util.concatUint8Array([new Uint8Array([0x40]), publicKey]); + case 'curve25519Legacy': { + const { sharedSecret: sharedKey, ephemeralPublicKey } = await ecdhXGenerateEphemeralEncryptionMaterial(enums.publicKey.x25519, Q.subarray(1)); + const publicKey = util.concatUint8Array([new Uint8Array([curve.wireFormatLeadingByte]), ephemeralPublicKey]); return { publicKey, sharedKey }; // Note: sharedKey is little-endian here, unlike below } case 'web': @@ -106,13 +101,16 @@ async function genPublicEphemeralKey(curve, Q) { return await webPublicEphemeralKey(curve, Q); } catch (err) { util.printDebugError(err); + return jsPublicEphemeralKey(curve, Q); } } break; case 'node': return nodePublicEphemeralKey(curve, Q); + default: + return jsPublicEphemeralKey(curve, Q); + } - return ellipticPublicEphemeralKey(curve, Q); } /** @@ -122,7 +120,7 @@ async function genPublicEphemeralKey(curve, Q) { * @param {module:type/kdf_params} kdfParams - KDF params including cipher and algorithm to use * @param {Uint8Array} data - Unpadded session key data * @param {Uint8Array} Q - Recipient public key - * @param {Uint8Array} fingerprint - Recipient fingerprint + * @param {Uint8Array} fingerprint - Recipient fingerprint, already truncated depending on the key version * @returns {Promise<{publicKey: Uint8Array, wrappedKey: Uint8Array}>} * @async */ @@ -130,11 +128,12 @@ export async function encrypt(oid, kdfParams, data, Q, fingerprint) { const m = pkcs5.encode(data); const curve = new CurveWithOID(oid); + checkPublicPointEnconding(curve, Q); const { publicKey, sharedKey } = await genPublicEphemeralKey(curve, Q); const param = buildEcdhParam(enums.publicKey.ecdh, oid, kdfParams, fingerprint); - const { keySize } = getCipher(kdfParams.cipher); + const { keySize } = getCipherParams(kdfParams.cipher); const Z = await kdf(kdfParams.hash, sharedKey, keySize, param); - const wrappedKey = aesKW.wrap(Z, m); + const wrappedKey = await aesKW.wrap(kdfParams.cipher, Z, m); return { publicKey, wrappedKey }; } @@ -155,9 +154,9 @@ async function genPrivateEphemeralKey(curve, V, Q, d) { d = privateKey; } switch (curve.type) { - case 'curve25519': { + case 'curve25519Legacy': { const secretKey = d.slice().reverse(); - const sharedKey = nacl.scalarMult(secretKey, V.subarray(1)); + const sharedKey = await ecdhXRecomputeSharedSecret(enums.publicKey.x25519, V.subarray(1), Q.subarray(1), secretKey); return { secretKey, sharedKey }; // Note: sharedKey is little-endian here, unlike below } case 'web': @@ -166,13 +165,15 @@ async function genPrivateEphemeralKey(curve, V, Q, d) { return await webPrivateEphemeralKey(curve, V, Q, d); } catch (err) { util.printDebugError(err); + return jsPrivateEphemeralKey(curve, V, d); } } break; case 'node': return nodePrivateEphemeralKey(curve, V, d); + default: + return jsPrivateEphemeralKey(curve, V, d); } - return ellipticPrivateEphemeralKey(curve, V, d); } /** @@ -184,21 +185,23 @@ async function genPrivateEphemeralKey(curve, V, Q, d) { * @param {Uint8Array} C - Encrypted and wrapped value derived from session key * @param {Uint8Array} Q - Recipient public key * @param {Uint8Array} d - Recipient private key - * @param {Uint8Array} fingerprint - Recipient fingerprint + * @param {Uint8Array} fingerprint - Recipient fingerprint, already truncated depending on the key version * @returns {Promise} Value derived from session key. * @async */ export async function decrypt(oid, kdfParams, V, C, Q, d, fingerprint) { const curve = new CurveWithOID(oid); + checkPublicPointEnconding(curve, Q); + checkPublicPointEnconding(curve, V); const { sharedKey } = await genPrivateEphemeralKey(curve, V, Q, d); const param = buildEcdhParam(enums.publicKey.ecdh, oid, kdfParams, fingerprint); - const { keySize } = getCipher(kdfParams.cipher); + const { keySize } = getCipherParams(kdfParams.cipher); let err; for (let i = 0; i < 3; i++) { try { // Work around old go crypto bug and old OpenPGP.js bug, respectively. const Z = await kdf(kdfParams.hash, sharedKey, keySize, param, i === 1, i === 2); - return pkcs5.decode(aesKW.unwrap(Z, C)); + return pkcs5.decode(await aesKW.unwrap(kdfParams.cipher, Z, C)); } catch (e) { err = e; } @@ -206,6 +209,24 @@ export async function decrypt(oid, kdfParams, V, C, Q, d, fingerprint) { throw err; } +async function jsPrivateEphemeralKey(curve, V, d) { + const nobleCurve = await util.getNobleCurve(enums.publicKey.ecdh, curve.name); + // The output includes parity byte + const sharedSecretWithParity = nobleCurve.getSharedSecret(d, V); + const sharedKey = sharedSecretWithParity.subarray(1); + return { secretKey: d, sharedKey }; +} + +async function jsPublicEphemeralKey(curve, Q) { + const nobleCurve = await util.getNobleCurve(enums.publicKey.ecdh, curve.name); + const { publicKey: V, privateKey: v } = await curve.genKeyPair(); + + // The output includes parity byte + const sharedSecretWithParity = nobleCurve.getSharedSecret(v, Q); + const sharedKey = sharedSecretWithParity.subarray(1); + return { publicKey: V, sharedKey }; +} + /** * Generate ECDHE secret from private key and public part of ephemeral key using webCrypto * @@ -217,24 +238,24 @@ export async function decrypt(oid, kdfParams, V, C, Q, d, fingerprint) { * @async */ async function webPrivateEphemeralKey(curve, V, Q, d) { - const recipient = privateToJWK(curve.payloadSize, curve.web.web, Q, d); + const recipient = privateToJWK(curve.payloadSize, curve.web, Q, d); let privateKey = webCrypto.importKey( 'jwk', recipient, { name: 'ECDH', - namedCurve: curve.web.web + namedCurve: curve.web }, true, ['deriveKey', 'deriveBits'] ); - const jwk = rawPublicToJWK(curve.payloadSize, curve.web.web, V); + const jwk = rawPublicToJWK(curve.payloadSize, curve.web, V); let sender = webCrypto.importKey( 'jwk', jwk, { name: 'ECDH', - namedCurve: curve.web.web + namedCurve: curve.web }, true, [] @@ -243,11 +264,11 @@ async function webPrivateEphemeralKey(curve, V, Q, d) { let S = webCrypto.deriveBits( { name: 'ECDH', - namedCurve: curve.web.web, + namedCurve: curve.web, public: sender }, privateKey, - curve.web.sharedSize + curve.sharedSize ); let secret = webCrypto.exportKey( 'jwk', @@ -268,11 +289,11 @@ async function webPrivateEphemeralKey(curve, V, Q, d) { * @async */ async function webPublicEphemeralKey(curve, Q) { - const jwk = rawPublicToJWK(curve.payloadSize, curve.web.web, Q); + const jwk = rawPublicToJWK(curve.payloadSize, curve.web, Q); let keyPair = webCrypto.generateKey( { name: 'ECDH', - namedCurve: curve.web.web + namedCurve: curve.web }, true, ['deriveKey', 'deriveBits'] @@ -282,7 +303,7 @@ async function webPublicEphemeralKey(curve, Q) { jwk, { name: 'ECDH', - namedCurve: curve.web.web + namedCurve: curve.web }, false, [] @@ -291,11 +312,11 @@ async function webPublicEphemeralKey(curve, Q) { let s = webCrypto.deriveBits( { name: 'ECDH', - namedCurve: curve.web.web, + namedCurve: curve.web, public: recipient }, keyPair.privateKey, - curve.web.sharedSize + curve.sharedSize ); let p = webCrypto.exportKey( 'jwk', @@ -303,47 +324,7 @@ async function webPublicEphemeralKey(curve, Q) { ); [s, p] = await Promise.all([s, p]); const sharedKey = new Uint8Array(s); - const publicKey = new Uint8Array(jwkToRawPublic(p)); - return { publicKey, sharedKey }; -} - -/** - * Generate ECDHE secret from private key and public part of ephemeral key using indutny/elliptic - * - * @param {CurveWithOID} curve - Elliptic curve object - * @param {Uint8Array} V - Public part of ephemeral key - * @param {Uint8Array} d - Recipient private key - * @returns {Promise<{secretKey: Uint8Array, sharedKey: Uint8Array}>} - * @async - */ -async function ellipticPrivateEphemeralKey(curve, V, d) { - const indutnyCurve = await getIndutnyCurve(curve.name); - V = keyFromPublic(indutnyCurve, V); - d = keyFromPrivate(indutnyCurve, d); - const secretKey = new Uint8Array(d.getPrivate()); - const S = d.derive(V.getPublic()); - const len = indutnyCurve.curve.p.byteLength(); - const sharedKey = S.toArrayLike(Uint8Array, 'be', len); - return { secretKey, sharedKey }; -} - -/** - * Generate ECDHE ephemeral key and secret from public key using indutny/elliptic - * - * @param {CurveWithOID} curve - Elliptic curve object - * @param {Uint8Array} Q - Recipient public key - * @returns {Promise<{publicKey: Uint8Array, sharedKey: Uint8Array}>} - * @async - */ -async function ellipticPublicEphemeralKey(curve, Q) { - const indutnyCurve = await getIndutnyCurve(curve.name); - const v = await curve.genKeyPair(); - Q = keyFromPublic(indutnyCurve, Q); - const V = keyFromPrivate(indutnyCurve, v.privateKey); - const publicKey = v.publicKey; - const S = V.derive(Q.getPublic()); - const len = indutnyCurve.curve.p.byteLength(); - const sharedKey = S.toArrayLike(Uint8Array, 'be', len); + const publicKey = new Uint8Array(jwkToRawPublic(p, curve.wireFormatLeadingByte)); return { publicKey, sharedKey }; } @@ -357,7 +338,7 @@ async function ellipticPublicEphemeralKey(curve, Q) { * @async */ async function nodePrivateEphemeralKey(curve, V, d) { - const recipient = nodeCrypto.createECDH(curve.node.node); + const recipient = nodeCrypto.createECDH(curve.node); recipient.setPrivateKey(d); const sharedKey = new Uint8Array(recipient.computeSecret(V)); const secretKey = new Uint8Array(recipient.getPrivateKey()); @@ -373,7 +354,7 @@ async function nodePrivateEphemeralKey(curve, V, d) { * @async */ async function nodePublicEphemeralKey(curve, Q) { - const sender = nodeCrypto.createECDH(curve.node.node); + const sender = nodeCrypto.createECDH(curve.node); sender.generateKeys(); const sharedKey = new Uint8Array(sender.computeSecret(Q)); const publicKey = new Uint8Array(sender.getPublicKey()); diff --git a/src/crypto/public_key/elliptic/ecdh_x.js b/src/crypto/public_key/elliptic/ecdh_x.js index 3b66c0c0..b293d4e5 100644 --- a/src/crypto/public_key/elliptic/ecdh_x.js +++ b/src/crypto/public_key/elliptic/ecdh_x.js @@ -1,20 +1,20 @@ /** * @fileoverview Key encryption and decryption for RFC 6637 ECDH * @module crypto/public_key/elliptic/ecdh - * @private */ -import nacl from '@openpgp/tweetnacl/nacl-fast-light'; +import x25519 from '@openpgp/tweetnacl'; import * as aesKW from '../../aes_kw'; import { getRandomBytes } from '../../random'; import enums from '../../../enums'; import util from '../../../util'; -import getCipher from '../../cipher/getCipher'; import computeHKDF from '../../hkdf'; +import { getCipherParams } from '../../cipher'; const HKDF_INFO = { - x25519: util.encodeUTF8('OpenPGP X25519') + x25519: util.encodeUTF8('OpenPGP X25519'), + x448: util.encodeUTF8('OpenPGP X448') }; /** @@ -27,7 +27,14 @@ export async function generate(algo) { case enums.publicKey.x25519: { // k stays in little-endian, unlike legacy ECDH over curve25519 const k = getRandomBytes(32); - const { publicKey: A } = nacl.box.keyPair.fromSecretKey(k); + const { publicKey: A } = x25519.box.keyPair.fromSecretKey(k); + return { A, k }; + } + + case enums.publicKey.x448: { + const x448 = await util.getNobleCurve(enums.publicKey.x448); + const k = x448.utils.randomPrivateKey(); + const A = x448.getPublicKey(k); return { A, k }; } default: @@ -50,7 +57,16 @@ export async function validateParams(algo, A, k) { * Derive public point A' from private key * and expect A == A' */ - const { publicKey } = nacl.box.keyPair.fromSecretKey(k); + const { publicKey } = x25519.box.keyPair.fromSecretKey(k); + return util.equalsUint8Array(A, publicKey); + } + case enums.publicKey.x448: { + const x448 = await util.getNobleCurve(enums.publicKey.x448); + /** + * Derive public point A' from private key + * and expect A == A' + */ + const publicKey = x448.getPublicKey(k); return util.equalsUint8Array(A, publicKey); } @@ -72,19 +88,25 @@ export async function validateParams(algo, A, k) { * @async */ export async function encrypt(algo, data, recipientA) { + const { ephemeralPublicKey, sharedSecret } = await generateEphemeralEncryptionMaterial(algo, recipientA); + const hkdfInput = util.concatUint8Array([ + ephemeralPublicKey, + recipientA, + sharedSecret + ]); switch (algo) { case enums.publicKey.x25519: { - const ephemeralSecretKey = getRandomBytes(32); - const sharedSecret = nacl.scalarMult(ephemeralSecretKey, recipientA); - const { publicKey: ephemeralPublicKey } = nacl.box.keyPair.fromSecretKey(ephemeralSecretKey); - const hkdfInput = util.concatUint8Array([ - ephemeralPublicKey, - recipientA, - sharedSecret - ]); - const { keySize } = getCipher(enums.symmetric.aes128); + const cipherAlgo = enums.symmetric.aes128; + const { keySize } = getCipherParams(cipherAlgo); const encryptionKey = await computeHKDF(enums.hash.sha256, hkdfInput, new Uint8Array(), HKDF_INFO.x25519, keySize); - const wrappedKey = aesKW.wrap(encryptionKey, data); + const wrappedKey = await aesKW.wrap(cipherAlgo, encryptionKey, data); + return { ephemeralPublicKey, wrappedKey }; + } + case enums.publicKey.x448: { + const cipherAlgo = enums.symmetric.aes256; + const { keySize } = getCipherParams(enums.symmetric.aes256); + const encryptionKey = await computeHKDF(enums.hash.sha512, hkdfInput, new Uint8Array(), HKDF_INFO.x448, keySize); + const wrappedKey = await aesKW.wrap(cipherAlgo, encryptionKey, data); return { ephemeralPublicKey, wrappedKey }; } @@ -105,19 +127,100 @@ export async function encrypt(algo, data, recipientA) { * @async */ export async function decrypt(algo, ephemeralPublicKey, wrappedKey, A, k) { + const sharedSecret = await recomputeSharedSecret(algo, ephemeralPublicKey, A, k); + const hkdfInput = util.concatUint8Array([ + ephemeralPublicKey, + A, + sharedSecret + ]); switch (algo) { case enums.publicKey.x25519: { - const sharedSecret = nacl.scalarMult(k, ephemeralPublicKey); - const hkdfInput = util.concatUint8Array([ - ephemeralPublicKey, - A, - sharedSecret - ]); - const { keySize } = getCipher(enums.symmetric.aes128); + const cipherAlgo = enums.symmetric.aes128; + const { keySize } = getCipherParams(cipherAlgo); const encryptionKey = await computeHKDF(enums.hash.sha256, hkdfInput, new Uint8Array(), HKDF_INFO.x25519, keySize); - return aesKW.unwrap(encryptionKey, wrappedKey); + return aesKW.unwrap(cipherAlgo, encryptionKey, wrappedKey); + } + case enums.publicKey.x448: { + const cipherAlgo = enums.symmetric.aes256; + const { keySize } = getCipherParams(enums.symmetric.aes256); + const encryptionKey = await computeHKDF(enums.hash.sha512, hkdfInput, new Uint8Array(), HKDF_INFO.x448, keySize); + return aesKW.unwrap(cipherAlgo, encryptionKey, wrappedKey); } default: throw new Error('Unsupported ECDH algorithm'); } } + +export function getPayloadSize(algo) { + switch (algo) { + case enums.publicKey.x25519: + return 32; + + case enums.publicKey.x448: + return 56; + + default: + throw new Error('Unsupported ECDH algorithm'); + } +} + +/** + * Generate shared secret and ephemeral public key for encryption + * @returns {Promise<{ ephemeralPublicKey: Uint8Array, sharedSecret: Uint8Array }>} ephemeral public key (K_A) and shared secret + * @async + */ +export async function generateEphemeralEncryptionMaterial(algo, recipientA) { + switch (algo) { + case enums.publicKey.x25519: { + const ephemeralSecretKey = getRandomBytes(getPayloadSize(algo)); + const sharedSecret = x25519.scalarMult(ephemeralSecretKey, recipientA); + assertNonZeroArray(sharedSecret); + const { publicKey: ephemeralPublicKey } = x25519.box.keyPair.fromSecretKey(ephemeralSecretKey); + return { ephemeralPublicKey, sharedSecret }; + } + case enums.publicKey.x448: { + const x448 = await util.getNobleCurve(enums.publicKey.x448); + const ephemeralSecretKey = x448.utils.randomPrivateKey(); + const sharedSecret = x448.getSharedSecret(ephemeralSecretKey, recipientA); + assertNonZeroArray(sharedSecret); + const ephemeralPublicKey = x448.getPublicKey(ephemeralSecretKey); + return { ephemeralPublicKey, sharedSecret }; + } + default: + throw new Error('Unsupported ECDH algorithm'); + } +} + +export async function recomputeSharedSecret(algo, ephemeralPublicKey, A, k) { + switch (algo) { + case enums.publicKey.x25519: { + const sharedSecret = x25519.scalarMult(k, ephemeralPublicKey); + assertNonZeroArray(sharedSecret); + return sharedSecret; + } + case enums.publicKey.x448: { + const x448 = await util.getNobleCurve(enums.publicKey.x448); + const sharedSecret = x448.getSharedSecret(k, ephemeralPublicKey); + assertNonZeroArray(sharedSecret); + return sharedSecret; + } + default: + throw new Error('Unsupported ECDH algorithm'); + } +} + +/** + * x25519 and x448 produce an all-zero value when given as input a point with small order. + * This does not lead to a security issue in the context of ECDH, but it is still unexpected, + * hence we throw. + * @param {Uint8Array} sharedSecret + */ +function assertNonZeroArray(sharedSecret) { + let acc = 0; + for (let i = 0; i < sharedSecret.length; i++) { + acc |= sharedSecret[i]; + } + if (acc === 0) { + throw new Error('Unexpected low order point'); + } +} diff --git a/src/crypto/public_key/elliptic/ecdsa.js b/src/crypto/public_key/elliptic/ecdsa.js index 285c40f3..b0797963 100644 --- a/src/crypto/public_key/elliptic/ecdsa.js +++ b/src/crypto/public_key/elliptic/ecdsa.js @@ -18,15 +18,14 @@ /** * @fileoverview Implementation of ECDSA following RFC6637 for Openpgpjs * @module crypto/public_key/elliptic/ecdsa - * @private */ import enums from '../../../enums'; import util from '../../../util'; import { getRandomBytes } from '../../random'; import hash from '../../hash'; -import { CurveWithOID, webCurves, privateToJWK, rawPublicToJWK, validateStandardParams } from './oid_curves'; -import { getIndutnyCurve, keyFromPrivate, keyFromPublic } from './indutnyKey'; +import { CurveWithOID, webCurves, privateToJWK, rawPublicToJWK, validateStandardParams, nodeCurves, checkPublicPointEnconding } from './oid_curves'; +import { bigIntToUint8Array } from '../../biginteger'; const webCrypto = util.getWebCrypto(); const nodeCrypto = util.getNodeCrypto(); @@ -47,35 +46,37 @@ const nodeCrypto = util.getNodeCrypto(); */ export async function sign(oid, hashAlgo, message, publicKey, privateKey, hashed) { const curve = new CurveWithOID(oid); + checkPublicPointEnconding(curve, publicKey); if (message && !util.isStream(message)) { const keyPair = { publicKey, privateKey }; switch (curve.type) { - case 'web': { + case 'web': // If browser doesn't support a curve, we'll catch it try { // Need to await to make sure browser succeeds return await webSign(curve, hashAlgo, message, keyPair); } catch (err) { // We do not fallback if the error is related to key integrity - // Unfortunaley Safari does not support p521 and throws a DataError when using it + // Unfortunaley Safari does not support nistP521 and throws a DataError when using it // So we need to always fallback for that curve - if (curve.name !== 'p521' && (err.name === 'DataError' || err.name === 'OperationError')) { + if (curve.name !== 'nistP521' && (err.name === 'DataError' || err.name === 'OperationError')) { throw err; } util.printDebugError('Browser did not support signing: ' + err.message); } break; - } - case 'node': { - const signature = await nodeSign(curve, hashAlgo, message, keyPair); - return { - r: signature.r.toArrayLike(Uint8Array), - s: signature.s.toArrayLike(Uint8Array) - }; - } + case 'node': + return nodeSign(curve, hashAlgo, message, privateKey); } } - return ellipticSign(curve, hashed, privateKey); + + const nobleCurve = await util.getNobleCurve(enums.publicKey.ecdsa, curve.name); + // lowS: non-canonical sig: https://stackoverflow.com/questions/74338846/ecdsa-signature-verification-mismatch + const signature = nobleCurve.sign(hashed, privateKey, { lowS: false }); + return { + r: bigIntToUint8Array(signature.r, 'be', curve.payloadSize), + s: bigIntToUint8Array(signature.s, 'be', curve.payloadSize) + }; } /** @@ -92,28 +93,45 @@ export async function sign(oid, hashAlgo, message, publicKey, privateKey, hashed */ export async function verify(oid, hashAlgo, signature, message, publicKey, hashed) { const curve = new CurveWithOID(oid); + checkPublicPointEnconding(curve, publicKey); + // See https://github.com/openpgpjs/openpgpjs/pull/948. + // NB: the impact was more likely limited to Brainpool curves, since thanks + // to WebCrypto availability, NIST curve should not have been affected. + // Similarly, secp256k1 should have been used rarely enough. + // However, we implement the fix for all curves, since it's only needed in case of + // verification failure, which is unexpected, hence a minor slowdown is acceptable. + const tryFallbackVerificationForOldBug = async () => ( + hashed[0] === 0 ? + jsVerify(curve, signature, hashed.subarray(1), publicKey) : + false + ); + if (message && !util.isStream(message)) { switch (curve.type) { case 'web': try { // Need to await to make sure browser succeeds - return await webVerify(curve, hashAlgo, signature, message, publicKey); + const verified = await webVerify(curve, hashAlgo, signature, message, publicKey); + return verified || tryFallbackVerificationForOldBug(); } catch (err) { // We do not fallback if the error is related to key integrity - // Unfortunately Safari does not support p521 and throws a DataError when using it + // Unfortunately Safari does not support nistP521 and throws a DataError when using it // So we need to always fallback for that curve - if (curve.name !== 'p521' && (err.name === 'DataError' || err.name === 'OperationError')) { + if (curve.name !== 'nistP521' && (err.name === 'DataError' || err.name === 'OperationError')) { throw err; } util.printDebugError('Browser did not support verifying: ' + err.message); } break; - case 'node': - return nodeVerify(curve, hashAlgo, signature, message, publicKey); + case 'node': { + const verified = await nodeVerify(curve, hashAlgo, signature, message, publicKey); + return verified || tryFallbackVerificationForOldBug(); + } } } - const digest = (typeof hashAlgo === 'undefined') ? message : hashed; - return ellipticVerify(curve, signature, digest, publicKey); + + const verified = await jsVerify(curve, signature, hashed, publicKey); + return verified || tryFallbackVerificationForOldBug(); } /** @@ -141,6 +159,7 @@ export async function validateParams(oid, Q, d) { const hashed = await hash.digest(hashAlgo, message); try { const signature = await sign(oid, hashAlgo, message, Q, d, hashed); + // eslint-disable-next-line @typescript-eslint/return-await return await verify(oid, hashAlgo, signature, message, Q, hashed); } catch (err) { return false; @@ -158,20 +177,14 @@ export async function validateParams(oid, Q, d) { // // ////////////////////////// -async function ellipticSign(curve, hashed, privateKey) { - const indutnyCurve = await getIndutnyCurve(curve.name); - const key = keyFromPrivate(indutnyCurve, privateKey); - const signature = key.sign(hashed); - return { - r: signature.r.toArrayLike(Uint8Array), - s: signature.s.toArrayLike(Uint8Array) - }; -} - -async function ellipticVerify(curve, signature, digest, publicKey) { - const indutnyCurve = await getIndutnyCurve(curve.name); - const key = keyFromPublic(indutnyCurve, publicKey); - return key.verify(digest, signature); +/** + * Fallback javascript implementation of ECDSA verification. + * To be used if no native implementation is available for the given curve/operation. + */ +async function jsVerify(curve, signature, hashed, publicKey) { + const nobleCurve = await util.getNobleCurve(enums.publicKey.ecdsa, curve.name); + // lowS: non-canonical sig: https://stackoverflow.com/questions/74338846/ecdsa-signature-verification-mismatch + return nobleCurve.verify(util.concatUint8Array([signature.r, signature.s]), hashed, publicKey, { lowS: false }); } async function webSign(curve, hashAlgo, message, keyPair) { @@ -233,85 +246,45 @@ async function webVerify(curve, hashAlgo, { r, s }, message, publicKey) { ); } -async function nodeSign(curve, hashAlgo, message, keyPair) { +async function nodeSign(curve, hashAlgo, message, privateKey) { + // JWT encoding cannot be used for now, as Brainpool curves are not supported + const ecKeyUtils = util.nodeRequire('eckey-utils'); + const nodeBuffer = util.getNodeBuffer(); + const { privateKey: derPrivateKey } = ecKeyUtils.generateDer({ + curveName: nodeCurves[curve.name], + privateKey: nodeBuffer.from(privateKey) + }); + const sign = nodeCrypto.createSign(enums.read(enums.hash, hashAlgo)); sign.write(message); sign.end(); - const key = ECPrivateKey.encode({ - version: 1, - parameters: curve.oid, - privateKey: Array.from(keyPair.privateKey), - publicKey: { unused: 0, data: Array.from(keyPair.publicKey) } - }, 'pem', { - label: 'EC PRIVATE KEY' - }); - return ECDSASignature.decode(sign.sign(key), 'der'); + const signature = new Uint8Array(sign.sign({ key: derPrivateKey, format: 'der', type: 'sec1', dsaEncoding: 'ieee-p1363' })); + const len = curve.payloadSize; + + return { + r: signature.subarray(0, len), + s: signature.subarray(len, len << 1) + }; } async function nodeVerify(curve, hashAlgo, { r, s }, message, publicKey) { - const { default: BN } = await import('bn.js'); + const ecKeyUtils = util.nodeRequire('eckey-utils'); + const nodeBuffer = util.getNodeBuffer(); + const { publicKey: derPublicKey } = ecKeyUtils.generateDer({ + curveName: nodeCurves[curve.name], + publicKey: nodeBuffer.from(publicKey) + }); const verify = nodeCrypto.createVerify(enums.read(enums.hash, hashAlgo)); verify.write(message); verify.end(); - const key = SubjectPublicKeyInfo.encode({ - algorithm: { - algorithm: [1, 2, 840, 10045, 2, 1], - parameters: curve.oid - }, - subjectPublicKey: { unused: 0, data: Array.from(publicKey) } - }, 'pem', { - label: 'PUBLIC KEY' - }); - const signature = ECDSASignature.encode({ - r: new BN(r), s: new BN(s) - }, 'der'); + + const signature = util.concatUint8Array([r, s]); try { - return verify.verify(key, signature); + return verify.verify({ key: derPublicKey, format: 'der', type: 'spki', dsaEncoding: 'ieee-p1363' }, signature); } catch (err) { return false; } } - -// Originally written by Owen Smith https://github.com/omsmith -// Adapted on Feb 2018 from https://github.com/Brightspace/node-jwk-to-pem/ - -/* eslint-disable no-invalid-this */ - -const asn1 = nodeCrypto ? require('asn1.js') : undefined; - -const ECDSASignature = nodeCrypto ? - asn1.define('ECDSASignature', function() { - this.seq().obj( - this.key('r').int(), - this.key('s').int() - ); - }) : undefined; - -const ECPrivateKey = nodeCrypto ? - asn1.define('ECPrivateKey', function() { - this.seq().obj( - this.key('version').int(), - this.key('privateKey').octstr(), - this.key('parameters').explicit(0).optional().any(), - this.key('publicKey').explicit(1).optional().bitstr() - ); - }) : undefined; - -const AlgorithmIdentifier = nodeCrypto ? - asn1.define('AlgorithmIdentifier', function() { - this.seq().obj( - this.key('algorithm').objid(), - this.key('parameters').optional().any() - ); - }) : undefined; - -const SubjectPublicKeyInfo = nodeCrypto ? - asn1.define('SubjectPublicKeyInfo', function() { - this.seq().obj( - this.key('algorithm').use(AlgorithmIdentifier), - this.key('subjectPublicKey').bitstr() - ); - }) : undefined; diff --git a/src/crypto/public_key/elliptic/eddsa.js b/src/crypto/public_key/elliptic/eddsa.js index e38cbb96..7b41eb23 100644 --- a/src/crypto/public_key/elliptic/eddsa.js +++ b/src/crypto/public_key/elliptic/eddsa.js @@ -18,17 +18,15 @@ /** * @fileoverview Implementation of EdDSA following RFC4880bis-03 for OpenPGP * @module crypto/public_key/elliptic/eddsa - * @private */ -import sha512 from 'hash.js/lib/hash/sha/512'; -import nacl from '@openpgp/tweetnacl/nacl-fast-light'; +import ed25519 from '@openpgp/tweetnacl'; import util from '../../../util'; import enums from '../../../enums'; import hash from '../../hash'; import { getRandomBytes } from '../../random'; +import { b64ToUint8Array, uint8ArrayToB64 } from '../../../encoding/base64'; -nacl.hash = bytes => new Uint8Array(sha512().update(bytes).digest()); /** * Generate (non-legacy) EdDSA key @@ -37,9 +35,31 @@ nacl.hash = bytes => new Uint8Array(sha512().update(bytes).digest()); */ export async function generate(algo) { switch (algo) { - case enums.publicKey.ed25519: { - const seed = getRandomBytes(32); - const { publicKey: A } = nacl.sign.keyPair.fromSeed(seed); + case enums.publicKey.ed25519: + try { + const webCrypto = util.getWebCrypto(); + const webCryptoKey = await webCrypto.generateKey('Ed25519', true, ['sign', 'verify']); + + const privateKey = await webCrypto.exportKey('jwk', webCryptoKey.privateKey); + const publicKey = await webCrypto.exportKey('jwk', webCryptoKey.publicKey); + + return { + A: new Uint8Array(b64ToUint8Array(publicKey.x)), + seed: b64ToUint8Array(privateKey.d, true) + }; + } catch (err) { + if (err.name !== 'NotSupportedError' && err.name !== 'OperationError') { // Temporary (hopefully) fix for WebKit on Linux + throw err; + } + const seed = getRandomBytes(getPayloadSize(algo)); + const { publicKey: A } = ed25519.sign.keyPair.fromSeed(seed); + return { A, seed }; + } + + case enums.publicKey.ed448: { + const ed448 = await util.getNobleCurve(enums.publicKey.ed448); + const seed = ed448.utils.randomPrivateKey(); + const A = ed448.getPublicKey(seed); return { A, seed }; } default: @@ -62,15 +82,37 @@ export async function generate(algo) { */ export async function sign(algo, hashAlgo, message, publicKey, privateKey, hashed) { if (hash.getHashByteLength(hashAlgo) < hash.getHashByteLength(getPreferredHashAlgo(algo))) { + // Enforce digest sizes: + // - Ed25519: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.4-4 + // - Ed448: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.5-4 throw new Error('Hash algorithm too weak for EdDSA.'); } switch (algo) { - case enums.publicKey.ed25519: { - const secretKey = util.concatUint8Array([privateKey, publicKey]); - const signature = nacl.sign.detached(hashed, secretKey); + case enums.publicKey.ed25519: + try { + const webCrypto = util.getWebCrypto(); + const jwk = privateKeyToJWK(algo, publicKey, privateKey); + const key = await webCrypto.importKey('jwk', jwk, 'Ed25519', false, ['sign']); + + const signature = new Uint8Array( + await webCrypto.sign('Ed25519', key, hashed) + ); + + return { RS: signature }; + } catch (err) { + if (err.name !== 'NotSupportedError') { + throw err; + } + const secretKey = util.concatUint8Array([privateKey, publicKey]); + const signature = ed25519.sign.detached(hashed, secretKey); + return { RS: signature }; + } + + case enums.publicKey.ed448: { + const ed448 = await util.getNobleCurve(enums.publicKey.ed448); + const signature = ed448.sign(hashed, privateKey); return { RS: signature }; } - case enums.publicKey.ed448: default: throw new Error('Unsupported EdDSA algorithm'); } @@ -90,13 +132,30 @@ export async function sign(algo, hashAlgo, message, publicKey, privateKey, hashe */ export async function verify(algo, hashAlgo, { RS }, m, publicKey, hashed) { if (hash.getHashByteLength(hashAlgo) < hash.getHashByteLength(getPreferredHashAlgo(algo))) { + // Enforce digest sizes: + // - Ed25519: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.4-4 + // - Ed448: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.5-4 throw new Error('Hash algorithm too weak for EdDSA.'); } switch (algo) { - case enums.publicKey.ed25519: { - return nacl.sign.detached.verify(hashed, RS, publicKey); + case enums.publicKey.ed25519: + try { + const webCrypto = util.getWebCrypto(); + const jwk = publicKeyToJWK(algo, publicKey); + const key = await webCrypto.importKey('jwk', jwk, 'Ed25519', false, ['verify']); + const verified = await webCrypto.verify('Ed25519', key, RS, hashed); + return verified; + } catch (err) { + if (err.name !== 'NotSupportedError') { + throw err; + } + return ed25519.sign.detached.verify(hashed, RS, publicKey); + } + + case enums.publicKey.ed448: { + const ed448 = await util.getNobleCurve(enums.publicKey.ed448); + return ed448.verify(RS, hashed, publicKey); } - case enums.publicKey.ed448: default: throw new Error('Unsupported EdDSA algorithm'); } @@ -116,22 +175,71 @@ export async function validateParams(algo, A, seed) { /** * Derive public point A' from private key * and expect A == A' + * TODO: move to sign-verify using WebCrypto (same as ECDSA) when curve is more widely implemented */ - const { publicKey } = nacl.sign.keyPair.fromSeed(seed); + const { publicKey } = ed25519.sign.keyPair.fromSeed(seed); return util.equalsUint8Array(A, publicKey); } - case enums.publicKey.ed448: // unsupported + case enums.publicKey.ed448: { + const ed448 = await util.getNobleCurve(enums.publicKey.ed448); + + const publicKey = ed448.getPublicKey(seed); + return util.equalsUint8Array(A, publicKey); + } default: return false; } } +export function getPayloadSize(algo) { + switch (algo) { + case enums.publicKey.ed25519: + return 32; + + case enums.publicKey.ed448: + return 57; + + default: + throw new Error('Unsupported EdDSA algorithm'); + } +} + export function getPreferredHashAlgo(algo) { switch (algo) { case enums.publicKey.ed25519: return enums.hash.sha256; + case enums.publicKey.ed448: + return enums.hash.sha512; default: throw new Error('Unknown EdDSA algo'); } } + +const publicKeyToJWK = (algo, publicKey) => { + switch (algo) { + case enums.publicKey.ed25519: { + const jwk = { + kty: 'OKP', + crv: 'Ed25519', + x: uint8ArrayToB64(publicKey, true), + ext: true + }; + return jwk; + } + default: + throw new Error('Unsupported EdDSA algorithm'); + } +}; + +const privateKeyToJWK = (algo, publicKey, privateKey) => { + switch (algo) { + case enums.publicKey.ed25519: { + const jwk = publicKeyToJWK(algo, publicKey); + jwk.d = uint8ArrayToB64(privateKey, true); + return jwk; + } + default: + throw new Error('Unsupported EdDSA algorithm'); + } +}; diff --git a/src/crypto/public_key/elliptic/eddsa_legacy.js b/src/crypto/public_key/elliptic/eddsa_legacy.js index 63929ea7..a88e67b4 100644 --- a/src/crypto/public_key/elliptic/eddsa_legacy.js +++ b/src/crypto/public_key/elliptic/eddsa_legacy.js @@ -19,16 +19,14 @@ * @fileoverview Implementation of legacy EdDSA following RFC4880bis-03 for OpenPGP. * This key type has been deprecated by the crypto-refresh RFC. * @module crypto/public_key/elliptic/eddsa_legacy - * @private */ -import sha512 from 'hash.js/lib/hash/sha/512'; -import nacl from '@openpgp/tweetnacl/nacl-fast-light'; +import nacl from '@openpgp/tweetnacl'; import util from '../../../util'; import enums from '../../../enums'; import hash from '../../hash'; - -nacl.hash = bytes => new Uint8Array(sha512().update(bytes).digest()); +import { CurveWithOID, checkPublicPointEnconding } from './oid_curves'; +import { sign as eddsaSign, verify as eddsaVerify } from './eddsa'; /** * Sign a message using the provided legacy EdDSA key @@ -45,12 +43,15 @@ nacl.hash = bytes => new Uint8Array(sha512().update(bytes).digest()); * @async */ export async function sign(oid, hashAlgo, message, publicKey, privateKey, hashed) { + const curve = new CurveWithOID(oid); + checkPublicPointEnconding(curve, publicKey); if (hash.getHashByteLength(hashAlgo) < hash.getHashByteLength(enums.hash.sha256)) { + // Enforce digest sizes, since the constraint was already present in RFC4880bis: // see https://tools.ietf.org/id/draft-ietf-openpgp-rfc4880bis-10.html#section-15-7.2 + // and https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.3-3 throw new Error('Hash algorithm too weak for EdDSA.'); } - const secretKey = util.concatUint8Array([privateKey, publicKey.subarray(1)]); - const signature = nacl.sign.detached(hashed, secretKey); + const { RS: signature } = await eddsaSign(enums.publicKey.ed25519, hashAlgo, message, publicKey.subarray(1), privateKey, hashed); // EdDSA signature params are returned in little-endian format return { r: signature.subarray(0, 32), @@ -71,11 +72,16 @@ export async function sign(oid, hashAlgo, message, publicKey, privateKey, hashed * @async */ export async function verify(oid, hashAlgo, { r, s }, m, publicKey, hashed) { + const curve = new CurveWithOID(oid); + checkPublicPointEnconding(curve, publicKey); if (hash.getHashByteLength(hashAlgo) < hash.getHashByteLength(enums.hash.sha256)) { + // Enforce digest sizes, since the constraint was already present in RFC4880bis: + // see https://tools.ietf.org/id/draft-ietf-openpgp-rfc4880bis-10.html#section-15-7.2 + // and https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.3-3 throw new Error('Hash algorithm too weak for EdDSA.'); } - const signature = util.concatUint8Array([r, s]); - return nacl.sign.detached.verify(hashed, signature, publicKey.subarray(1)); + const RS = util.concatUint8Array([r, s]); + return eddsaVerify(enums.publicKey.ed25519, hashAlgo, { RS }, m, publicKey.subarray(1), hashed); } /** * Validate legacy EdDSA parameters @@ -87,7 +93,7 @@ export async function verify(oid, hashAlgo, { r, s }, m, publicKey, hashed) { */ export async function validateParams(oid, Q, k) { // Check whether the given curve is supported - if (oid.getName() !== 'ed25519') { + if (oid.getName() !== enums.curve.ed25519Legacy) { return false; } diff --git a/src/crypto/public_key/elliptic/index.js b/src/crypto/public_key/elliptic/index.js index 562918b9..61a75a23 100644 --- a/src/crypto/public_key/elliptic/index.js +++ b/src/crypto/public_key/elliptic/index.js @@ -22,7 +22,6 @@ * @see module:crypto/public_key/elliptic/ecdsa * @see module:crypto/public_key/elliptic/eddsa * @module crypto/public_key/elliptic - * @private */ import { CurveWithOID, generate, getPreferredHashAlgo } from './oid_curves'; diff --git a/src/crypto/public_key/elliptic/indutnyKey.js b/src/crypto/public_key/elliptic/indutnyKey.js deleted file mode 100644 index aa7565c5..00000000 --- a/src/crypto/public_key/elliptic/indutnyKey.js +++ /dev/null @@ -1,45 +0,0 @@ -// OpenPGP.js - An OpenPGP implementation in javascript -// Copyright (C) 2015-2016 Decentral -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 3.0 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -/** - * @fileoverview Wrapper for a KeyPair of an curve from indutny/elliptic library - * @module crypto/public_key/elliptic/indutnyKey - * @private - */ - -import config from '../../../config'; - -export function keyFromPrivate(indutnyCurve, priv) { - const keyPair = indutnyCurve.keyPair({ priv: priv }); - return keyPair; -} - -export function keyFromPublic(indutnyCurve, pub) { - const keyPair = indutnyCurve.keyPair({ pub: pub }); - if (keyPair.validate().result !== true) { - throw new Error('Invalid elliptic public key'); - } - return keyPair; -} - -export async function getIndutnyCurve(name) { - if (!config.useIndutnyElliptic) { - throw new Error('This curve is only supported in the full build of OpenPGP.js'); - } - const { default: elliptic } = await import('@openpgp/elliptic'); - return new elliptic.ec(name); -} diff --git a/src/crypto/public_key/elliptic/noble_curves.js b/src/crypto/public_key/elliptic/noble_curves.js new file mode 100644 index 00000000..acfac17e --- /dev/null +++ b/src/crypto/public_key/elliptic/noble_curves.js @@ -0,0 +1,27 @@ +/** + * This file is needed to dynamic import the noble-curves. + * Separate dynamic imports are not convenient as they result in too many chunks, + * which share a lot of code anyway. + */ + +import { p256 as nistP256 } from '@noble/curves/p256'; +import { p384 as nistP384 } from '@noble/curves/p384'; +import { p521 as nistP521 } from '@noble/curves/p521'; +import { x448, ed448 } from '@noble/curves/ed448'; +import { secp256k1 } from '@noble/curves/secp256k1'; +import { brainpoolP256r1 } from './brainpool/brainpoolP256r1'; +import { brainpoolP384r1 } from './brainpool/brainpoolP384r1'; +import { brainpoolP512r1 } from './brainpool/brainpoolP512r1'; + +export const nobleCurves = new Map(Object.entries({ + nistP256, + nistP384, + nistP521, + brainpoolP256r1, + brainpoolP384r1, + brainpoolP512r1, + secp256k1, + x448, + ed448 +})); + diff --git a/src/crypto/public_key/elliptic/oid_curves.js b/src/crypto/public_key/elliptic/oid_curves.js index e8d563ef..7404cbe6 100644 --- a/src/crypto/public_key/elliptic/oid_curves.js +++ b/src/crypto/public_key/elliptic/oid_curves.js @@ -18,206 +18,199 @@ /** * @fileoverview Wrapper of an instance of an Elliptic Curve * @module crypto/public_key/elliptic/curve - * @private */ - -import nacl from '@openpgp/tweetnacl/nacl-fast-light'; -import { getRandomBytes } from '../../random'; +import nacl from '@openpgp/tweetnacl'; import enums from '../../../enums'; import util from '../../../util'; import { uint8ArrayToB64, b64ToUint8Array } from '../../../encoding/base64'; import OID from '../../../type/oid'; -import { keyFromPublic, keyFromPrivate, getIndutnyCurve } from './indutnyKey'; import { UnsupportedError } from '../../../packet/packet'; +import { generate as eddsaGenerate } from './eddsa'; +import { generate as ecdhXGenerate } from './ecdh_x'; const webCrypto = util.getWebCrypto(); const nodeCrypto = util.getNodeCrypto(); const webCurves = { - 'p256': 'P-256', - 'p384': 'P-384', - 'p521': 'P-521' + [enums.curve.nistP256]: 'P-256', + [enums.curve.nistP384]: 'P-384', + [enums.curve.nistP521]: 'P-521' }; const knownCurves = nodeCrypto ? nodeCrypto.getCurves() : []; const nodeCurves = nodeCrypto ? { - secp256k1: knownCurves.includes('secp256k1') ? 'secp256k1' : undefined, - p256: knownCurves.includes('prime256v1') ? 'prime256v1' : undefined, - p384: knownCurves.includes('secp384r1') ? 'secp384r1' : undefined, - p521: knownCurves.includes('secp521r1') ? 'secp521r1' : undefined, - ed25519: knownCurves.includes('ED25519') ? 'ED25519' : undefined, - curve25519: knownCurves.includes('X25519') ? 'X25519' : undefined, - brainpoolP256r1: knownCurves.includes('brainpoolP256r1') ? 'brainpoolP256r1' : undefined, - brainpoolP384r1: knownCurves.includes('brainpoolP384r1') ? 'brainpoolP384r1' : undefined, - brainpoolP512r1: knownCurves.includes('brainpoolP512r1') ? 'brainpoolP512r1' : undefined + [enums.curve.secp256k1]: knownCurves.includes('secp256k1') ? 'secp256k1' : undefined, + [enums.curve.nistP256]: knownCurves.includes('prime256v1') ? 'prime256v1' : undefined, + [enums.curve.nistP384]: knownCurves.includes('secp384r1') ? 'secp384r1' : undefined, + [enums.curve.nistP521]: knownCurves.includes('secp521r1') ? 'secp521r1' : undefined, + [enums.curve.ed25519Legacy]: knownCurves.includes('ED25519') ? 'ED25519' : undefined, + [enums.curve.curve25519Legacy]: knownCurves.includes('X25519') ? 'X25519' : undefined, + [enums.curve.brainpoolP256r1]: knownCurves.includes('brainpoolP256r1') ? 'brainpoolP256r1' : undefined, + [enums.curve.brainpoolP384r1]: knownCurves.includes('brainpoolP384r1') ? 'brainpoolP384r1' : undefined, + [enums.curve.brainpoolP512r1]: knownCurves.includes('brainpoolP512r1') ? 'brainpoolP512r1' : undefined } : {}; const curves = { - p256: { + [enums.curve.nistP256]: { oid: [0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07], keyType: enums.publicKey.ecdsa, hash: enums.hash.sha256, cipher: enums.symmetric.aes128, - node: nodeCurves.p256, - web: webCurves.p256, + node: nodeCurves[enums.curve.nistP256], + web: webCurves[enums.curve.nistP256], payloadSize: 32, - sharedSize: 256 + sharedSize: 256, + wireFormatLeadingByte: 0x04 }, - p384: { + [enums.curve.nistP384]: { oid: [0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22], keyType: enums.publicKey.ecdsa, hash: enums.hash.sha384, cipher: enums.symmetric.aes192, - node: nodeCurves.p384, - web: webCurves.p384, + node: nodeCurves[enums.curve.nistP384], + web: webCurves[enums.curve.nistP384], payloadSize: 48, - sharedSize: 384 + sharedSize: 384, + wireFormatLeadingByte: 0x04 }, - p521: { + [enums.curve.nistP521]: { oid: [0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x23], keyType: enums.publicKey.ecdsa, hash: enums.hash.sha512, cipher: enums.symmetric.aes256, - node: nodeCurves.p521, - web: webCurves.p521, + node: nodeCurves[enums.curve.nistP521], + web: webCurves[enums.curve.nistP521], payloadSize: 66, - sharedSize: 528 + sharedSize: 528, + wireFormatLeadingByte: 0x04 }, - secp256k1: { + [enums.curve.secp256k1]: { oid: [0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x0A], keyType: enums.publicKey.ecdsa, hash: enums.hash.sha256, cipher: enums.symmetric.aes128, - node: nodeCurves.secp256k1, - payloadSize: 32 + node: nodeCurves[enums.curve.secp256k1], + payloadSize: 32, + wireFormatLeadingByte: 0x04 }, - ed25519: { + [enums.curve.ed25519Legacy]: { oid: [0x06, 0x09, 0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01], keyType: enums.publicKey.eddsaLegacy, hash: enums.hash.sha512, node: false, // nodeCurves.ed25519 TODO - payloadSize: 32 + payloadSize: 32, + wireFormatLeadingByte: 0x40 }, - curve25519: { + [enums.curve.curve25519Legacy]: { oid: [0x06, 0x0A, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01], keyType: enums.publicKey.ecdh, hash: enums.hash.sha256, cipher: enums.symmetric.aes128, node: false, // nodeCurves.curve25519 TODO - payloadSize: 32 + payloadSize: 32, + wireFormatLeadingByte: 0x40 }, - brainpoolP256r1: { + [enums.curve.brainpoolP256r1]: { oid: [0x06, 0x09, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07], keyType: enums.publicKey.ecdsa, hash: enums.hash.sha256, cipher: enums.symmetric.aes128, - node: nodeCurves.brainpoolP256r1, - payloadSize: 32 + node: nodeCurves[enums.curve.brainpoolP256r1], + payloadSize: 32, + wireFormatLeadingByte: 0x04 }, - brainpoolP384r1: { + [enums.curve.brainpoolP384r1]: { oid: [0x06, 0x09, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0B], keyType: enums.publicKey.ecdsa, hash: enums.hash.sha384, cipher: enums.symmetric.aes192, - node: nodeCurves.brainpoolP384r1, - payloadSize: 48 + node: nodeCurves[enums.curve.brainpoolP384r1], + payloadSize: 48, + wireFormatLeadingByte: 0x04 }, - brainpoolP512r1: { + [enums.curve.brainpoolP512r1]: { oid: [0x06, 0x09, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D], keyType: enums.publicKey.ecdsa, hash: enums.hash.sha512, cipher: enums.symmetric.aes256, - node: nodeCurves.brainpoolP512r1, - payloadSize: 64 + node: nodeCurves[enums.curve.brainpoolP512r1], + payloadSize: 64, + wireFormatLeadingByte: 0x04 } }; class CurveWithOID { - constructor(oidOrName, params) { + constructor(oidOrName) { try { - if (util.isArray(oidOrName) || - util.isUint8Array(oidOrName)) { - // by oid byte array - oidOrName = new OID(oidOrName); - } - if (oidOrName instanceof OID) { - // by curve OID - oidOrName = oidOrName.getName(); - } - // by curve name or oid string - this.name = enums.write(enums.curve, oidOrName); + this.name = oidOrName instanceof OID ? + oidOrName.getName() : + enums.write(enums.curve,oidOrName); } catch (err) { throw new UnsupportedError('Unknown curve'); } - params = params || curves[this.name]; + const params = curves[this.name]; this.keyType = params.keyType; this.oid = params.oid; this.hash = params.hash; this.cipher = params.cipher; - this.node = params.node && curves[this.name]; - this.web = params.web && curves[this.name]; + this.node = params.node; + this.web = params.web; this.payloadSize = params.payloadSize; + this.sharedSize = params.sharedSize; + this.wireFormatLeadingByte = params.wireFormatLeadingByte; if (this.web && util.getWebCrypto()) { this.type = 'web'; } else if (this.node && util.getNodeCrypto()) { this.type = 'node'; - } else if (this.name === 'curve25519') { - this.type = 'curve25519'; - } else if (this.name === 'ed25519') { - this.type = 'ed25519'; + } else if (this.name === enums.curve.curve25519Legacy) { + this.type = 'curve25519Legacy'; + } else if (this.name === enums.curve.ed25519Legacy) { + this.type = 'ed25519Legacy'; } } async genKeyPair() { - let keyPair; switch (this.type) { case 'web': try { - return await webGenKeyPair(this.name); + return await webGenKeyPair(this.name, this.wireFormatLeadingByte); } catch (err) { util.printDebugError('Browser did not support generating ec key ' + err.message); - break; + return jsGenKeyPair(this.name); } case 'node': return nodeGenKeyPair(this.name); - case 'curve25519': { - const privateKey = getRandomBytes(32); + case 'curve25519Legacy': { + // the private key must be stored in big endian and already clamped: https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-13.html#section-5.5.5.6.1.1-3 + const { k, A } = await ecdhXGenerate(enums.publicKey.x25519); + const privateKey = k.slice().reverse(); privateKey[0] = (privateKey[0] & 127) | 64; privateKey[31] &= 248; - const secretKey = privateKey.slice().reverse(); - keyPair = nacl.box.keyPair.fromSecretKey(secretKey); - const publicKey = util.concatUint8Array([new Uint8Array([0x40]), keyPair.publicKey]); + const publicKey = util.concatUint8Array([new Uint8Array([this.wireFormatLeadingByte]), A]); return { publicKey, privateKey }; } - case 'ed25519': { - const privateKey = getRandomBytes(32); - const keyPair = nacl.sign.keyPair.fromSeed(privateKey); - const publicKey = util.concatUint8Array([new Uint8Array([0x40]), keyPair.publicKey]); + case 'ed25519Legacy': { + const { seed: privateKey, A } = await eddsaGenerate(enums.publicKey.ed25519); + const publicKey = util.concatUint8Array([new Uint8Array([this.wireFormatLeadingByte]), A]); return { publicKey, privateKey }; } + default: + return jsGenKeyPair(this.name); } - const indutnyCurve = await getIndutnyCurve(this.name); - keyPair = await indutnyCurve.genKeyPair({ - entropy: util.uint8ArrayToString(getRandomBytes(32)) - }); - return { publicKey: new Uint8Array(keyPair.getPublic('array', false)), privateKey: keyPair.getPrivate().toArrayLike(Uint8Array) }; } } -async function generate(curve) { - const BigInteger = await util.getBigInteger(); - - curve = new CurveWithOID(curve); +async function generate(curveName) { + const curve = new CurveWithOID(curveName); + const { oid, hash, cipher } = curve; const keyPair = await curve.genKeyPair(); - const Q = new BigInteger(keyPair.publicKey).toUint8Array(); - const secret = new BigInteger(keyPair.privateKey).toUint8Array('be', curve.payloadSize); return { - oid: curve.oid, - Q, - secret, - hash: curve.hash, - cipher: curve.cipher + oid, + Q: keyPair.publicKey, + secret: util.leftPad(keyPair.privateKey, curve.payloadSize), + hash, + cipher }; } @@ -227,7 +220,7 @@ async function generate(curve) { * @returns {enums.hash} hash algorithm */ function getPreferredHashAlgo(oid) { - return curves[enums.write(enums.curve, oid.toHex())].hash; + return curves[oid.getName()].hash; } /** @@ -242,14 +235,14 @@ function getPreferredHashAlgo(oid) { */ async function validateStandardParams(algo, oid, Q, d) { const supportedCurves = { - p256: true, - p384: true, - p521: true, - secp256k1: true, - curve25519: algo === enums.publicKey.ecdh, - brainpoolP256r1: true, - brainpoolP384r1: true, - brainpoolP512r1: true + [enums.curve.nistP256]: true, + [enums.curve.nistP384]: true, + [enums.curve.nistP521]: true, + [enums.curve.secp256k1]: true, + [enums.curve.curve25519Legacy]: algo === enums.publicKey.ecdh, + [enums.curve.brainpoolP256r1]: true, + [enums.curve.brainpoolP384r1]: true, + [enums.curve.brainpoolP512r1]: true }; // Check whether the given curve is supported @@ -258,7 +251,7 @@ async function validateStandardParams(algo, oid, Q, d) { return false; } - if (curveName === 'curve25519') { + if (curveName === enums.curve.curve25519Legacy) { d = d.slice().reverse(); // Re-derive public point Q' const { publicKey } = nacl.box.keyPair.fromSecretKey(d); @@ -272,28 +265,35 @@ async function validateStandardParams(algo, oid, Q, d) { return true; } - const curve = await getIndutnyCurve(curveName); - try { - // Parse Q and check that it is on the curve but not at infinity - Q = keyFromPublic(curve, Q).getPublic(); - } catch (validationErrors) { - return false; - } - - /** + const nobleCurve = await util.getNobleCurve(enums.publicKey.ecdsa, curveName); // excluding curve25519Legacy, ecdh and ecdsa use the same curves + /* * Re-derive public point Q' = dG from private key * Expect Q == Q' */ - const dG = keyFromPrivate(curve, d).getPublic(); - if (!dG.eq(Q)) { + const dG = nobleCurve.getPublicKey(d, false); + if (!util.equalsUint8Array(dG, Q)) { return false; } return true; } +/** + * Check whether the public point has a valid encoding. + * NB: this function does not check e.g. whether the point belongs to the curve. + */ +function checkPublicPointEnconding(curve, V) { + const { payloadSize, wireFormatLeadingByte, name: curveName } = curve; + + const pointSize = (curveName === enums.curve.curve25519Legacy || curveName === enums.curve.ed25519Legacy) ? payloadSize : payloadSize * 2; + + if (V[0] !== wireFormatLeadingByte || V.length !== pointSize + 1) { + throw new Error('Invalid point encoding'); + } +} + export { - CurveWithOID, curves, webCurves, nodeCurves, generate, getPreferredHashAlgo, jwkToRawPublic, rawPublicToJWK, privateToJWK, validateStandardParams + CurveWithOID, curves, webCurves, nodeCurves, generate, getPreferredHashAlgo, jwkToRawPublic, rawPublicToJWK, privateToJWK, validateStandardParams, checkPublicPointEnconding }; ////////////////////////// @@ -301,9 +301,14 @@ export { // Helper functions // // // ////////////////////////// +async function jsGenKeyPair(name) { + const nobleCurve = await util.getNobleCurve(enums.publicKey.ecdsa, name); // excluding curve25519Legacy, ecdh and ecdsa use the same curves + const privateKey = nobleCurve.utils.randomPrivateKey(); + const publicKey = nobleCurve.getPublicKey(privateKey, false); + return { publicKey, privateKey }; +} - -async function webGenKeyPair(name) { +async function webGenKeyPair(name, wireFormatLeadingByte) { // Note: keys generated with ECDSA and ECDH are structurally equivalent const webCryptoKey = await webCrypto.generateKey({ name: 'ECDSA', namedCurve: webCurves[name] }, true, ['sign', 'verify']); @@ -311,7 +316,7 @@ async function webGenKeyPair(name) { const publicKey = await webCrypto.exportKey('jwk', webCryptoKey.publicKey); return { - publicKey: jwkToRawPublic(publicKey), + publicKey: jwkToRawPublic(publicKey, wireFormatLeadingByte), privateKey: b64ToUint8Array(privateKey.d, true) }; } @@ -337,11 +342,11 @@ async function nodeGenKeyPair(name) { * * @returns {Uint8Array} Raw public key. */ -function jwkToRawPublic(jwk) { +function jwkToRawPublic(jwk, wireFormatLeadingByte) { const bufX = b64ToUint8Array(jwk.x); const bufY = b64ToUint8Array(jwk.y); const publicKey = new Uint8Array(bufX.length + bufY.length + 1); - publicKey[0] = 0x04; + publicKey[0] = wireFormatLeadingByte; publicKey.set(bufX, 1); publicKey.set(bufY, bufX.length + 1); return publicKey; diff --git a/src/crypto/public_key/index.js b/src/crypto/public_key/index.js index 94714eb4..ffcf73cc 100644 --- a/src/crypto/public_key/index.js +++ b/src/crypto/public_key/index.js @@ -1,10 +1,8 @@ /** * @fileoverview Asymmetric cryptography functions * @module crypto/public_key - * @private */ -import nacl from '@openpgp/tweetnacl/nacl-fast-light'; import * as rsa from './rsa'; import * as elgamal from './elgamal'; import * as elliptic from './elliptic'; @@ -18,7 +16,5 @@ export default { /** @see module:crypto/public_key/elliptic */ elliptic: elliptic, /** @see module:crypto/public_key/dsa */ - dsa: dsa, - /** @see tweetnacl */ - nacl: nacl + dsa: dsa }; diff --git a/src/crypto/public_key/prime.js b/src/crypto/public_key/prime.ts similarity index 77% rename from src/crypto/public_key/prime.js rename to src/crypto/public_key/prime.ts index 3755d08e..b892d666 100644 --- a/src/crypto/public_key/prime.js +++ b/src/crypto/public_key/prime.ts @@ -18,25 +18,21 @@ /** * @fileoverview Algorithms for probabilistic random prime generation * @module crypto/public_key/prime - * @private */ - -import util from '../../util'; +import { bigIntToNumber, bitLength, gcd, getBit, mod, modExp } from '../biginteger'; import { getRandomBigInteger } from '../random'; +const _1n = BigInt(1); + /** * Generate a probably prime random number - * @param {Integer} bits - Bit length of the prime - * @param {BigInteger} e - Optional RSA exponent to check against the prime - * @param {Integer} k - Optional number of iterations of Miller-Rabin test - * @returns BigInteger - * @async + * @param bits - Bit length of the prime + * @param e - Optional RSA exponent to check against the prime + * @param k - Optional number of iterations of Miller-Rabin test */ -export async function randomProbablePrime(bits, e, k) { - const BigInteger = await util.getBigInteger(); - const one = new BigInteger(1); - const min = one.leftShift(new BigInteger(bits - 1)); - const thirty = new BigInteger(30); +export function randomProbablePrime(bits: number, e: bigint, k: number) { + const _30n = BigInt(30); + const min = _1n << BigInt(bits - 1); /* * We can avoid any multiples of 3 and 5 by looking at n mod 30 * n mod 30 = 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 @@ -45,40 +41,38 @@ export async function randomProbablePrime(bits, e, k) { */ const adds = [1, 6, 5, 4, 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, 3, 2, 1, 2, 1, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 2]; - const n = await getRandomBigInteger(min, min.leftShift(one)); - let i = n.mod(thirty).toNumber(); + let n = getRandomBigInteger(min, min << _1n); + let i = bigIntToNumber(mod(n, _30n)); do { - n.iadd(new BigInteger(adds[i])); + n += BigInt(adds[i]); i = (i + adds[i]) % adds.length; // If reached the maximum, go back to the minimum. - if (n.bitLength() > bits) { - n.imod(min.leftShift(one)).iadd(min); - i = n.mod(thirty).toNumber(); + if (bitLength(n) > bits) { + n = mod(n, min << _1n); n += min; + i = bigIntToNumber(mod(n, _30n)); } - } while (!await isProbablePrime(n, e, k)); + } while (!isProbablePrime(n, e, k)); return n; } /** * Probabilistic primality testing - * @param {BigInteger} n - Number to test - * @param {BigInteger} e - Optional RSA exponent to check against the prime - * @param {Integer} k - Optional number of iterations of Miller-Rabin test - * @returns {boolean} - * @async + * @param n - Number to test + * @param e - Optional RSA exponent to check against the prime + * @param k - Optional number of iterations of Miller-Rabin test */ -export async function isProbablePrime(n, e, k) { - if (e && !n.dec().gcd(e).isOne()) { +export function isProbablePrime(n: bigint, e: bigint, k: number) { + if (e && gcd(n - _1n, e) !== _1n) { return false; } - if (!await divisionTest(n)) { + if (!divisionTest(n)) { return false; } - if (!await fermat(n)) { + if (!fermat(n)) { return false; } - if (!await millerRabin(n, k)) { + if (!millerRabin(n, k)) { return false; } // TODO implement the Lucas test @@ -89,21 +83,16 @@ export async function isProbablePrime(n, e, k) { /** * Tests whether n is probably prime or not using Fermat's test with b = 2. * Fails if b^(n-1) mod n != 1. - * @param {BigInteger} n - Number to test - * @param {BigInteger} b - Optional Fermat test base - * @returns {boolean} + * @param n - Number to test + * @param b - Optional Fermat test base */ -export async function fermat(n, b) { - const BigInteger = await util.getBigInteger(); - b = b || new BigInteger(2); - return b.modExp(n.dec(), n).isOne(); +export function fermat(n: bigint, b = BigInt(2)) { + return modExp(b, n - _1n, n) === _1n; } -export async function divisionTest(n) { - const BigInteger = await util.getBigInteger(); - return smallPrimes.every(m => { - return n.mod(new BigInteger(m)) !== 0; - }); +export function divisionTest(n: bigint) { + const _0n = BigInt(0); + return smallPrimes.every(m => mod(n, m) !== _0n); } // https://github.com/gpg/libgcrypt/blob/master/cipher/primegen.c @@ -187,7 +176,7 @@ const smallPrimes = [ 4801, 4813, 4817, 4831, 4861, 4871, 4877, 4889, 4903, 4909, 4919, 4931, 4933, 4937, 4943, 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999 -]; +].map(n => BigInt(n)); // Miller-Rabin - Miller Rabin algorithm for primality test @@ -222,43 +211,42 @@ const smallPrimes = [ /** * Tests whether n is probably prime or not using the Miller-Rabin test. * See HAC Remark 4.28. - * @param {BigInteger} n - Number to test - * @param {Integer} k - Optional number of iterations of Miller-Rabin test - * @param {Function} rand - Optional function to generate potential witnesses + * @param n - Number to test + * @param k - Optional number of iterations of Miller-Rabin test + * @param rand - Optional function to generate potential witnesses * @returns {boolean} * @async */ -export async function millerRabin(n, k, rand) { - const BigInteger = await util.getBigInteger(); - const len = n.bitLength(); +export function millerRabin(n: bigint, k: number, rand?: () => bigint) { + const len = bitLength(n); if (!k) { k = Math.max(1, (len / 48) | 0); } - const n1 = n.dec(); // n - 1 + const n1 = n - _1n; // n - 1 // Find d and s, (n - 1) = (2 ^ s) * d; let s = 0; - while (!n1.getBit(s)) { s++; } - const d = n.rightShift(new BigInteger(s)); + while (!getBit(n1, s)) { s++; } + const d = n >> BigInt(s); for (; k > 0; k--) { - const a = rand ? rand() : await getRandomBigInteger(new BigInteger(2), n1); + const a = rand ? rand() : getRandomBigInteger(BigInt(2), n1); - let x = a.modExp(d, n); - if (x.isOne() || x.equal(n1)) { + let x = modExp(a, d, n); + if (x === _1n || x === n1) { continue; } let i; for (i = 1; i < s; i++) { - x = x.mul(x).mod(n); + x = mod(x * x, n); - if (x.isOne()) { + if (x === _1n) { return false; } - if (x.equal(n1)) { + if (x === n1) { break; } } diff --git a/src/crypto/public_key/rsa.js b/src/crypto/public_key/rsa.js index 2343eb3e..07ac8484 100644 --- a/src/crypto/public_key/rsa.js +++ b/src/crypto/public_key/rsa.js @@ -18,42 +18,19 @@ /** * @fileoverview RSA implementation * @module crypto/public_key/rsa - * @private */ - import { randomProbablePrime } from './prime'; import { getRandomBigInteger } from '../random'; import util from '../../util'; import { uint8ArrayToB64, b64ToUint8Array } from '../../encoding/base64'; import { emsaEncode, emeEncode, emeDecode } from '../pkcs1'; import enums from '../../enums'; +import { bigIntToNumber, bigIntToUint8Array, bitLength, byteLength, mod, modExp, modInv, uint8ArrayToBigInt } from '../biginteger'; +import hash from '../hash'; const webCrypto = util.getWebCrypto(); const nodeCrypto = util.getNodeCrypto(); -const asn1 = nodeCrypto ? require('asn1.js') : undefined; - -/* eslint-disable no-invalid-this */ -const RSAPrivateKey = nodeCrypto ? asn1.define('RSAPrivateKey', function () { - this.seq().obj( // used for native NodeJS crypto - this.key('version').int(), // 0 - this.key('modulus').int(), // n - this.key('publicExponent').int(), // e - this.key('privateExponent').int(), // d - this.key('prime1').int(), // p - this.key('prime2').int(), // q - this.key('exponent1').int(), // dp - this.key('exponent2').int(), // dq - this.key('coefficient').int() // u - ); -}) : undefined; - -const RSAPublicKey = nodeCrypto ? asn1.define('RSAPubliceKey', function () { - this.seq().obj( // used for native NodeJS crypto - this.key('modulus').int(), // n - this.key('publicExponent').int() // e - ); -}) : undefined; -/* eslint-enable no-invalid-this */ +const _1n = BigInt(1); /** Create signature * @param {module:enums.hash} hashAlgo - Hash algorithm @@ -69,6 +46,14 @@ const RSAPublicKey = nodeCrypto ? asn1.define('RSAPubliceKey', function () { * @async */ export async function sign(hashAlgo, data, n, e, d, p, q, u, hashed) { + if (hash.getHashByteLength(hashAlgo) >= n.length) { + // Throw here instead of `emsaEncode` below, to provide a clearer and consistent error + // e.g. if a 512-bit RSA key is used with a SHA-512 digest. + // The size limit is actually slightly different but here we only care about throwing + // on common key sizes. + throw new Error('Digest size cannot exceed key modulus size'); + } + if (data && !util.isStream(data)) { if (util.getWebCrypto()) { try { @@ -167,16 +152,14 @@ export async function decrypt(data, n, e, d, p, q, u, randomPayload) { * @async */ export async function generate(bits, e) { - const BigInteger = await util.getBigInteger(); - - e = new BigInteger(e); + e = BigInt(e); // Native RSA keygen using Web Crypto if (util.getWebCrypto()) { const keyGenOpt = { name: 'RSASSA-PKCS1-v1_5', modulusLength: bits, // the specified keysize in bits - publicExponent: e.toUint8Array(), // take three bytes (max 65537) for exponent + publicExponent: bigIntToUint8Array(e), // take three bytes (max 65537) for exponent hash: { name: 'SHA-1' // not required for actual RSA keys, but for crypto api 'sign' and 'verify' } @@ -187,47 +170,24 @@ export async function generate(bits, e) { // https://tools.ietf.org/html/draft-ietf-jose-json-web-key-33 const jwk = await webCrypto.exportKey('jwk', keyPair.privateKey); // map JWK parameters to corresponding OpenPGP names - return { - n: b64ToUint8Array(jwk.n), - e: e.toUint8Array(), - d: b64ToUint8Array(jwk.d), - // switch p and q - p: b64ToUint8Array(jwk.q), - q: b64ToUint8Array(jwk.p), - // Since p and q are switched in places, u is the inverse of jwk.q - u: b64ToUint8Array(jwk.qi) - }; - } else if (util.getNodeCrypto() && nodeCrypto.generateKeyPair && RSAPrivateKey) { + return jwkToPrivate(jwk, e); + } else if (util.getNodeCrypto()) { const opts = { modulusLength: bits, - publicExponent: e.toNumber(), - publicKeyEncoding: { type: 'pkcs1', format: 'der' }, - privateKeyEncoding: { type: 'pkcs1', format: 'der' } + publicExponent: bigIntToNumber(e), + publicKeyEncoding: { type: 'pkcs1', format: 'jwk' }, + privateKeyEncoding: { type: 'pkcs1', format: 'jwk' } }; - const prv = await new Promise((resolve, reject) => { - nodeCrypto.generateKeyPair('rsa', opts, (err, _, der) => { + const jwk = await new Promise((resolve, reject) => { + nodeCrypto.generateKeyPair('rsa', opts, (err, _, jwkPrivateKey) => { if (err) { reject(err); } else { - resolve(RSAPrivateKey.decode(der, 'der')); + resolve(jwkPrivateKey); } }); }); - /** - * OpenPGP spec differs from DER spec, DER: `u = (inverse of q) mod p`, OpenPGP: `u = (inverse of p) mod q`. - * @link https://tools.ietf.org/html/rfc3447#section-3.2 - * @link https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-08#section-5.6.1 - */ - return { - n: prv.modulus.toArrayLike(Uint8Array), - e: prv.publicExponent.toArrayLike(Uint8Array), - d: prv.privateExponent.toArrayLike(Uint8Array), - // switch p and q - p: prv.prime2.toArrayLike(Uint8Array), - q: prv.prime1.toArrayLike(Uint8Array), - // Since p and q are switched in places, we can keep u as defined by DER - u: prv.coefficient.toArrayLike(Uint8Array) - }; + return jwkToPrivate(jwk, e); } // RSA keygen fallback using 40 iterations of the Miller-Rabin test @@ -237,26 +197,26 @@ export async function generate(bits, e) { let q; let n; do { - q = await randomProbablePrime(bits - (bits >> 1), e, 40); - p = await randomProbablePrime(bits >> 1, e, 40); - n = p.mul(q); - } while (n.bitLength() !== bits); + q = randomProbablePrime(bits - (bits >> 1), e, 40); + p = randomProbablePrime(bits >> 1, e, 40); + n = p * q; + } while (bitLength(n) !== bits); - const phi = p.dec().imul(q.dec()); + const phi = (p - _1n) * (q - _1n); - if (q.lt(p)) { + if (q < p) { [p, q] = [q, p]; } return { - n: n.toUint8Array(), - e: e.toUint8Array(), - d: e.modInv(phi).toUint8Array(), - p: p.toUint8Array(), - q: q.toUint8Array(), + n: bigIntToUint8Array(n), + e: bigIntToUint8Array(e), + d: bigIntToUint8Array(modInv(e, phi)), + p: bigIntToUint8Array(p), + q: bigIntToUint8Array(q), // dp: d.mod(p.subn(1)), // dq: d.mod(q.subn(1)), - u: p.modInv(q).toUint8Array() + u: bigIntToUint8Array(modInv(p, q)) }; } @@ -272,25 +232,24 @@ export async function generate(bits, e) { * @async */ export async function validateParams(n, e, d, p, q, u) { - const BigInteger = await util.getBigInteger(); - n = new BigInteger(n); - p = new BigInteger(p); - q = new BigInteger(q); + n = uint8ArrayToBigInt(n); + p = uint8ArrayToBigInt(p); + q = uint8ArrayToBigInt(q); // expect pq = n - if (!p.mul(q).equal(n)) { + if ((p * q) !== n) { return false; } - const two = new BigInteger(2); + const _2n = BigInt(2); // expect p*u = 1 mod q - u = new BigInteger(u); - if (!p.mul(u).mod(q).isOne()) { + u = uint8ArrayToBigInt(u); + if (mod(p * u, q) !== BigInt(1)) { return false; } - e = new BigInteger(e); - d = new BigInteger(d); + e = uint8ArrayToBigInt(e); + d = uint8ArrayToBigInt(d); /** * In RSA pkcs#1 the exponents (d, e) are inverses modulo lcm(p-1, q-1) * We check that [de = 1 mod (p-1)] and [de = 1 mod (q-1)] @@ -298,11 +257,11 @@ export async function validateParams(n, e, d, p, q, u) { * * We blind the multiplication with r, and check that rde = r mod lcm(p-1, q-1) */ - const nSizeOver3 = new BigInteger(Math.floor(n.bitLength() / 3)); - const r = await getRandomBigInteger(two, two.leftShift(nSizeOver3)); // r in [ 2, 2^{|n|/3} ) < p and q - const rde = r.mul(d).mul(e); + const nSizeOver3 = BigInt(Math.floor(bitLength(n) / 3)); + const r = getRandomBigInteger(_2n, _2n << nSizeOver3); // r in [ 2, 2^{|n|/3} ) < p and q + const rde = r * d * e; - const areInverses = rde.mod(p.dec()).equal(r) && rde.mod(q.dec()).equal(r); + const areInverses = mod(rde, p - _1n) === r && mod(rde, q - _1n) === r; if (!areInverses) { return false; } @@ -311,14 +270,10 @@ export async function validateParams(n, e, d, p, q, u) { } async function bnSign(hashAlgo, n, d, hashed) { - const BigInteger = await util.getBigInteger(); - n = new BigInteger(n); - const m = new BigInteger(await emsaEncode(hashAlgo, hashed, n.byteLength())); - d = new BigInteger(d); - if (m.gte(n)) { - throw new Error('Message size cannot exceed modulus size'); - } - return m.modExp(d, n).toUint8Array('be', n.byteLength()); + n = uint8ArrayToBigInt(n); + const m = uint8ArrayToBigInt(emsaEncode(hashAlgo, hashed, byteLength(n))); + d = uint8ArrayToBigInt(d); + return bigIntToUint8Array(modExp(m, d, n), 'be', byteLength(n)); } async function webSign(hashName, data, n, e, d, p, q, u) { @@ -339,48 +294,23 @@ async function webSign(hashName, data, n, e, d, p, q, u) { } async function nodeSign(hashAlgo, data, n, e, d, p, q, u) { - const { default: BN } = await import('bn.js'); - const pBNum = new BN(p); - const qBNum = new BN(q); - const dBNum = new BN(d); - const dq = dBNum.mod(qBNum.subn(1)); // d mod (q-1) - const dp = dBNum.mod(pBNum.subn(1)); // d mod (p-1) const sign = nodeCrypto.createSign(enums.read(enums.hash, hashAlgo)); sign.write(data); sign.end(); - const keyObject = { - version: 0, - modulus: new BN(n), - publicExponent: new BN(e), - privateExponent: new BN(d), - // switch p and q - prime1: new BN(q), - prime2: new BN(p), - // switch dp and dq - exponent1: dq, - exponent2: dp, - coefficient: new BN(u) - }; - if (typeof nodeCrypto.createPrivateKey !== 'undefined') { //from version 11.6.0 Node supports der encoded key objects - const der = RSAPrivateKey.encode(keyObject, 'der'); - return new Uint8Array(sign.sign({ key: der, format: 'der', type: 'pkcs1' })); - } - const pem = RSAPrivateKey.encode(keyObject, 'pem', { - label: 'RSA PRIVATE KEY' - }); - return new Uint8Array(sign.sign(pem)); + + const jwk = await privateToJWK(n, e, d, p, q, u); + return new Uint8Array(sign.sign({ key: jwk, format: 'jwk', type: 'pkcs1' })); } async function bnVerify(hashAlgo, s, n, e, hashed) { - const BigInteger = await util.getBigInteger(); - n = new BigInteger(n); - s = new BigInteger(s); - e = new BigInteger(e); - if (s.gte(n)) { + n = uint8ArrayToBigInt(n); + s = uint8ArrayToBigInt(s); + e = uint8ArrayToBigInt(e); + if (s >= n) { throw new Error('Signature size cannot exceed modulus size'); } - const EM1 = s.modExp(e, n).toUint8Array('be', n.byteLength()); - const EM2 = await emsaEncode(hashAlgo, hashed, n.byteLength()); + const EM1 = bigIntToUint8Array(modExp(s, e, n), 'be', byteLength(n)); + const EM2 = emsaEncode(hashAlgo, hashed, byteLength(n)); return util.equalsUint8Array(EM1, EM2); } @@ -394,93 +324,41 @@ async function webVerify(hashName, data, s, n, e) { } async function nodeVerify(hashAlgo, data, s, n, e) { - const { default: BN } = await import('bn.js'); + const jwk = publicToJWK(n, e); + const key = { key: jwk, format: 'jwk', type: 'pkcs1' }; const verify = nodeCrypto.createVerify(enums.read(enums.hash, hashAlgo)); verify.write(data); verify.end(); - const keyObject = { - modulus: new BN(n), - publicExponent: new BN(e) - }; - let key; - if (typeof nodeCrypto.createPrivateKey !== 'undefined') { //from version 11.6.0 Node supports der encoded key objects - const der = RSAPublicKey.encode(keyObject, 'der'); - key = { key: der, format: 'der', type: 'pkcs1' }; - } else { - key = RSAPublicKey.encode(keyObject, 'pem', { - label: 'RSA PUBLIC KEY' - }); - } + try { - return await verify.verify(key, s); + return verify.verify(key, s); } catch (err) { return false; } } async function nodeEncrypt(data, n, e) { - const { default: BN } = await import('bn.js'); + const jwk = publicToJWK(n, e); + const key = { key: jwk, format: 'jwk', type: 'pkcs1', padding: nodeCrypto.constants.RSA_PKCS1_PADDING }; - const keyObject = { - modulus: new BN(n), - publicExponent: new BN(e) - }; - let key; - if (typeof nodeCrypto.createPrivateKey !== 'undefined') { - const der = RSAPublicKey.encode(keyObject, 'der'); - key = { key: der, format: 'der', type: 'pkcs1', padding: nodeCrypto.constants.RSA_PKCS1_PADDING }; - } else { - const pem = RSAPublicKey.encode(keyObject, 'pem', { - label: 'RSA PUBLIC KEY' - }); - key = { key: pem, padding: nodeCrypto.constants.RSA_PKCS1_PADDING }; - } return new Uint8Array(nodeCrypto.publicEncrypt(key, data)); } async function bnEncrypt(data, n, e) { - const BigInteger = await util.getBigInteger(); - n = new BigInteger(n); - data = new BigInteger(emeEncode(data, n.byteLength())); - e = new BigInteger(e); - if (data.gte(n)) { + n = uint8ArrayToBigInt(n); + data = uint8ArrayToBigInt(emeEncode(data, byteLength(n))); + e = uint8ArrayToBigInt(e); + if (data >= n) { throw new Error('Message size cannot exceed modulus size'); } - return data.modExp(e, n).toUint8Array('be', n.byteLength()); + return bigIntToUint8Array(modExp(data, e, n), 'be', byteLength(n)); } async function nodeDecrypt(data, n, e, d, p, q, u) { - const { default: BN } = await import('bn.js'); + const jwk = await privateToJWK(n, e, d, p, q, u); + const key = { key: jwk, format: 'jwk' , type: 'pkcs1', padding: nodeCrypto.constants.RSA_PKCS1_PADDING }; - const pBNum = new BN(p); - const qBNum = new BN(q); - const dBNum = new BN(d); - const dq = dBNum.mod(qBNum.subn(1)); // d mod (q-1) - const dp = dBNum.mod(pBNum.subn(1)); // d mod (p-1) - const keyObject = { - version: 0, - modulus: new BN(n), - publicExponent: new BN(e), - privateExponent: new BN(d), - // switch p and q - prime1: new BN(q), - prime2: new BN(p), - // switch dp and dq - exponent1: dq, - exponent2: dp, - coefficient: new BN(u) - }; - let key; - if (typeof nodeCrypto.createPrivateKey !== 'undefined') { - const der = RSAPrivateKey.encode(keyObject, 'der'); - key = { key: der, format: 'der' , type: 'pkcs1', padding: nodeCrypto.constants.RSA_PKCS1_PADDING }; - } else { - const pem = RSAPrivateKey.encode(keyObject, 'pem', { - label: 'RSA PRIVATE KEY' - }); - key = { key: pem, padding: nodeCrypto.constants.RSA_PKCS1_PADDING }; - } try { return new Uint8Array(nodeCrypto.privateDecrypt(key, data)); } catch (err) { @@ -489,35 +367,32 @@ async function nodeDecrypt(data, n, e, d, p, q, u) { } async function bnDecrypt(data, n, e, d, p, q, u, randomPayload) { - const BigInteger = await util.getBigInteger(); - data = new BigInteger(data); - n = new BigInteger(n); - e = new BigInteger(e); - d = new BigInteger(d); - p = new BigInteger(p); - q = new BigInteger(q); - u = new BigInteger(u); - if (data.gte(n)) { + data = uint8ArrayToBigInt(data); + n = uint8ArrayToBigInt(n); + e = uint8ArrayToBigInt(e); + d = uint8ArrayToBigInt(d); + p = uint8ArrayToBigInt(p); + q = uint8ArrayToBigInt(q); + u = uint8ArrayToBigInt(u); + if (data >= n) { throw new Error('Data too large.'); } - const dq = d.mod(q.dec()); // d mod (q-1) - const dp = d.mod(p.dec()); // d mod (p-1) + const dq = mod(d, q - _1n); // d mod (q-1) + const dp = mod(d, p - _1n); // d mod (p-1) - const unblinder = (await getRandomBigInteger(new BigInteger(2), n)).mod(n); - const blinder = unblinder.modInv(n).modExp(e, n); - data = data.mul(blinder).mod(n); + const unblinder = getRandomBigInteger(BigInt(2), n); + const blinder = modExp(modInv(unblinder, n), e, n); + data = mod(data * blinder, n); + const mp = modExp(data, dp, p); // data**{d mod (q-1)} mod p + const mq = modExp(data, dq, q); // data**{d mod (p-1)} mod q + const h = mod(u * (mq - mp), q); // u * (mq-mp) mod q (operands already < q) - const mp = data.modExp(dp, p); // data**{d mod (q-1)} mod p - const mq = data.modExp(dq, q); // data**{d mod (p-1)} mod q - const h = u.mul(mq.sub(mp)).mod(q); // u * (mq-mp) mod q (operands already < q) + let result = h * p + mp; // result < n due to relations above - let result = h.mul(p).add(mp); // result < n due to relations above + result = mod(result * unblinder, n); - result = result.mul(unblinder).mod(n); - - - return emeDecode(result.toUint8Array('be', n.byteLength()), randomPayload); + return emeDecode(bigIntToUint8Array(result, 'be', byteLength(n)), randomPayload); } /** Convert Openpgp private key params to jwk key according to @@ -531,15 +406,14 @@ async function bnDecrypt(data, n, e, d, p, q, u, randomPayload) { * @param {Uint8Array} u */ async function privateToJWK(n, e, d, p, q, u) { - const BigInteger = await util.getBigInteger(); - const pNum = new BigInteger(p); - const qNum = new BigInteger(q); - const dNum = new BigInteger(d); + const pNum = uint8ArrayToBigInt(p); + const qNum = uint8ArrayToBigInt(q); + const dNum = uint8ArrayToBigInt(d); - let dq = dNum.mod(qNum.dec()); // d mod (q-1) - let dp = dNum.mod(pNum.dec()); // d mod (p-1) - dp = dp.toUint8Array(); - dq = dq.toUint8Array(); + let dq = mod(dNum, qNum - _1n); // d mod (q-1) + let dp = mod(dNum, pNum - _1n); // d mod (p-1) + dp = bigIntToUint8Array(dp); + dq = bigIntToUint8Array(dq); return { kty: 'RSA', n: uint8ArrayToB64(n, true), @@ -570,3 +444,17 @@ function publicToJWK(n, e) { ext: true }; } + +/** Convert JWK private key to OpenPGP private key params */ +function jwkToPrivate(jwk, e) { + return { + n: b64ToUint8Array(jwk.n), + e: bigIntToUint8Array(e), + d: b64ToUint8Array(jwk.d), + // switch p and q + p: b64ToUint8Array(jwk.q), + q: b64ToUint8Array(jwk.p), + // Since p and q are switched in places, u is the inverse of jwk.q + u: b64ToUint8Array(jwk.qi) + }; +} diff --git a/src/crypto/random.js b/src/crypto/random.js index 022bb4aa..3ebe4a1f 100644 --- a/src/crypto/random.js +++ b/src/crypto/random.js @@ -20,8 +20,8 @@ /** * @fileoverview Provides tools for retrieving secure randomness from browsers or Node.js * @module crypto/random - * @private */ +import { byteLength, mod, uint8ArrayToBigInt } from './biginteger'; import util from '../util'; const nodeCrypto = util.getNodeCrypto(); @@ -32,38 +32,33 @@ const nodeCrypto = util.getNodeCrypto(); * @returns {Uint8Array} Random byte array. */ export function getRandomBytes(length) { - const buf = new Uint8Array(length); - if (nodeCrypto) { - const bytes = nodeCrypto.randomBytes(buf.length); - buf.set(bytes); - } else if (typeof crypto !== 'undefined' && crypto.getRandomValues) { - crypto.getRandomValues(buf); + const webcrypto = typeof crypto !== 'undefined' ? crypto : nodeCrypto?.webcrypto; + if (webcrypto?.getRandomValues) { + const buf = new Uint8Array(length); + return webcrypto.getRandomValues(buf); } else { throw new Error('No secure random number generator available.'); } - return buf; } /** - * Create a secure random BigInteger that is greater than or equal to min and less than max. - * @param {module:BigInteger} min - Lower bound, included - * @param {module:BigInteger} max - Upper bound, excluded - * @returns {Promise} Random BigInteger. + * Create a secure random BigInt that is greater than or equal to min and less than max. + * @param {bigint} min - Lower bound, included + * @param {bigint} max - Upper bound, excluded + * @returns {bigint} Random BigInt. * @async */ -export async function getRandomBigInteger(min, max) { - const BigInteger = await util.getBigInteger(); - - if (max.lt(min)) { +export function getRandomBigInteger(min, max) { + if (max < min) { throw new Error('Illegal parameter value: max <= min'); } - const modulus = max.sub(min); - const bytes = modulus.byteLength(); + const modulus = max - min; + const bytes = byteLength(modulus); // Using a while loop is necessary to avoid bias introduced by the mod operation. // However, we request 64 extra random bits so that the bias is negligible. // Section B.1.1 here: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf - const r = new BigInteger(await getRandomBytes(bytes + 8)); - return r.mod(modulus).add(min); + const r = uint8ArrayToBigInt(getRandomBytes(bytes + 8)); + return mod(r, modulus) + min; } diff --git a/src/crypto/signature.js b/src/crypto/signature.js index b4d2aed3..4f78a1ad 100644 --- a/src/crypto/signature.js +++ b/src/crypto/signature.js @@ -1,7 +1,6 @@ /** * @fileoverview Provides functions for asymmetric signing and signature verification * @module crypto/signature - * @private */ import publicKey from './public_key'; @@ -28,10 +27,10 @@ export function parseSignatureParams(algo, signature) { case enums.publicKey.rsaEncryptSign: case enums.publicKey.rsaEncrypt: case enums.publicKey.rsaSign: { - const s = util.readMPI(signature.subarray(read)); + const s = util.readMPI(signature.subarray(read)); read += s.length + 2; // The signature needs to be the same length as the public key modulo n. // We pad s on signature verification, where we have access to n. - return { s }; + return { read, signatureParams: { s } }; } // Algorithm-Specific Fields for DSA or ECDSA signatures: // - MPI of DSA or ECDSA value r. @@ -39,28 +38,34 @@ export function parseSignatureParams(algo, signature) { case enums.publicKey.dsa: case enums.publicKey.ecdsa: { + // If the signature payload sizes are unexpected, we will throw on verification, + // where we also have access to the OID curve from the key. const r = util.readMPI(signature.subarray(read)); read += r.length + 2; - const s = util.readMPI(signature.subarray(read)); - return { r, s }; + const s = util.readMPI(signature.subarray(read)); read += s.length + 2; + return { read, signatureParams: { r, s } }; } // Algorithm-Specific Fields for legacy EdDSA signatures: // - MPI of an EC point r. // - EdDSA value s, in MPI, in the little endian representation case enums.publicKey.eddsaLegacy: { - // When parsing little-endian MPI data, we always need to left-pad it, as done with big-endian values: - // https://www.ietf.org/archive/id/draft-ietf-openpgp-rfc4880bis-10.html#section-3.2-9 - let r = util.readMPI(signature.subarray(read)); read += r.length + 2; - r = util.leftPad(r, 32); - let s = util.readMPI(signature.subarray(read)); - s = util.leftPad(s, 32); - return { r, s }; + // Only Curve25519Legacy is supported (no Curve448Legacy), but the relevant checks are done on key parsing and signature + // verification: if the signature payload sizes are unexpected, we will throw on verification, + // where we also have access to the OID curve from the key. + const r = util.readMPI(signature.subarray(read)); read += r.length + 2; + const s = util.readMPI(signature.subarray(read)); read += s.length + 2; + return { read, signatureParams: { r, s } }; } // Algorithm-Specific Fields for Ed25519 signatures: // - 64 octets of the native signature - case enums.publicKey.ed25519: { - const RS = signature.subarray(read, read + 64); read += RS.length; - return { RS }; + // Algorithm-Specific Fields for Ed448 signatures: + // - 114 octets of the native signature + case enums.publicKey.ed25519: + case enums.publicKey.ed448: { + const rsSize = 2 * publicKey.elliptic.eddsa.getPayloadSize(algo); + const RS = util.readExactSubarray(signature, read, read + rsSize); read += RS.length; + return { read, signatureParams: { RS } }; } + default: throw new UnsupportedError('Unknown signature algorithm.'); } @@ -104,10 +109,15 @@ export async function verify(algo, hashAlgo, signature, publicParams, data, hash } case enums.publicKey.eddsaLegacy: { const { oid, Q } = publicParams; - // signature already padded on parsing - return publicKey.elliptic.eddsaLegacy.verify(oid, hashAlgo, signature, data, Q, hashed); + const curveSize = new publicKey.elliptic.CurveWithOID(oid).payloadSize; + // When dealing little-endian MPI data, we always need to left-pad it, as done with big-endian values: + // https://www.ietf.org/archive/id/draft-ietf-openpgp-rfc4880bis-10.html#section-3.2-9 + const r = util.leftPad(signature.r, curveSize); + const s = util.leftPad(signature.s, curveSize); + return publicKey.elliptic.eddsaLegacy.verify(oid, hashAlgo, { r, s }, data, Q, hashed); } - case enums.publicKey.ed25519: { + case enums.publicKey.ed25519: + case enums.publicKey.ed448: { const { A } = publicParams; return publicKey.elliptic.eddsa.verify(algo, hashAlgo, signature, data, A, hashed); } @@ -148,9 +158,8 @@ export async function sign(algo, hashAlgo, publicKeyParams, privateKeyParams, da const { x } = privateKeyParams; return publicKey.dsa.sign(hashAlgo, hashed, g, p, q, x); } - case enums.publicKey.elgamal: { + case enums.publicKey.elgamal: throw new Error('Signing with Elgamal is not defined in the OpenPGP standard.'); - } case enums.publicKey.ecdsa: { const { oid, Q } = publicKeyParams; const { d } = privateKeyParams; @@ -161,7 +170,8 @@ export async function sign(algo, hashAlgo, publicKeyParams, privateKeyParams, da const { seed } = privateKeyParams; return publicKey.elliptic.eddsaLegacy.sign(oid, hashAlgo, data, Q, seed, hashed); } - case enums.publicKey.ed25519: { + case enums.publicKey.ed25519: + case enums.publicKey.ed448: { const { A } = publicKeyParams; const { seed } = privateKeyParams; return publicKey.elliptic.eddsa.sign(algo, hashAlgo, data, A, seed, hashed); diff --git a/src/encoding/armor.js b/src/encoding/armor.js index 47442c34..5404d8f6 100644 --- a/src/encoding/armor.js +++ b/src/encoding/armor.js @@ -47,33 +47,33 @@ function getType(text) { // parts, and this is the Xth part out of Y. if (/MESSAGE, PART \d+\/\d+/.test(header[1])) { return enums.armor.multipartSection; - } else + } // BEGIN PGP MESSAGE, PART X // Used for multi-part messages, where this is the Xth part of an // unspecified number of parts. Requires the MESSAGE-ID Armor // Header to be used. if (/MESSAGE, PART \d+/.test(header[1])) { return enums.armor.multipartLast; - } else + } // BEGIN PGP SIGNED MESSAGE if (/SIGNED MESSAGE/.test(header[1])) { return enums.armor.signed; - } else + } // BEGIN PGP MESSAGE // Used for signed, encrypted, or compressed files. if (/MESSAGE/.test(header[1])) { return enums.armor.message; - } else + } // BEGIN PGP PUBLIC KEY BLOCK // Used for armoring public keys. if (/PUBLIC KEY BLOCK/.test(header[1])) { return enums.armor.publicKey; - } else + } // BEGIN PGP PRIVATE KEY BLOCK // Used for armoring private keys. if (/PRIVATE KEY BLOCK/.test(header[1])) { return enums.armor.privateKey; - } else + } // BEGIN PGP SIGNATURE // Used for detached signatures, OpenPGP/MIME signatures, and // cleartext signatures. Note that PGP 2.x uses BEGIN PGP MESSAGE @@ -107,7 +107,6 @@ function addheader(customComment, config) { return result; } - /** * Calculates a checksum over the given data and returns it base64 encoded * @param {String | ReadableStream} data - Data to create a CRC-24 checksum for @@ -201,24 +200,21 @@ function verifyHeaders(headers) { } /** - * Splits a message into two parts, the body and the checksum. This is an internal function - * @param {String} text - OpenPGP armored message part - * @returns {Object} An object with attribute "body" containing the body. - * and an attribute "checksum" containing the checksum. + * Remove the (optional) checksum from an armored message. + * @param {String} text - OpenPGP armored message + * @returns {String} The body of the armored message. * @private */ -function splitChecksum(text) { +function removeChecksum(text) { let body = text; - let checksum = ''; const lastEquals = text.lastIndexOf('='); if (lastEquals >= 0 && lastEquals !== text.length - 1) { // '=' as the last char means no checksum body = text.slice(0, lastEquals); - checksum = text.slice(lastEquals + 1).substr(0, 4); } - return { body: body, checksum: checksum }; + return body; } /** @@ -230,7 +226,7 @@ function splitChecksum(text) { * @async * @static */ -export function unarmor(input, config = defaultConfig) { +export function unarmor(input) { // eslint-disable-next-line no-async-promise-executor return new Promise(async (resolve, reject) => { try { @@ -243,8 +239,7 @@ export function unarmor(input, config = defaultConfig) { let headersDone; let text = []; let textDone; - let checksum; - let data = base64.decode(stream.transformPair(input, async (readable, writable) => { + const data = base64.decode(stream.transformPair(input, async (readable, writable) => { const reader = stream.getReader(readable); try { while (true) { @@ -267,12 +262,12 @@ export function unarmor(input, config = defaultConfig) { } else { verifyHeaders(lastHeaders); headersDone = true; - if (textDone || type !== 2) { + if (textDone || type !== enums.armor.signed) { resolve({ text, data, headers, type }); break; } } - } else if (!textDone && type === 2) { + } else if (!textDone && type === enums.armor.signed) { if (!reSplit.test(line)) { // Reverse dash-escaping for msg text.push(line.replace(/^- /, '')); @@ -309,9 +304,8 @@ export function unarmor(input, config = defaultConfig) { if (parts.length === 1) { throw new Error('Misformed armored text'); } - const split = splitChecksum(parts[0].slice(0, -1)); - checksum = split.checksum; - await writer.write(split.body); + const body = removeChecksum(parts[0].slice(0, -1)); + await writer.write(body); break; } } @@ -321,24 +315,6 @@ export function unarmor(input, config = defaultConfig) { await writer.abort(e); } })); - data = stream.transformPair(data, async (readable, writable) => { - const checksumVerified = stream.readToEnd(getCheckSum(stream.passiveClone(readable))); - checksumVerified.catch(() => {}); - await stream.pipe(readable, writable, { - preventClose: true - }); - const writer = stream.getWriter(writable); - try { - const checksumVerifiedString = (await checksumVerified).replace('\n', ''); - if (checksum !== checksumVerifiedString && (checksum || config.checksumRequired)) { - throw new Error('Ascii armor integrity check failed'); - } - await writer.ready; - await writer.close(); - } catch (e) { - await writer.abort(e); - } - }); } catch (e) { reject(e); } @@ -358,10 +334,13 @@ export function unarmor(input, config = defaultConfig) { * @param {Integer} [partIndex] * @param {Integer} [partTotal] * @param {String} [customComment] - Additional comment to add to the armored string + * @param {Boolean} [emitChecksum] - Whether to compute and include the CRC checksum + * (NB: some types of data must not include it, but compliance is left as responsibility of the caller: this function does not carry out any checks) + * @param {Object} [config] - Full configuration, defaults to openpgp.config * @returns {String | ReadableStream} Armored text. * @static */ -export function armor(messageType, body, partIndex, partTotal, customComment, config = defaultConfig) { +export function armor(messageType, body, partIndex, partTotal, customComment, emitChecksum = false, config = defaultConfig) { let text; let hash; if (messageType === enums.armor.signed) { @@ -369,59 +348,62 @@ export function armor(messageType, body, partIndex, partTotal, customComment, co hash = body.hash; body = body.data; } - const bodyClone = stream.passiveClone(body); + // unless explicitly forbidden by the spec, we need to include the checksum to work around a GnuPG bug + // where data fails to be decoded if the base64 ends with no padding chars (=) (see https://dev.gnupg.org/T7071) + const maybeBodyClone = emitChecksum && stream.passiveClone(body); + const result = []; switch (messageType) { case enums.armor.multipartSection: result.push('-----BEGIN PGP MESSAGE, PART ' + partIndex + '/' + partTotal + '-----\n'); result.push(addheader(customComment, config)); result.push(base64.encode(body)); - result.push('=', getCheckSum(bodyClone)); + maybeBodyClone && result.push('=', getCheckSum(maybeBodyClone)); result.push('-----END PGP MESSAGE, PART ' + partIndex + '/' + partTotal + '-----\n'); break; case enums.armor.multipartLast: result.push('-----BEGIN PGP MESSAGE, PART ' + partIndex + '-----\n'); result.push(addheader(customComment, config)); result.push(base64.encode(body)); - result.push('=', getCheckSum(bodyClone)); + maybeBodyClone && result.push('=', getCheckSum(maybeBodyClone)); result.push('-----END PGP MESSAGE, PART ' + partIndex + '-----\n'); break; case enums.armor.signed: result.push('-----BEGIN PGP SIGNED MESSAGE-----\n'); - result.push('Hash: ' + hash + '\n\n'); + result.push(hash ? `Hash: ${hash}\n\n` : '\n'); result.push(text.replace(/^-/mg, '- -')); result.push('\n-----BEGIN PGP SIGNATURE-----\n'); result.push(addheader(customComment, config)); result.push(base64.encode(body)); - result.push('=', getCheckSum(bodyClone)); + maybeBodyClone && result.push('=', getCheckSum(maybeBodyClone)); result.push('-----END PGP SIGNATURE-----\n'); break; case enums.armor.message: result.push('-----BEGIN PGP MESSAGE-----\n'); result.push(addheader(customComment, config)); result.push(base64.encode(body)); - result.push('=', getCheckSum(bodyClone)); + maybeBodyClone && result.push('=', getCheckSum(maybeBodyClone)); result.push('-----END PGP MESSAGE-----\n'); break; case enums.armor.publicKey: result.push('-----BEGIN PGP PUBLIC KEY BLOCK-----\n'); result.push(addheader(customComment, config)); result.push(base64.encode(body)); - result.push('=', getCheckSum(bodyClone)); + maybeBodyClone && result.push('=', getCheckSum(maybeBodyClone)); result.push('-----END PGP PUBLIC KEY BLOCK-----\n'); break; case enums.armor.privateKey: result.push('-----BEGIN PGP PRIVATE KEY BLOCK-----\n'); result.push(addheader(customComment, config)); result.push(base64.encode(body)); - result.push('=', getCheckSum(bodyClone)); + maybeBodyClone && result.push('=', getCheckSum(maybeBodyClone)); result.push('-----END PGP PRIVATE KEY BLOCK-----\n'); break; case enums.armor.signature: result.push('-----BEGIN PGP SIGNATURE-----\n'); result.push(addheader(customComment, config)); result.push(base64.encode(body)); - result.push('=', getCheckSum(bodyClone)); + maybeBodyClone && result.push('=', getCheckSum(maybeBodyClone)); result.push('-----END PGP SIGNATURE-----\n'); break; } diff --git a/src/encoding/base64.js b/src/encoding/base64.js index dfea632d..ee95d56b 100644 --- a/src/encoding/base64.js +++ b/src/encoding/base64.js @@ -13,7 +13,6 @@ /** * @module encoding/base64 - * @private */ import * as stream from '@openpgp/web-stream-tools'; diff --git a/src/enums.js b/src/enums.js index 220ed807..b9ea1e2b 100644 --- a/src/enums.js +++ b/src/enums.js @@ -13,74 +13,41 @@ export default { */ curve: { /** NIST P-256 Curve */ - 'p256': 'p256', - 'P-256': 'p256', - 'secp256r1': 'p256', - 'prime256v1': 'p256', - '1.2.840.10045.3.1.7': 'p256', - '2a8648ce3d030107': 'p256', - '2A8648CE3D030107': 'p256', + 'nistP256': 'nistP256', + /** @deprecated use `nistP256` instead */ + 'p256': 'nistP256', /** NIST P-384 Curve */ - 'p384': 'p384', - 'P-384': 'p384', - 'secp384r1': 'p384', - '1.3.132.0.34': 'p384', - '2b81040022': 'p384', - '2B81040022': 'p384', + 'nistP384': 'nistP384', + /** @deprecated use `nistP384` instead */ + 'p384': 'nistP384', /** NIST P-521 Curve */ - 'p521': 'p521', - 'P-521': 'p521', - 'secp521r1': 'p521', - '1.3.132.0.35': 'p521', - '2b81040023': 'p521', - '2B81040023': 'p521', + 'nistP521': 'nistP521', + /** @deprecated use `nistP521` instead */ + 'p521': 'nistP521', /** SECG SECP256k1 Curve */ - 'secp256k1': 'secp256k1', - '1.3.132.0.10': 'secp256k1', - '2b8104000a': 'secp256k1', - '2B8104000A': 'secp256k1', + 'secp256k1': 'secp256k1', /** Ed25519 - deprecated by crypto-refresh (replaced by standaone Ed25519 algo) */ - 'ed25519Legacy': 'ed25519', - 'ED25519': 'ed25519', + 'ed25519Legacy': 'ed25519Legacy', /** @deprecated use `ed25519Legacy` instead */ - 'ed25519': 'ed25519', - 'Ed25519': 'ed25519', - '1.3.6.1.4.1.11591.15.1': 'ed25519', - '2b06010401da470f01': 'ed25519', - '2B06010401DA470F01': 'ed25519', + 'ed25519': 'ed25519Legacy', /** Curve25519 - deprecated by crypto-refresh (replaced by standaone X25519 algo) */ - 'curve25519Legacy': 'curve25519', - 'X25519': 'curve25519', - 'cv25519': 'curve25519', + 'curve25519Legacy': 'curve25519Legacy', /** @deprecated use `curve25519Legacy` instead */ - 'curve25519': 'curve25519', - 'Curve25519': 'curve25519', - '1.3.6.1.4.1.3029.1.5.1': 'curve25519', - '2b060104019755010501': 'curve25519', - '2B060104019755010501': 'curve25519', + 'curve25519': 'curve25519Legacy', /** BrainpoolP256r1 Curve */ 'brainpoolP256r1': 'brainpoolP256r1', - '1.3.36.3.3.2.8.1.1.7': 'brainpoolP256r1', - '2b2403030208010107': 'brainpoolP256r1', - '2B2403030208010107': 'brainpoolP256r1', /** BrainpoolP384r1 Curve */ 'brainpoolP384r1': 'brainpoolP384r1', - '1.3.36.3.3.2.8.1.1.11': 'brainpoolP384r1', - '2b240303020801010b': 'brainpoolP384r1', - '2B240303020801010B': 'brainpoolP384r1', /** BrainpoolP512r1 Curve */ - 'brainpoolP512r1': 'brainpoolP512r1', - '1.3.36.3.3.2.8.1.1.13': 'brainpoolP512r1', - '2b240303020801010d': 'brainpoolP512r1', - '2B240303020801010D': 'brainpoolP512r1' + 'brainpoolP512r1': 'brainpoolP512r1' }, /** A string to key specifier type @@ -91,6 +58,7 @@ export default { simple: 0, salted: 1, iterated: 3, + argon2: 4, gnu: 101 }, @@ -115,11 +83,7 @@ export default { ecdsa: 19, /** EdDSA (Sign only) - deprecated by crypto-refresh (replaced by `ed25519` identifier below) * [{@link https://tools.ietf.org/html/draft-koch-eddsa-for-openpgp-04|Draft RFC}] */ - eddsaLegacy: 22, // NB: this is declared before `eddsa` to translate 22 to 'eddsa' for backwards compatibility - /** @deprecated use `eddsaLegacy` instead */ - ed25519Legacy: 22, - /** @deprecated use `eddsaLegacy` instead */ - eddsa: 22, + eddsaLegacy: 22, /** Reserved for AEDH */ aedh: 23, /** Reserved for AEDSA */ @@ -139,7 +103,6 @@ export default { * @readonly */ symmetric: { - plaintext: 0, /** Not implemented! */ idea: 1, tripledes: 2, @@ -175,7 +138,9 @@ export default { sha256: 8, sha384: 9, sha512: 10, - sha224: 11 + sha224: 11, + sha3_256: 12, + sha3_512: 14 }, /** A list of hash names as accepted by webCrypto functions. @@ -196,6 +161,7 @@ export default { aead: { eax: 1, ocb: 2, + gcm: 3, experimentalGCM: 100 // Private algorithm }, @@ -221,7 +187,8 @@ export default { userAttribute: 17, symEncryptedIntegrityProtectedData: 18, modificationDetectionCode: 19, - aeadEncryptedData: 20 // see IETF draft: https://tools.ietf.org/html/draft-ford-openpgp-format-00#section-2.1 + aeadEncryptedData: 20, // see IETF draft: https://tools.ietf.org/html/draft-ford-openpgp-format-00#section-2.1 + padding: 21 }, /** Data types in the literal packet @@ -377,7 +344,7 @@ export default { placeholderBackwardsCompatibility: 10, preferredSymmetricAlgorithms: 11, revocationKey: 12, - issuer: 16, + issuerKeyID: 16, notationData: 20, preferredHashAlgorithms: 21, preferredCompressionAlgorithms: 22, @@ -392,7 +359,8 @@ export default { signatureTarget: 31, embeddedSignature: 32, issuerFingerprint: 33, - preferredAEADAlgorithms: 34 + preferredAEADAlgorithms: 34, + preferredCipherSuites: 39 }, /** Key flags @@ -461,7 +429,8 @@ export default { aead: 2, /** 0x04 - Version 5 Public-Key Packet format and corresponding new * fingerprint format */ - v5Keys: 4 + v5Keys: 4, + seipdv2: 8 }, /** diff --git a/src/key/factory.js b/src/key/factory.js index 44d7462f..68cdfb95 100644 --- a/src/key/factory.js +++ b/src/key/factory.js @@ -64,9 +64,9 @@ function createKey(packetlist) { /** - * Generates a new OpenPGP key. Supports RSA and ECC keys. + * Generates a new OpenPGP key. Supports RSA and ECC keys, as well as the newer Curve448 and Curve25519 keys. * By default, primary and subkeys will be of same type. - * @param {ecc|rsa} options.type The primary key algorithm type: ECC or RSA + * @param {ecc|rsa|curve448|curve25519} options.type The primary key algorithm type: ECC, RSA, Curve448 or Curve25519 (new format). * @param {String} options.curve Elliptic curve for ECC keys * @param {Integer} options.rsaBits Number of bits for RSA keys * @param {Array} options.userIDs User IDs as strings or objects: 'Jo Doe ' or { name:'Jo Doe', email:'info@jo.com' } @@ -188,59 +188,81 @@ async function wrapKeyObject(secretKeyPacket, secretSubkeyPackets, options, conf const packetlist = new PacketList(); packetlist.push(secretKeyPacket); - await Promise.all(options.userIDs.map(async function(userID, index) { - function createPreferredAlgos(algos, preferredAlgo) { - return [preferredAlgo, ...algos.filter(algo => algo !== preferredAlgo)]; - } - - const userIDPacket = UserIDPacket.fromObject(userID); - const dataToSign = {}; - dataToSign.userID = userIDPacket; - dataToSign.key = secretKeyPacket; + function createPreferredAlgos(algos, preferredAlgo) { + return [preferredAlgo, ...algos.filter(algo => algo !== preferredAlgo)]; + } + function getKeySignatureProperties() { const signatureProperties = {}; - signatureProperties.signatureType = enums.signature.certGeneric; signatureProperties.keyFlags = [enums.keyFlags.certifyKeys | enums.keyFlags.signData]; - signatureProperties.preferredSymmetricAlgorithms = createPreferredAlgos([ - // prefer aes256, aes128, then aes192 (no WebCrypto support: https://www.chromium.org/blink/webcrypto#TOC-AES-support) + const symmetricAlgorithms = createPreferredAlgos([ + // prefer aes256, aes128, no aes192 (no Web Crypto support in Chrome: https://www.chromium.org/blink/webcrypto#TOC-AES-support) enums.symmetric.aes256, - enums.symmetric.aes128, - enums.symmetric.aes192 + enums.symmetric.aes128 ], config.preferredSymmetricAlgorithm); + signatureProperties.preferredSymmetricAlgorithms = symmetricAlgorithms; if (config.aeadProtect) { - signatureProperties.preferredAEADAlgorithms = createPreferredAlgos([ + const aeadAlgorithms = createPreferredAlgos([ + enums.aead.gcm, enums.aead.eax, enums.aead.ocb ], config.preferredAEADAlgorithm); + signatureProperties.preferredCipherSuites = aeadAlgorithms.flatMap(aeadAlgorithm => { + return symmetricAlgorithms.map(symmetricAlgorithm => { + return [symmetricAlgorithm, aeadAlgorithm]; + }); + }); } signatureProperties.preferredHashAlgorithms = createPreferredAlgos([ // prefer fast asm.js implementations (SHA-256) enums.hash.sha256, - enums.hash.sha512 + enums.hash.sha512, + enums.hash.sha3_256, + enums.hash.sha3_512 ], config.preferredHashAlgorithm); signatureProperties.preferredCompressionAlgorithms = createPreferredAlgos([ + enums.compression.uncompressed, enums.compression.zlib, - enums.compression.zip, - enums.compression.uncompressed + enums.compression.zip ], config.preferredCompressionAlgorithm); - if (index === 0) { - signatureProperties.isPrimaryUserID = true; - } // integrity protection always enabled signatureProperties.features = [0]; signatureProperties.features[0] |= enums.features.modificationDetection; if (config.aeadProtect) { - signatureProperties.features[0] |= enums.features.aead; - } - if (config.v5Keys) { - signatureProperties.features[0] |= enums.features.v5Keys; + signatureProperties.features[0] |= enums.features.seipdv2; } if (options.keyExpirationTime > 0) { signatureProperties.keyExpirationTime = options.keyExpirationTime; signatureProperties.keyNeverExpires = false; } + return signatureProperties; + } - const signaturePacket = await helper.createSignaturePacket(dataToSign, null, secretKeyPacket, signatureProperties, options.date, undefined, undefined, undefined, config); + if (secretKeyPacket.version === 6) { // add direct key signature with key prefs + const dataToSign = { + key: secretKeyPacket + }; + + const signatureProperties = getKeySignatureProperties(); + signatureProperties.signatureType = enums.signature.key; + + const signaturePacket = await helper.createSignaturePacket(dataToSign, [], secretKeyPacket, signatureProperties, options.date, undefined, undefined, undefined, config); + packetlist.push(signaturePacket); + } + + await Promise.all(options.userIDs.map(async function(userID, index) { + const userIDPacket = UserIDPacket.fromObject(userID); + const dataToSign = { + userID: userIDPacket, + key: secretKeyPacket + }; + const signatureProperties = secretKeyPacket.version !== 6 ? getKeySignatureProperties() : {}; + signatureProperties.signatureType = enums.signature.certPositive; + if (index === 0) { + signatureProperties.isPrimaryUserID = true; + } + + const signaturePacket = await helper.createSignaturePacket(dataToSign, [], secretKeyPacket, signatureProperties, options.date, undefined, undefined, undefined, config); return { userIDPacket, signaturePacket }; })).then(list => { @@ -264,7 +286,7 @@ async function wrapKeyObject(secretKeyPacket, secretSubkeyPackets, options, conf // Add revocation signature packet for creating a revocation certificate. // This packet should be removed before returning the key. const dataToSign = { key: secretKeyPacket }; - packetlist.push(await helper.createSignaturePacket(dataToSign, null, secretKeyPacket, { + packetlist.push(await helper.createSignaturePacket(dataToSign, [], secretKeyPacket, { signatureType: enums.signature.keyRevocation, reasonForRevocationFlag: enums.reasonForRevocation.noReason, reasonForRevocationString: '' @@ -318,7 +340,12 @@ export async function readKey({ armoredKey, binaryKey, config, ...rest }) { input = binaryKey; } const packetlist = await PacketList.fromBinary(input, allowedKeyPackets, config); - return createKey(packetlist); + const keyIndex = packetlist.indexOfTag(enums.packet.publicKey, enums.packet.secretKey); + if (keyIndex.length === 0) { + throw new Error('No key packet found'); + } + const firstKeyPacketList = packetlist.slice(keyIndex[0], keyIndex[1]); + return createKey(firstKeyPacketList); } /** @@ -355,7 +382,15 @@ export async function readPrivateKey({ armoredKey, binaryKey, config, ...rest }) input = binaryKey; } const packetlist = await PacketList.fromBinary(input, allowedKeyPackets, config); - return new PrivateKey(packetlist); + const keyIndex = packetlist.indexOfTag(enums.packet.publicKey, enums.packet.secretKey); + for (let i = 0; i < keyIndex.length; i++) { + if (packetlist[keyIndex[i]].constructor.tag === enums.packet.publicKey) { + continue; + } + const firstPrivateKeyList = packetlist.slice(keyIndex[i], keyIndex[i + 1]); + return new PrivateKey(firstPrivateKeyList); + } + throw new Error('No secret key packet found'); } /** @@ -434,14 +469,17 @@ export async function readPrivateKeys({ armoredKeys, binaryKeys, config }) { } const keys = []; const packetlist = await PacketList.fromBinary(input, allowedKeyPackets, config); - const keyIndex = packetlist.indexOfTag(enums.packet.secretKey); - if (keyIndex.length === 0) { - throw new Error('No secret key packet found'); - } + const keyIndex = packetlist.indexOfTag(enums.packet.publicKey, enums.packet.secretKey); for (let i = 0; i < keyIndex.length; i++) { + if (packetlist[keyIndex[i]].constructor.tag === enums.packet.publicKey) { + continue; + } const oneKeyList = packetlist.slice(keyIndex[i], keyIndex[i + 1]); const newKey = new PrivateKey(oneKeyList); keys.push(newKey); } + if (keys.length === 0) { + throw new Error('No secret key packet found'); + } return keys; } diff --git a/src/key/helper.js b/src/key/helper.js index 05a83331..cd1643d5 100644 --- a/src/key/helper.js +++ b/src/key/helper.js @@ -1,7 +1,6 @@ /** * @fileoverview Provides helpers methods for key module * @module key/helper - * @private */ import { @@ -36,6 +35,8 @@ export async function generateSecretKey(options, config) { * Returns the valid and non-expired signature that has the latest creation date, while ignoring signatures created in the future. * @param {Array} signatures - List of signatures * @param {PublicKeyPacket|PublicSubkeyPacket} publicKey - Public key packet to verify the signature + * @param {module:enums.signature} signatureType - Signature type to determine how to hash the data (NB: for userID signatures, + * `enums.signatures.certGeneric` should be given regardless of the actual trust level) * @param {Date} date - Use the given date instead of the current time * @param {Object} config - full configuration * @returns {Promise} The latest valid signature. @@ -89,7 +90,7 @@ export async function createBindingSignature(subkey, primaryKey, options, config const signatureProperties = { signatureType: enums.signature.subkeyBinding }; if (options.sign) { signatureProperties.keyFlags = [enums.keyFlags.signData]; - signatureProperties.embeddedSignature = await createSignaturePacket(dataToSign, null, subkey, { + signatureProperties.embeddedSignature = await createSignaturePacket(dataToSign, [], subkey, { signatureType: enums.signature.keyBinding }, options.date, undefined, undefined, undefined, config); } else { @@ -99,83 +100,166 @@ export async function createBindingSignature(subkey, primaryKey, options, config signatureProperties.keyExpirationTime = options.keyExpirationTime; signatureProperties.keyNeverExpires = false; } - const subkeySignaturePacket = await createSignaturePacket(dataToSign, null, primaryKey, signatureProperties, options.date, undefined, undefined, undefined, config); + const subkeySignaturePacket = await createSignaturePacket(dataToSign, [], primaryKey, signatureProperties, options.date, undefined, undefined, undefined, config); return subkeySignaturePacket; } /** - * Returns the preferred signature hash algorithm of a key - * @param {Key} [key] - The key to get preferences from - * @param {SecretKeyPacket|SecretSubkeyPacket} keyPacket - key packet used for signing + * Returns the preferred signature hash algorithm for a set of keys. + * @param {Array} [targetKeys] - The keys to get preferences from + * @param {SecretKeyPacket|SecretSubkeyPacket} signingKeyPacket - key packet used for signing * @param {Date} [date] - Use the given date for verification instead of the current time - * @param {Object} [userID] - User ID + * @param {Object} [targetUserID] - User IDs corresponding to `targetKeys` to get preferences from * @param {Object} config - full configuration * @returns {Promise} * @async */ -export async function getPreferredHashAlgo(key, keyPacket, date = new Date(), userID = {}, config) { - let hashAlgo = config.preferredHashAlgorithm; - let prefAlgo = hashAlgo; - if (key) { - const primaryUser = await key.getPrimaryUser(date, userID, config); - if (primaryUser.selfCertification.preferredHashAlgorithms) { - [prefAlgo] = primaryUser.selfCertification.preferredHashAlgorithms; - hashAlgo = crypto.hash.getHashByteLength(hashAlgo) <= crypto.hash.getHashByteLength(prefAlgo) ? - prefAlgo : hashAlgo; +export async function getPreferredHashAlgo(targetKeys, signingKeyPacket, date = new Date(), targetUserIDs = [], config) { + /** + * If `preferredSenderAlgo` appears in the prefs of all recipients, we pick it; otherwise, we use the + * strongest supported algo (`defaultAlgo` is always implicitly supported by all keys). + * if no keys are available, `preferredSenderAlgo` is returned. + * For ECC signing key, the curve preferred hash is taken into account as well (see logic below). + */ + const defaultAlgo = enums.hash.sha256; // MUST implement + const preferredSenderAlgo = config.preferredHashAlgorithm; + + const supportedAlgosPerTarget = await Promise.all(targetKeys.map(async (key, i) => { + const selfCertification = await key.getPrimarySelfSignature(date, targetUserIDs[i], config); + const targetPrefs = selfCertification.preferredHashAlgorithms; + return targetPrefs; + })); + const supportedAlgosMap = new Map(); // use Map over object to preserve numeric keys + for (const supportedAlgos of supportedAlgosPerTarget) { + for (const hashAlgo of supportedAlgos) { + try { + // ensure that `hashAlgo` is recognized/implemented by us, otherwise e.g. `getHashByteLength` will throw later on + const supportedAlgo = enums.write(enums.hash, hashAlgo); + supportedAlgosMap.set( + supportedAlgo, + supportedAlgosMap.has(supportedAlgo) ? supportedAlgosMap.get(supportedAlgo) + 1 : 1 + ); + } catch {} } } - switch (keyPacket.algorithm) { - case enums.publicKey.ecdsa: - case enums.publicKey.eddsaLegacy: - case enums.publicKey.ed25519: - prefAlgo = crypto.getPreferredCurveHashAlgo(keyPacket.algorithm, keyPacket.publicParams.oid); + const isSupportedHashAlgo = hashAlgo => targetKeys.length === 0 || supportedAlgosMap.get(hashAlgo) === targetKeys.length || hashAlgo === defaultAlgo; + const getStrongestSupportedHashAlgo = () => { + if (supportedAlgosMap.size === 0) { + return defaultAlgo; + } + const sortedHashAlgos = Array.from(supportedAlgosMap.keys()) + .filter(hashAlgo => isSupportedHashAlgo(hashAlgo)) + .sort((algoA, algoB) => crypto.hash.getHashByteLength(algoA) - crypto.hash.getHashByteLength(algoB)); + const strongestHashAlgo = sortedHashAlgos[0]; + // defaultAlgo is always implicilty supported, and might be stronger than the rest + return crypto.hash.getHashByteLength(strongestHashAlgo) >= crypto.hash.getHashByteLength(defaultAlgo) ? strongestHashAlgo : defaultAlgo; + }; + + const eccAlgos = new Set([ + enums.publicKey.ecdsa, + enums.publicKey.eddsaLegacy, + enums.publicKey.ed25519, + enums.publicKey.ed448 + ]); + + if (eccAlgos.has(signingKeyPacket.algorithm)) { + // For ECC, the returned hash algo MUST be at least as strong as `preferredCurveHashAlgo`, see: + // - ECDSA: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.2-5 + // - EdDSALegacy: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.3-3 + // - Ed25519: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.4-4 + // - Ed448: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.5-4 + // Hence, we return the `preferredHashAlgo` as long as it's supported and strong enough; + // Otherwise, we look at the strongest supported algo, and ultimately fallback to the curve + // preferred algo, even if not supported by all targets. + const preferredCurveAlgo = crypto.getPreferredCurveHashAlgo(signingKeyPacket.algorithm, signingKeyPacket.publicParams.oid); + + const preferredSenderAlgoIsSupported = isSupportedHashAlgo(preferredSenderAlgo); + const preferredSenderAlgoStrongerThanCurveAlgo = crypto.hash.getHashByteLength(preferredSenderAlgo) >= crypto.hash.getHashByteLength(preferredCurveAlgo); + + if (preferredSenderAlgoIsSupported && preferredSenderAlgoStrongerThanCurveAlgo) { + return preferredSenderAlgo; + } else { + const strongestSupportedAlgo = getStrongestSupportedHashAlgo(); + return crypto.hash.getHashByteLength(strongestSupportedAlgo) >= crypto.hash.getHashByteLength(preferredCurveAlgo) ? + strongestSupportedAlgo : + preferredCurveAlgo; + } } - return crypto.hash.getHashByteLength(hashAlgo) <= crypto.hash.getHashByteLength(prefAlgo) ? - prefAlgo : hashAlgo; + + // `preferredSenderAlgo` may be weaker than the default, but we do not guard against this, + // since it was manually set by the sender. + return isSupportedHashAlgo(preferredSenderAlgo) ? preferredSenderAlgo : getStrongestSupportedHashAlgo(); } /** - * Returns the preferred symmetric/aead/compression algorithm for a set of keys - * @param {'symmetric'|'aead'|'compression'} type - Type of preference to return + * Returns the preferred compression algorithm for a set of keys * @param {Array} [keys] - Set of keys * @param {Date} [date] - Use the given date for verification instead of the current time * @param {Array} [userIDs] - User IDs * @param {Object} [config] - Full configuration, defaults to openpgp.config - * @returns {Promise} Preferred algorithm + * @returns {Promise} Preferred compression algorithm * @async */ -export async function getPreferredAlgo(type, keys = [], date = new Date(), userIDs = [], config = defaultConfig) { - const defaultAlgo = { // these are all must-implement in rfc4880bis - 'symmetric': enums.symmetric.aes128, - 'aead': enums.aead.eax, - 'compression': enums.compression.uncompressed - }[type]; - const preferredSenderAlgo = { - 'symmetric': config.preferredSymmetricAlgorithm, - 'aead': config.preferredAEADAlgorithm, - 'compression': config.preferredCompressionAlgorithm - }[type]; - const prefPropertyName = { - 'symmetric': 'preferredSymmetricAlgorithms', - 'aead': 'preferredAEADAlgorithms', - 'compression': 'preferredCompressionAlgorithms' - }[type]; +export async function getPreferredCompressionAlgo(keys = [], date = new Date(), userIDs = [], config = defaultConfig) { + const defaultAlgo = enums.compression.uncompressed; + const preferredSenderAlgo = config.preferredCompressionAlgorithm; // if preferredSenderAlgo appears in the prefs of all recipients, we pick it // otherwise we use the default algo // if no keys are available, preferredSenderAlgo is returned const senderAlgoSupport = await Promise.all(keys.map(async function(key, i) { - const primaryUser = await key.getPrimaryUser(date, userIDs[i], config); - const recipientPrefs = primaryUser.selfCertification[prefPropertyName]; + const selfCertification = await key.getPrimarySelfSignature(date, userIDs[i], config); + const recipientPrefs = selfCertification.preferredCompressionAlgorithms; return !!recipientPrefs && recipientPrefs.indexOf(preferredSenderAlgo) >= 0; })); return senderAlgoSupport.every(Boolean) ? preferredSenderAlgo : defaultAlgo; } +/** + * Returns the preferred symmetric and AEAD algorithm (if any) for a set of keys + * @param {Array} [keys] - Set of keys + * @param {Date} [date] - Use the given date for verification instead of the current time + * @param {Array} [userIDs] - User IDs + * @param {Object} [config] - Full configuration, defaults to openpgp.config + * @returns {Promise<{ symmetricAlgo: module:enums.symmetric, aeadAlgo: module:enums.aead | undefined }>} Object containing the preferred symmetric algorithm, and the preferred AEAD algorithm, or undefined if CFB is preferred + * @async + */ +export async function getPreferredCipherSuite(keys = [], date = new Date(), userIDs = [], config = defaultConfig) { + const selfSigs = await Promise.all(keys.map((key, i) => key.getPrimarySelfSignature(date, userIDs[i], config))); + const withAEAD = keys.length ? + selfSigs.every(selfSig => selfSig.features && (selfSig.features[0] & enums.features.seipdv2)) : + config.aeadProtect; + + if (withAEAD) { + const defaultCipherSuite = { symmetricAlgo: enums.symmetric.aes128, aeadAlgo: enums.aead.ocb }; + const desiredCipherSuites = [ + { symmetricAlgo: config.preferredSymmetricAlgorithm, aeadAlgo: config.preferredAEADAlgorithm }, + { symmetricAlgo: config.preferredSymmetricAlgorithm, aeadAlgo: enums.aead.ocb }, + { symmetricAlgo: enums.symmetric.aes128, aeadAlgo: config.preferredAEADAlgorithm } + ]; + for (const desiredCipherSuite of desiredCipherSuites) { + if (selfSigs.every(selfSig => selfSig.preferredCipherSuites && selfSig.preferredCipherSuites.some( + cipherSuite => cipherSuite[0] === desiredCipherSuite.symmetricAlgo && cipherSuite[1] === desiredCipherSuite.aeadAlgo + ))) { + return desiredCipherSuite; + } + } + return defaultCipherSuite; + } + const defaultSymAlgo = enums.symmetric.aes128; + const desiredSymAlgo = config.preferredSymmetricAlgorithm; + return { + symmetricAlgo: selfSigs.every(selfSig => selfSig.preferredSymmetricAlgorithms && selfSig.preferredSymmetricAlgorithms.includes(desiredSymAlgo)) ? + desiredSymAlgo : + defaultSymAlgo, + aeadAlgo: undefined + }; +} + /** * Create signature packet * @param {Object} dataToSign - Contains packets to be signed - * @param {PrivateKey} privateKey - key to get preferences from + * @param {Array} recipientKeys - keys to get preferences from * @param {SecretKeyPacket| * SecretSubkeyPacket} signingKeyPacket secret key packet for signing * @param {Object} [signatureProperties] - Properties to write on the signature packet before signing @@ -186,7 +270,7 @@ export async function getPreferredAlgo(type, keys = [], date = new Date(), userI * @param {Object} config - full configuration * @returns {Promise} Signature packet. */ -export async function createSignaturePacket(dataToSign, privateKey, signingKeyPacket, signatureProperties, date, userID, notations = [], detached = false, config) { +export async function createSignaturePacket(dataToSign, recipientKeys, signingKeyPacket, signatureProperties, date, recipientUserIDs, notations = [], detached = false, config) { if (signingKeyPacket.isDummy()) { throw new Error('Cannot sign with a gnu-dummy key.'); } @@ -196,9 +280,9 @@ export async function createSignaturePacket(dataToSign, privateKey, signingKeyPa const signaturePacket = new SignaturePacket(); Object.assign(signaturePacket, signatureProperties); signaturePacket.publicKeyAlgorithm = signingKeyPacket.algorithm; - signaturePacket.hashAlgorithm = await getPreferredHashAlgo(privateKey, signingKeyPacket, date, userID, config); - signaturePacket.rawNotations = notations; - await signaturePacket.sign(signingKeyPacket, dataToSign, date, detached); + signaturePacket.hashAlgorithm = await getPreferredHashAlgo(recipientKeys, signingKeyPacket, date, recipientUserIDs, config); + signaturePacket.rawNotations = [...notations]; + await signaturePacket.sign(signingKeyPacket, dataToSign, date, detached, config); return signaturePacket; } @@ -260,8 +344,14 @@ export async function isDataRevoked(primaryKey, signatureType, dataToVerify, rev // `verifyAllCertifications`.) !signature || revocationSignature.issuerKeyID.equals(signature.issuerKeyID) ) { + const isHardRevocation = ![ + enums.reasonForRevocation.keyRetired, + enums.reasonForRevocation.keySuperseded, + enums.reasonForRevocation.userIDInvalid + ].includes(revocationSignature.reasonForRevocationFlag); + await revocationSignature.verify( - key, signatureType, dataToVerify, config.revocationsExpire ? date : null, false, config + key, signatureType, dataToVerify, isHardRevocation ? null : date, false, config ); // TODO get an identifier of the revoked object instead @@ -294,28 +384,6 @@ export function getKeyExpirationTime(keyPacket, signature) { return expirationTime ? new Date(expirationTime) : Infinity; } -/** - * Returns whether aead is supported by all keys in the set - * @param {Array} keys - Set of keys - * @param {Date} [date] - Use the given date for verification instead of the current time - * @param {Array} [userIDs] - User IDs - * @param {Object} config - full configuration - * @returns {Promise} - * @async - */ -export async function isAEADSupported(keys, date = new Date(), userIDs = [], config = defaultConfig) { - let supported = true; - // TODO replace when Promise.some or Promise.any are implemented - await Promise.all(keys.map(async function(key, i) { - const primaryUser = await key.getPrimaryUser(date, userIDs[i], config); - if (!primaryUser.selfCertification.features || - !(primaryUser.selfCertification.features[0] & enums.features.aead)) { - supported = false; - } - })); - return supported; -} - export function sanitizeKeyOptions(options, subkeyDefaults = {}) { options.type = options.type || subkeyDefaults.type; options.curve = options.curve || subkeyDefaults.curve; @@ -327,13 +395,14 @@ export function sanitizeKeyOptions(options, subkeyDefaults = {}) { options.sign = options.sign || false; switch (options.type) { - case 'ecc': + case 'ecc': // NB: this case also handles legacy eddsa and x25519 keys, based on `options.curve` try { options.curve = enums.write(enums.curve, options.curve); } catch (e) { throw new Error('Unknown curve'); } - if (options.curve === enums.curve.ed25519Legacy || options.curve === enums.curve.curve25519Legacy) { + if (options.curve === enums.curve.ed25519Legacy || options.curve === enums.curve.curve25519Legacy || + options.curve === 'ed25519' || options.curve === 'curve25519') { // keep support for curve names without 'Legacy' addition, for now options.curve = options.sign ? enums.curve.ed25519Legacy : enums.curve.curve25519Legacy; } if (options.sign) { @@ -342,6 +411,12 @@ export function sanitizeKeyOptions(options, subkeyDefaults = {}) { options.algorithm = enums.publicKey.ecdh; } break; + case 'curve25519': + options.algorithm = options.sign ? enums.publicKey.ed25519 : enums.publicKey.x25519; + break; + case 'curve448': + options.algorithm = options.sign ? enums.publicKey.ed448 : enums.publicKey.x448; + break; case 'rsa': options.algorithm = enums.publicKey.rsaEncryptSign; break; @@ -351,37 +426,69 @@ export function sanitizeKeyOptions(options, subkeyDefaults = {}) { return options; } -export function isValidSigningKeyPacket(keyPacket, signature) { - const keyAlgo = keyPacket.algorithm; - return keyAlgo !== enums.publicKey.rsaEncrypt && - keyAlgo !== enums.publicKey.elgamal && - keyAlgo !== enums.publicKey.ecdh && - keyAlgo !== enums.publicKey.x25519 && - (!signature.keyFlags || - (signature.keyFlags[0] & enums.keyFlags.signData) !== 0); +export function validateSigningKeyPacket(keyPacket, signature, config) { + switch (keyPacket.algorithm) { + case enums.publicKey.rsaEncryptSign: + case enums.publicKey.rsaSign: + case enums.publicKey.dsa: + case enums.publicKey.ecdsa: + case enums.publicKey.eddsaLegacy: + case enums.publicKey.ed25519: + case enums.publicKey.ed448: + if (!signature.keyFlags && !config.allowMissingKeyFlags) { + throw new Error('None of the key flags is set: consider passing `config.allowMissingKeyFlags`'); + } + return !signature.keyFlags || + (signature.keyFlags[0] & enums.keyFlags.signData) !== 0; + default: + return false; + } } -export function isValidEncryptionKeyPacket(keyPacket, signature) { - const keyAlgo = keyPacket.algorithm; - return keyAlgo !== enums.publicKey.dsa && - keyAlgo !== enums.publicKey.rsaSign && - keyAlgo !== enums.publicKey.ecdsa && - keyAlgo !== enums.publicKey.eddsaLegacy && - keyAlgo !== enums.publicKey.ed25519 && - (!signature.keyFlags || - (signature.keyFlags[0] & enums.keyFlags.encryptCommunication) !== 0 || - (signature.keyFlags[0] & enums.keyFlags.encryptStorage) !== 0); +export function validateEncryptionKeyPacket(keyPacket, signature, config) { + switch (keyPacket.algorithm) { + case enums.publicKey.rsaEncryptSign: + case enums.publicKey.rsaEncrypt: + case enums.publicKey.elgamal: + case enums.publicKey.ecdh: + case enums.publicKey.x25519: + case enums.publicKey.x448: + if (!signature.keyFlags && !config.allowMissingKeyFlags) { + throw new Error('None of the key flags is set: consider passing `config.allowMissingKeyFlags`'); + } + return !signature.keyFlags || + (signature.keyFlags[0] & enums.keyFlags.encryptCommunication) !== 0 || + (signature.keyFlags[0] & enums.keyFlags.encryptStorage) !== 0; + default: + return false; + } } -export function isValidDecryptionKeyPacket(signature, config) { - if (config.allowInsecureDecryptionWithSigningKeys) { - // This is only relevant for RSA keys, all other signing algorithms cannot decrypt - return true; +export function validateDecryptionKeyPacket(keyPacket, signature, config) { + if (!signature.keyFlags && !config.allowMissingKeyFlags) { + throw new Error('None of the key flags is set: consider passing `config.allowMissingKeyFlags`'); } - return !signature.keyFlags || - (signature.keyFlags[0] & enums.keyFlags.encryptCommunication) !== 0 || - (signature.keyFlags[0] & enums.keyFlags.encryptStorage) !== 0; + switch (keyPacket.algorithm) { + case enums.publicKey.rsaEncryptSign: + case enums.publicKey.rsaEncrypt: + case enums.publicKey.elgamal: + case enums.publicKey.ecdh: + case enums.publicKey.x25519: + case enums.publicKey.x448: { + const isValidSigningKeyPacket = !signature.keyFlags || (signature.keyFlags[0] & enums.keyFlags.signData) !== 0; + if (isValidSigningKeyPacket && config.allowInsecureDecryptionWithSigningKeys) { + // This is only relevant for RSA keys, all other signing algorithms cannot decrypt + return true; + } + + return !signature.keyFlags || + (signature.keyFlags[0] & enums.keyFlags.encryptCommunication) !== 0 || + (signature.keyFlags[0] & enums.keyFlags.encryptStorage) !== 0; + } + default: + return false; + } } /** diff --git a/src/key/index.js b/src/key/index.js index 47c43019..6781baa9 100644 --- a/src/key/index.js +++ b/src/key/index.js @@ -8,9 +8,9 @@ import { } from './factory'; import { - getPreferredAlgo, - isAEADSupported, getPreferredHashAlgo, + getPreferredCompressionAlgo, + getPreferredCipherSuite, createSignaturePacket } from './helper'; @@ -25,9 +25,9 @@ export { readPrivateKeys, generate, reformat, - getPreferredAlgo, - isAEADSupported, getPreferredHashAlgo, + getPreferredCompressionAlgo, + getPreferredCipherSuite, createSignaturePacket, PrivateKey, PublicKey, diff --git a/src/key/key.js b/src/key/key.js index 72286b1c..8e608572 100644 --- a/src/key/key.js +++ b/src/key/key.js @@ -259,6 +259,11 @@ class Key { async getSigningKey(keyID = null, date = new Date(), userID = {}, config = defaultConfig) { await this.verifyPrimaryKey(date, userID, config); const primaryKey = this.keyPacket; + try { + helper.checkKeyRequirements(primaryKey, config); + } catch (err) { + throw util.wrapError('Could not verify primary key', err); + } const subkeys = this.subkeys.slice().sort((a, b) => b.keyPacket.created - a.keyPacket.created); let exception; for (const subkey of subkeys) { @@ -269,7 +274,7 @@ class Key { const bindingSignature = await helper.getLatestValidSignature( subkey.bindingSignatures, primaryKey, enums.signature.subkeyBinding, dataToVerify, date, config ); - if (!helper.isValidSigningKeyPacket(subkey.keyPacket, bindingSignature)) { + if (!helper.validateSigningKeyPacket(subkey.keyPacket, bindingSignature, config)) { continue; } if (!bindingSignature.embeddedSignature) { @@ -288,9 +293,9 @@ class Key { } try { - const primaryUser = await this.getPrimaryUser(date, userID, config); + const selfCertification = await this.getPrimarySelfSignature(date, userID, config); if ((!keyID || primaryKey.getKeyID().equals(keyID)) && - helper.isValidSigningKeyPacket(primaryKey, primaryUser.selfCertification, config)) { + helper.validateSigningKeyPacket(primaryKey, selfCertification, config)) { helper.checkKeyRequirements(primaryKey, config); return this; } @@ -313,6 +318,11 @@ class Key { async getEncryptionKey(keyID, date = new Date(), userID = {}, config = defaultConfig) { await this.verifyPrimaryKey(date, userID, config); const primaryKey = this.keyPacket; + try { + helper.checkKeyRequirements(primaryKey, config); + } catch (err) { + throw util.wrapError('Could not verify primary key', err); + } // V4: by convention subkeys are preferred for encryption service const subkeys = this.subkeys.slice().sort((a, b) => b.keyPacket.created - a.keyPacket.created); let exception; @@ -322,7 +332,7 @@ class Key { await subkey.verify(date, config); const dataToVerify = { key: primaryKey, bind: subkey.keyPacket }; const bindingSignature = await helper.getLatestValidSignature(subkey.bindingSignatures, primaryKey, enums.signature.subkeyBinding, dataToVerify, date, config); - if (helper.isValidEncryptionKeyPacket(subkey.keyPacket, bindingSignature)) { + if (helper.validateEncryptionKeyPacket(subkey.keyPacket, bindingSignature, config)) { helper.checkKeyRequirements(subkey.keyPacket, config); return subkey; } @@ -334,9 +344,9 @@ class Key { try { // if no valid subkey for encryption, evaluate primary key - const primaryUser = await this.getPrimaryUser(date, userID, config); + const selfCertification = await this.getPrimarySelfSignature(date, userID, config); if ((!keyID || primaryKey.getKeyID().equals(keyID)) && - helper.isValidEncryptionKeyPacket(primaryKey, primaryUser.selfCertification)) { + helper.validateEncryptionKeyPacket(primaryKey, selfCertification, config)) { helper.checkKeyRequirements(primaryKey, config); return this; } @@ -380,18 +390,20 @@ class Key { throw new Error('Primary key is revoked'); } // check for valid, unrevoked, unexpired self signature - const { selfCertification } = await this.getPrimaryUser(date, userID, config); + const selfCertification = await this.getPrimarySelfSignature(date, userID, config); // check for expiration time in binding signatures if (helper.isDataExpired(primaryKey, selfCertification, date)) { throw new Error('Primary key is expired'); } - // check for expiration time in direct signatures - const directSignature = await helper.getLatestValidSignature( - this.directSignatures, primaryKey, enums.signature.key, { key: primaryKey }, date, config - ).catch(() => {}); // invalid signatures are discarded, to avoid breaking the key + if (primaryKey.version !== 6) { + // check for expiration time in direct signatures (for V6 keys, the above already did so) + const directSignature = await helper.getLatestValidSignature( + this.directSignatures, primaryKey, enums.signature.key, { key: primaryKey }, date, config + ).catch(() => {}); // invalid signatures are discarded, to avoid breaking the key - if (directSignature && helper.isDataExpired(primaryKey, directSignature, date)) { - throw new Error('Primary key is expired'); + if (directSignature && helper.isDataExpired(primaryKey, directSignature, date)) { + throw new Error('Primary key is expired'); + } } } @@ -406,12 +418,13 @@ class Key { async getExpirationTime(userID, config = defaultConfig) { let primaryKeyExpiry; try { - const { selfCertification } = await this.getPrimaryUser(null, userID, config); + const selfCertification = await this.getPrimarySelfSignature(null, userID, config); const selfSigKeyExpiry = helper.getKeyExpirationTime(this.keyPacket, selfCertification); const selfSigExpiry = selfCertification.getExpirationTime(); - const directSignature = await helper.getLatestValidSignature( - this.directSignatures, this.keyPacket, enums.signature.key, { key: this.keyPacket }, null, config - ).catch(() => {}); + const directSignature = this.keyPacket.version !== 6 && // For V6 keys, the above already returns the direct-key signature. + await helper.getLatestValidSignature( + this.directSignatures, this.keyPacket, enums.signature.key, { key: this.keyPacket }, null, config + ).catch(() => {}); if (directSignature) { const directSigKeyExpiry = helper.getKeyExpirationTime(this.keyPacket, directSignature); // We do not support the edge case where the direct signature expires, since it would invalidate the corresponding key expiration, @@ -428,6 +441,28 @@ class Key { } + /** + * For V4 keys, returns the self-signature of the primary user. + * For V5 keys, returns the latest valid direct-key self-signature. + * This self-signature is to be used to check the key expiration, + * algorithm preferences, and so on. + * @param {Date} [date] - Use the given date for verification instead of the current time + * @param {Object} [userID] - User ID to get instead of the primary user for V4 keys, if it exists + * @param {Object} [config] - Full configuration, defaults to openpgp.config + * @returns {Promise} The primary self-signature + * @async + */ + async getPrimarySelfSignature(date = new Date(), userID = {}, config = defaultConfig) { + const primaryKey = this.keyPacket; + if (primaryKey.version === 6) { + return helper.getLatestValidSignature( + this.directSignatures, primaryKey, enums.signature.key, { key: primaryKey }, date, config + ); + } + const { selfCertification } = await this.getPrimaryUser(date, userID, config); + return selfCertification; + } + /** * Returns primary user and most significant (latest valid) self signature * - if multiple primary users exist, returns the one with the latest self signature @@ -466,6 +501,7 @@ class Key { } } if (!users.length) { + // eslint-disable-next-line @typescript-eslint/no-throw-literal throw exception || new Error('Could not find primary user'); } await Promise.all(users.map(async function (a) { @@ -577,7 +613,9 @@ class Key { const revocationSignature = await helper.getLatestValidSignature(this.revocationSignatures, this.keyPacket, enums.signature.keyRevocation, dataToVerify, date, config); const packetlist = new PacketList(); packetlist.push(revocationSignature); - return armor(enums.armor.publicKey, packetlist.write(), null, null, 'This is a revocation certificate'); + // An ASCII-armored Transferable Public Key packet sequence of a v6 key MUST NOT contain a CRC24 footer. + const emitChecksum = this.keyPacket.version !== 6; + return armor(enums.armor.publicKey, packetlist.write(), null, null, 'This is a revocation certificate', emitChecksum, config); } /** diff --git a/src/key/private_key.js b/src/key/private_key.js index 384a94ec..e82fa118 100644 --- a/src/key/private_key.js +++ b/src/key/private_key.js @@ -64,7 +64,9 @@ class PrivateKey extends PublicKey { * @returns {ReadableStream} ASCII armor. */ armor(config = defaultConfig) { - return armor(enums.armor.privateKey, this.toPacketList().write(), undefined, undefined, undefined, config); + // An ASCII-armored Transferable Public Key packet sequence of a v6 key MUST NOT contain a CRC24 footer. + const emitChecksum = this.keyPacket.version !== 6; + return armor(enums.armor.privateKey, this.toPacketList().write(), undefined, undefined, undefined, emitChecksum, config); } /** @@ -75,28 +77,45 @@ class PrivateKey extends PublicKey { * @param {String} userID, optional * @param {Object} [config] - Full configuration, defaults to openpgp.config * @returns {Promise>} Array of decryption keys. + * @throws {Error} if no decryption key is found * @async */ async getDecryptionKeys(keyID, date = new Date(), userID = {}, config = defaultConfig) { const primaryKey = this.keyPacket; const keys = []; + let exception = null; for (let i = 0; i < this.subkeys.length; i++) { if (!keyID || this.subkeys[i].getKeyID().equals(keyID, true)) { + if (this.subkeys[i].keyPacket.isDummy()) { + exception = exception || new Error('Gnu-dummy key packets cannot be used for decryption'); + continue; + } + try { const dataToVerify = { key: primaryKey, bind: this.subkeys[i].keyPacket }; const bindingSignature = await helper.getLatestValidSignature(this.subkeys[i].bindingSignatures, primaryKey, enums.signature.subkeyBinding, dataToVerify, date, config); - if (helper.isValidDecryptionKeyPacket(bindingSignature, config)) { + if (helper.validateDecryptionKeyPacket(this.subkeys[i].keyPacket, bindingSignature, config)) { keys.push(this.subkeys[i]); } - } catch (e) {} + } catch (e) { + exception = e; + } } } // evaluate primary key - const primaryUser = await this.getPrimaryUser(date, userID, config); - if ((!keyID || primaryKey.getKeyID().equals(keyID, true)) && - helper.isValidDecryptionKeyPacket(primaryUser.selfCertification, config)) { - keys.push(this); + const selfCertification = await this.getPrimarySelfSignature(date, userID, config); + if ((!keyID || primaryKey.getKeyID().equals(keyID, true)) && helper.validateDecryptionKeyPacket(primaryKey, selfCertification, config)) { + if (primaryKey.isDummy()) { + exception = exception || new Error('Gnu-dummy key packets cannot be used for decryption'); + } else { + keys.push(this); + } + } + + if (keys.length === 0) { + // eslint-disable-next-line @typescript-eslint/no-throw-literal + throw exception || new Error('No decryption key packets found'); } return keys; @@ -187,7 +206,7 @@ class PrivateKey extends PublicKey { } const dataToSign = { key: this.keyPacket }; const key = this.clone(); - key.revocationSignatures.push(await helper.createSignaturePacket(dataToSign, null, this.keyPacket, { + key.revocationSignatures.push(await helper.createSignaturePacket(dataToSign, [], this.keyPacket, { signatureType: enums.signature.keyRevocation, reasonForRevocationFlag: enums.write(enums.reasonForRevocation, reasonForRevocationFlag), reasonForRevocationString @@ -198,8 +217,10 @@ class PrivateKey extends PublicKey { /** * Generates a new OpenPGP subkey, and returns a clone of the Key object with the new subkey added. - * Supports RSA and ECC keys. Defaults to the algorithm and bit size/curve of the primary key. DSA primary keys default to RSA subkeys. - * @param {ecc|rsa} options.type The subkey algorithm: ECC or RSA + * Supports RSA and ECC keys, as well as the newer Curve448 and Curve25519. + * Defaults to the algorithm and bit size/curve of the primary key. DSA primary keys default to RSA subkeys. + * @param {ecc|rsa|curve25519|curve448} options.type The subkey algorithm: ECC, RSA, Curve448 or Curve25519 (new format). + * Note: Curve448 and Curve25519 are not widely supported yet. * @param {String} options.curve (optional) Elliptic curve for ECC keys * @param {Integer} options.rsaBits (optional) Number of bits for RSA subkeys * @param {Number} options.keyExpirationTime (optional) Number of seconds from the key creation time after which the key expires @@ -225,11 +246,15 @@ class PrivateKey extends PublicKey { throw new Error('Key is not decrypted'); } const defaultOptions = secretKeyPacket.getAlgorithmInfo(); - defaultOptions.type = defaultOptions.curve ? 'ecc' : 'rsa'; // DSA keys default to RSA + defaultOptions.type = getDefaultSubkeyType(defaultOptions.algorithm); defaultOptions.rsaBits = defaultOptions.bits || 4096; - defaultOptions.curve = defaultOptions.curve || 'curve25519'; + defaultOptions.curve = defaultOptions.curve || 'curve25519Legacy'; options = helper.sanitizeKeyOptions(options, defaultOptions); - const keyPacket = await helper.generateSecretSubkey(options); + // Every subkey for a v4 primary key MUST be a v4 subkey. + // Every subkey for a v6 primary key MUST be a v6 subkey. + // For v5 keys, since we dropped generation support, a v4 subkey is added. + // The config is always overwritten since we cannot tell if the defaultConfig was changed by the user. + const keyPacket = await helper.generateSecretSubkey(options, { ...config, v6Keys: this.keyPacket.version === 6 }); helper.checkKeyRequirements(keyPacket, config); const bindingSignature = await helper.createBindingSignature(keyPacket, secretKeyPacket, options, config); const packetList = this.toPacketList(); @@ -238,4 +263,25 @@ class PrivateKey extends PublicKey { } } +function getDefaultSubkeyType(algoName) { + const algo = enums.write(enums.publicKey, algoName); + // NB: no encryption-only algos, since they cannot be in primary keys + switch (algo) { + case enums.publicKey.rsaEncrypt: + case enums.publicKey.rsaEncryptSign: + case enums.publicKey.rsaSign: + case enums.publicKey.dsa: + return 'rsa'; + case enums.publicKey.ecdsa: + case enums.publicKey.eddsaLegacy: + return 'ecc'; + case enums.publicKey.ed25519: + return 'curve25519'; + case enums.publicKey.ed448: + return 'curve448'; + default: + throw new Error('Unsupported algorithm'); + } +} + export default PrivateKey; diff --git a/src/key/public_key.js b/src/key/public_key.js index 66eac924..7996a32c 100644 --- a/src/key/public_key.js +++ b/src/key/public_key.js @@ -61,7 +61,9 @@ class PublicKey extends Key { * @returns {ReadableStream} ASCII armor. */ armor(config = defaultConfig) { - return armor(enums.armor.publicKey, this.toPacketList().write(), undefined, undefined, undefined, config); + // An ASCII-armored Transferable Public Key packet sequence of a v6 key MUST NOT contain a CRC24 footer. + const emitChecksum = this.keyPacket.version !== 6; + return armor(enums.armor.publicKey, this.toPacketList().write(), undefined, undefined, undefined, emitChecksum, config); } } diff --git a/src/key/subkey.js b/src/key/subkey.js index 57e34a0b..49af01b5 100644 --- a/src/key/subkey.js +++ b/src/key/subkey.js @@ -1,6 +1,5 @@ /** * @module key/Subkey - * @private */ import enums from '../enums'; @@ -186,7 +185,7 @@ class Subkey { ) { const dataToSign = { key: primaryKey, bind: this.keyPacket }; const subkey = new Subkey(this.keyPacket, this.mainKey); - subkey.revocationSignatures.push(await helper.createSignaturePacket(dataToSign, null, primaryKey, { + subkey.revocationSignatures.push(await helper.createSignaturePacket(dataToSign, [], primaryKey, { signatureType: enums.signature.subkeyRevocation, reasonForRevocationFlag: enums.write(enums.reasonForRevocation, reasonForRevocationFlag), reasonForRevocationString diff --git a/src/key/user.js b/src/key/user.js index c1e22eb0..b401a742 100644 --- a/src/key/user.js +++ b/src/key/user.js @@ -1,6 +1,5 @@ /** * @module key/User - * @private */ import enums from '../enums'; @@ -73,7 +72,7 @@ class User { throw new Error("The user's own key can only be used for self-certifications"); } const signingKey = await privateKey.getSigningKey(undefined, date, undefined, config); - return createSignaturePacket(dataToSign, privateKey, signingKey.keyPacket, { + return createSignaturePacket(dataToSign, [privateKey], signingKey.keyPacket, { // Most OpenPGP implementations use generic certification (0x10) signatureType: enums.signature.certGeneric, keyFlags: [enums.keyFlags.certifyKeys | enums.keyFlags.signData] @@ -261,7 +260,7 @@ class User { key: primaryKey }; const user = new User(dataToSign.userID || dataToSign.userAttribute, this.mainKey); - user.revocationSignatures.push(await createSignaturePacket(dataToSign, null, primaryKey, { + user.revocationSignatures.push(await createSignaturePacket(dataToSign, [], primaryKey, { signatureType: enums.signature.certRevocation, reasonForRevocationFlag: enums.write(enums.reasonForRevocation, reasonForRevocationFlag), reasonForRevocationString diff --git a/src/message.js b/src/message.js index 186fec42..d90efb58 100644 --- a/src/message.js +++ b/src/message.js @@ -17,13 +17,13 @@ import * as stream from '@openpgp/web-stream-tools'; import { armor, unarmor } from './encoding/armor'; -import KeyID from './type/keyid'; +import { Argon2OutOfMemoryError } from './type/s2k'; import defaultConfig from './config'; import crypto from './crypto'; import enums from './enums'; import util from './util'; import { Signature } from './signature'; -import { getPreferredHashAlgo, getPreferredAlgo, isAEADSupported, createSignaturePacket } from './key'; +import { getPreferredCipherSuite, createSignaturePacket } from './key'; import { PacketList, LiteralDataPacket, @@ -107,8 +107,6 @@ export class Message { * @async */ async decrypt(decryptionKeys, passwords, sessionKeys, date = new Date(), config = defaultConfig) { - const sessionKeyObjects = sessionKeys || await this.decryptSessionKeys(decryptionKeys, passwords, date, config); - const symEncryptedPacketlist = this.packets.filterByTag( enums.packet.symmetricallyEncryptedData, enums.packet.symEncryptedIntegrityProtectedData, @@ -120,14 +118,18 @@ export class Message { } const symEncryptedPacket = symEncryptedPacketlist[0]; + const expectedSymmetricAlgorithm = symEncryptedPacket.cipherAlgorithm; + + const sessionKeyObjects = sessionKeys || await this.decryptSessionKeys(decryptionKeys, passwords, expectedSymmetricAlgorithm, date, config); + let exception = null; const decryptedPromise = Promise.all(sessionKeyObjects.map(async ({ algorithm: algorithmName, data }) => { - if (!util.isUint8Array(data) || !util.isString(algorithmName)) { + if (!util.isUint8Array(data) || (!symEncryptedPacket.cipherAlgorithm && !util.isString(algorithmName))) { throw new Error('Invalid session key for decryption.'); } try { - const algo = enums.write(enums.symmetric, algorithmName); + const algo = symEncryptedPacket.cipherAlgorithm || enums.write(enums.symmetric, algorithmName); await symEncryptedPacket.decrypt(algo, data, config); } catch (e) { util.printDebugError(e); @@ -153,6 +155,7 @@ export class Message { * Decrypt encrypted session keys either with private keys or passwords. * @param {Array} [decryptionKeys] - Private keys with decrypted secret data * @param {Array} [passwords] - Passwords used to decrypt + * @param {enums.symmetric} [expectedSymmetricAlgorithm] - The symmetric algorithm the SEIPDv2 / AEAD packet is encrypted with (if applicable) * @param {Date} [date] - Use the given date for key verification, instead of current time * @param {Object} [config] - Full configuration, defaults to openpgp.config * @returns {Promise>} array of object with potential sessionKey, algorithm pairs * @async */ - async decryptSessionKeys(decryptionKeys, passwords, date = new Date(), config = defaultConfig) { + async decryptSessionKeys(decryptionKeys, passwords, expectedSymmetricAlgorithm, date = new Date(), config = defaultConfig) { let decryptedSessionKeyPackets = []; let exception; @@ -183,6 +186,9 @@ export class Message { decryptedSessionKeyPackets.push(skeskPacket); } catch (err) { util.printDebugError(err); + if (err instanceof Argon2OutOfMemoryError) { + exception = err; + } } })); })); @@ -193,6 +199,15 @@ export class Message { } await Promise.all(pkeskPackets.map(async function(pkeskPacket) { await Promise.all(decryptionKeys.map(async function(decryptionKey) { + let decryptionKeyPackets; + try { + // do not check key expiration to allow decryption of old messages + decryptionKeyPackets = (await decryptionKey.getDecryptionKeys(pkeskPacket.publicKeyID, null, undefined, config)).map(key => key.keyPacket); + } catch (err) { + exception = err; + return; + } + let algos = [ enums.symmetric.aes256, // Old OpenPGP.js default fallback enums.symmetric.aes128, // RFC4880bis fallback @@ -200,18 +215,13 @@ export class Message { enums.symmetric.cast5 // Golang OpenPGP fallback ]; try { - const primaryUser = await decryptionKey.getPrimaryUser(date, undefined, config); // TODO: Pass userID from somewhere. - if (primaryUser.selfCertification.preferredSymmetricAlgorithms) { - algos = algos.concat(primaryUser.selfCertification.preferredSymmetricAlgorithms); + const selfCertification = await decryptionKey.getPrimarySelfSignature(date, undefined, config); // TODO: Pass userID from somewhere. + if (selfCertification.preferredSymmetricAlgorithms) { + algos = algos.concat(selfCertification.preferredSymmetricAlgorithms); } } catch (e) {} - // do not check key expiration to allow decryption of old messages - const decryptionKeyPackets = (await decryptionKey.getDecryptionKeys(pkeskPacket.publicKeyID, null, undefined, config)).map(key => key.keyPacket); await Promise.all(decryptionKeyPackets.map(async function(decryptionKeyPacket) { - if (!decryptionKeyPacket || decryptionKeyPacket.isDummy()) { - return; - } if (!decryptionKeyPacket.isDecrypted()) { throw new Error('Decryption key is not decrypted.'); } @@ -236,7 +246,11 @@ export class Message { // NB: as a result, if the data is encrypted with a non-suported cipher, decryption will always fail. const serialisedPKESK = pkeskPacket.write(); // make copies to be able to decrypt the PKESK packet multiple times - await Promise.all(Array.from(config.constantTimePKCS1DecryptionSupportedSymmetricAlgorithms).map(async sessionKeyAlgorithm => { + await Promise.all(( + expectedSymmetricAlgorithm ? + [expectedSymmetricAlgorithm] : + Array.from(config.constantTimePKCS1DecryptionSupportedSymmetricAlgorithms) + ).map(async sessionKeyAlgorithm => { const pkeskPacketCopy = new PublicKeyEncryptedSessionKeyPacket(); pkeskPacketCopy.read(serialisedPKESK); const randomSessionKey = { @@ -256,7 +270,8 @@ export class Message { } else { try { await pkeskPacket.decrypt(decryptionKeyPacket); - if (!algos.includes(enums.write(enums.symmetric, pkeskPacket.sessionKeyAlgorithm))) { + const symmetricAlgorithm = expectedSymmetricAlgorithm || pkeskPacket.sessionKeyAlgorithm; + if (symmetricAlgorithm && !algos.includes(enums.write(enums.symmetric, symmetricAlgorithm))) { throw new Error('A non-preferred symmetric algorithm was used.'); } decryptedSessionKeyPackets.push(pkeskPacket); @@ -290,7 +305,7 @@ export class Message { return decryptedSessionKeyPackets.map(packet => ({ data: packet.sessionKey, - algorithm: enums.read(enums.symmetric, packet.sessionKeyAlgorithm) + algorithm: packet.sessionKeyAlgorithm && enums.read(enums.symmetric, packet.sessionKeyAlgorithm) })); } throw exception || new Error('Session key decryption failed.'); @@ -339,23 +354,22 @@ export class Message { * @async */ static async generateSessionKey(encryptionKeys = [], date = new Date(), userIDs = [], config = defaultConfig) { - const algo = await getPreferredAlgo('symmetric', encryptionKeys, date, userIDs, config); - const algorithmName = enums.read(enums.symmetric, algo); - const aeadAlgorithmName = config.aeadProtect && await isAEADSupported(encryptionKeys, date, userIDs, config) ? - enums.read(enums.aead, await getPreferredAlgo('aead', encryptionKeys, date, userIDs, config)) : - undefined; + const { symmetricAlgo, aeadAlgo } = await getPreferredCipherSuite(encryptionKeys, date, userIDs, config); + const symmetricAlgoName = enums.read(enums.symmetric, symmetricAlgo); + const aeadAlgoName = aeadAlgo ? enums.read(enums.aead, aeadAlgo) : undefined; await Promise.all(encryptionKeys.map(key => key.getEncryptionKey() .catch(() => null) // ignore key strength requirements .then(maybeKey => { - if (maybeKey && (maybeKey.keyPacket.algorithm === enums.publicKey.x25519) && !util.isAES(algo)) { - throw new Error('Could not generate a session key compatible with the given `encryptionKeys`: X22519 keys can only be used to encrypt AES session keys; change `config.preferredSymmetricAlgorithm` accordingly.'); + if (maybeKey && (maybeKey.keyPacket.algorithm === enums.publicKey.x25519 || maybeKey.keyPacket.algorithm === enums.publicKey.x448) && + !aeadAlgoName && !util.isAES(symmetricAlgo)) { // if AEAD is defined, then PKESK v6 are used, and the algo info is encrypted + throw new Error('Could not generate a session key compatible with the given `encryptionKeys`: X22519 and X448 keys can only be used to encrypt AES session keys; change `config.preferredSymmetricAlgorithm` accordingly.'); } }) )); - const sessionKeyData = crypto.generateSessionKey(algo); - return { data: sessionKeyData, algorithm: algorithmName, aeadAlgorithm: aeadAlgorithmName }; + const sessionKeyData = crypto.generateSessionKey(symmetricAlgo); + return { data: sessionKeyData, algorithm: symmetricAlgoName, aeadAlgorithm: aeadAlgoName }; } /** @@ -388,13 +402,10 @@ export class Message { const msg = await Message.encryptSessionKey(sessionKeyData, algorithmName, aeadAlgorithmName, encryptionKeys, passwords, wildcard, encryptionKeyIDs, date, userIDs, config); - let symEncryptedPacket; - if (aeadAlgorithmName) { - symEncryptedPacket = new AEADEncryptedDataPacket(); - symEncryptedPacket.aeadAlgorithm = enums.write(enums.aead, aeadAlgorithmName); - } else { - symEncryptedPacket = new SymEncryptedIntegrityProtectedDataPacket(); - } + const symEncryptedPacket = SymEncryptedIntegrityProtectedDataPacket.fromObject({ + version: aeadAlgorithmName ? 2 : 1, + aeadAlgorithm: aeadAlgorithmName ? enums.write(enums.aead, aeadAlgorithmName) : null + }); symEncryptedPacket.packets = this.packets; const algorithm = enums.write(enums.symmetric, algorithmName); @@ -422,17 +433,21 @@ export class Message { */ static async encryptSessionKey(sessionKey, algorithmName, aeadAlgorithmName, encryptionKeys, passwords, wildcard = false, encryptionKeyIDs = [], date = new Date(), userIDs = [], config = defaultConfig) { const packetlist = new PacketList(); - const algorithm = enums.write(enums.symmetric, algorithmName); + const symmetricAlgorithm = enums.write(enums.symmetric, algorithmName); const aeadAlgorithm = aeadAlgorithmName && enums.write(enums.aead, aeadAlgorithmName); if (encryptionKeys) { const results = await Promise.all(encryptionKeys.map(async function(primaryKey, i) { const encryptionKey = await primaryKey.getEncryptionKey(encryptionKeyIDs[i], date, userIDs, config); - const pkESKeyPacket = new PublicKeyEncryptedSessionKeyPacket(); - pkESKeyPacket.publicKeyID = wildcard ? KeyID.wildcard() : encryptionKey.getKeyID(); - pkESKeyPacket.publicKeyAlgorithm = encryptionKey.keyPacket.algorithm; - pkESKeyPacket.sessionKey = sessionKey; - pkESKeyPacket.sessionKeyAlgorithm = algorithm; + + const pkESKeyPacket = PublicKeyEncryptedSessionKeyPacket.fromObject({ + version: aeadAlgorithm ? 6 : 3, + encryptionKeyPacket: encryptionKey.keyPacket, + anonymousRecipient: wildcard, + sessionKey, + sessionKeyAlgorithm: symmetricAlgorithm + }); + await pkESKeyPacket.encrypt(encryptionKey.keyPacket); delete pkESKeyPacket.sessionKey; // delete plaintext session key after encryption return pkESKeyPacket; @@ -471,7 +486,7 @@ export class Message { return symEncryptedSessionKeyPacket; }; - const results = await Promise.all(passwords.map(pwd => encryptPassword(sessionKey, algorithm, aeadAlgorithm, pwd))); + const results = await Promise.all(passwords.map(pwd => encryptPassword(sessionKey, symmetricAlgorithm, aeadAlgorithm, pwd))); packetlist.push(...results); } @@ -481,16 +496,18 @@ export class Message { /** * Sign the message (the literal data packet of the message) * @param {Array} signingKeys - private keys with decrypted secret key data for signing + * @param {Array} recipientKeys - recipient keys to get the signing preferences from * @param {Signature} [signature] - Any existing detached signature to add to the message * @param {Array} [signingKeyIDs] - Array of key IDs to use for signing. Each signingKeyIDs[i] corresponds to signingKeys[i] * @param {Date} [date] - Override the creation time of the signature - * @param {Array} [userIDs] - User IDs to sign with, e.g. [{ name:'Steve Sender', email:'steve@openpgp.org' }] + * @param {Array} [signingUserIDs] - User IDs to sign with, e.g. [{ name:'Steve Sender', email:'steve@openpgp.org' }] + * @param {Array} [recipientUserIDs] - User IDs associated with `recipientKeys` to get the signing preferences from * @param {Array} [notations] - Notation Data to add to the signatures, e.g. [{ name: 'test@example.org', value: new TextEncoder().encode('test'), humanReadable: true, critical: false }] * @param {Object} [config] - Full configuration, defaults to openpgp.config * @returns {Promise} New message with signed content. * @async */ - async sign(signingKeys = [], signature = null, signingKeyIDs = [], date = new Date(), userIDs = [], notations = [], config = defaultConfig) { + async sign(signingKeys = [], recipientKeys = [], signature = null, signingKeyIDs = [], date = new Date(), signingUserIDs = [], recipientUserIDs = [], notations = [], config = defaultConfig) { const packetlist = new PacketList(); const literalDataPacket = this.packets.findPacket(enums.packet.literalData); @@ -498,49 +515,14 @@ export class Message { throw new Error('No literal data packet to sign.'); } - let i; - let existingSigPacketlist; - // If data packet was created from Uint8Array, use binary, otherwise use text - const signatureType = literalDataPacket.text === null ? - enums.signature.binary : enums.signature.text; - - if (signature) { - existingSigPacketlist = signature.packets.filterByTag(enums.packet.signature); - for (i = existingSigPacketlist.length - 1; i >= 0; i--) { - const signaturePacket = existingSigPacketlist[i]; - const onePassSig = new OnePassSignaturePacket(); - onePassSig.signatureType = signaturePacket.signatureType; - onePassSig.hashAlgorithm = signaturePacket.hashAlgorithm; - onePassSig.publicKeyAlgorithm = signaturePacket.publicKeyAlgorithm; - onePassSig.issuerKeyID = signaturePacket.issuerKeyID; - if (!signingKeys.length && i === 0) { - onePassSig.flags = 1; - } - packetlist.push(onePassSig); - } - } - - await Promise.all(Array.from(signingKeys).reverse().map(async function (primaryKey, i) { - if (!primaryKey.isPrivate()) { - throw new Error('Need private key for signing'); - } - const signingKeyID = signingKeyIDs[signingKeys.length - 1 - i]; - const signingKey = await primaryKey.getSigningKey(signingKeyID, date, userIDs, config); - const onePassSig = new OnePassSignaturePacket(); - onePassSig.signatureType = signatureType; - onePassSig.hashAlgorithm = await getPreferredHashAlgo(primaryKey, signingKey.keyPacket, date, userIDs, config); - onePassSig.publicKeyAlgorithm = signingKey.keyPacket.algorithm; - onePassSig.issuerKeyID = signingKey.getKeyID(); - if (i === signingKeys.length - 1) { - onePassSig.flags = 1; - } - return onePassSig; - })).then(onePassSignatureList => { - onePassSignatureList.forEach(onePassSig => packetlist.push(onePassSig)); - }); + const signaturePackets = await createSignaturePackets(literalDataPacket, signingKeys, recipientKeys, signature, signingKeyIDs, date, signingUserIDs, recipientUserIDs, notations, false, config); // this returns the existing signature packets as well + const onePassSignaturePackets = signaturePackets.map( + (signaturePacket, i) => OnePassSignaturePacket.fromSignaturePacket(signaturePacket, i === 0)) + .reverse(); // innermost OPS refers to the first signature packet + packetlist.push(...onePassSignaturePackets); packetlist.push(literalDataPacket); - packetlist.push(...(await createSignaturePackets(literalDataPacket, signingKeys, signature, signingKeyIDs, date, userIDs, notations, false, config))); + packetlist.push(...signaturePackets); return new Message(packetlist); } @@ -569,21 +551,23 @@ export class Message { /** * Create a detached signature for the message (the literal data packet of the message) * @param {Array} signingKeys - private keys with decrypted secret key data for signing + * @param {Array} recipientKeys - recipient keys to get the signing preferences from * @param {Signature} [signature] - Any existing detached signature * @param {Array} [signingKeyIDs] - Array of key IDs to use for signing. Each signingKeyIDs[i] corresponds to signingKeys[i] * @param {Date} [date] - Override the creation time of the signature - * @param {Array} [userIDs] - User IDs to sign with, e.g. [{ name:'Steve Sender', email:'steve@openpgp.org' }] + * @param {Array} [signingUserIDs] - User IDs to sign with, e.g. [{ name:'Steve Sender', email:'steve@openpgp.org' }] + * @param {Array} [recipientUserIDs] - User IDs associated with `recipientKeys` to get the signing preferences from * @param {Array} [notations] - Notation Data to add to the signatures, e.g. [{ name: 'test@example.org', value: new TextEncoder().encode('test'), humanReadable: true, critical: false }] * @param {Object} [config] - Full configuration, defaults to openpgp.config * @returns {Promise} New detached signature of message content. * @async */ - async signDetached(signingKeys = [], signature = null, signingKeyIDs = [], date = new Date(), userIDs = [], notations = [], config = defaultConfig) { + async signDetached(signingKeys = [], recipientKeys = [], signature = null, signingKeyIDs = [], recipientKeyIDs = [], date = new Date(), userIDs = [], notations = [], config = defaultConfig) { const literalDataPacket = this.packets.findPacket(enums.packet.literalData); if (!literalDataPacket) { throw new Error('No literal data packet to sign.'); } - return new Signature(await createSignaturePackets(literalDataPacket, signingKeys, signature, signingKeyIDs, date, userIDs, notations, true, config)); + return new Signature(await createSignaturePackets(literalDataPacket, signingKeys, recipientKeys, signature, signingKeyIDs, recipientKeyIDs, date, userIDs, notations, true, config)); } /** @@ -704,7 +688,13 @@ export class Message { * @returns {ReadableStream} ASCII armor. */ armor(config = defaultConfig) { - return armor(enums.armor.message, this.write(), null, null, null, config); + const trailingPacket = this.packets[this.packets.length - 1]; + // An ASCII-armored Encrypted Message packet sequence that ends in an v2 SEIPD packet MUST NOT contain a CRC24 footer. + // An ASCII-armored sequence of Signature packets that only includes v6 Signature packets MUST NOT contain a CRC24 footer. + const emitChecksum = trailingPacket.constructor.tag === SymEncryptedIntegrityProtectedDataPacket.tag ? + trailingPacket.version !== 2 : + this.packets.some(packet => packet.constructor.tag === SignaturePacket.tag && packet.version !== 6); + return armor(enums.armor.message, this.write(), null, null, null, emitChecksum, config); } } @@ -712,18 +702,21 @@ export class Message { * Create signature packets for the message * @param {LiteralDataPacket} literalDataPacket - the literal data packet to sign * @param {Array} [signingKeys] - private keys with decrypted secret key data for signing + * @param {Array} [recipientKeys] - recipient keys to get the signing preferences from * @param {Signature} [signature] - Any existing detached signature to append * @param {Array} [signingKeyIDs] - Array of key IDs to use for signing. Each signingKeyIDs[i] corresponds to signingKeys[i] * @param {Date} [date] - Override the creationtime of the signature - * @param {Array} [userIDs] - User IDs to sign with, e.g. [{ name:'Steve Sender', email:'steve@openpgp.org' }] + * @param {Array} [signingUserIDs] - User IDs to sign to, e.g. [{ name:'Steve Sender', email:'steve@openpgp.org' }] + * @param {Array} [recipientUserIDs] - User IDs associated with `recipientKeys` to get the signing preferences from * @param {Array} [notations] - Notation Data to add to the signatures, e.g. [{ name: 'test@example.org', value: new TextEncoder().encode('test'), humanReadable: true, critical: false }] + * @param {Array} [signatureSalts] - A list of signature salts matching the number of signingKeys that should be used for v6 signatures * @param {Boolean} [detached] - Whether to create detached signature packets * @param {Object} [config] - Full configuration, defaults to openpgp.config * @returns {Promise} List of signature packets. * @async * @private */ -export async function createSignaturePackets(literalDataPacket, signingKeys, signature = null, signingKeyIDs = [], date = new Date(), userIDs = [], notations = [], detached = false, config = defaultConfig) { +export async function createSignaturePackets(literalDataPacket, signingKeys, recipientKeys = [], signature = null, signingKeyIDs = [], date = new Date(), signingUserIDs = [], recipientUserIDs = [], notations = [], detached = false, config = defaultConfig) { const packetlist = new PacketList(); // If data packet was created from Uint8Array, use binary, otherwise use text @@ -731,12 +724,12 @@ export async function createSignaturePackets(literalDataPacket, signingKeys, sig enums.signature.binary : enums.signature.text; await Promise.all(signingKeys.map(async (primaryKey, i) => { - const userID = userIDs[i]; + const signingUserID = signingUserIDs[i]; if (!primaryKey.isPrivate()) { throw new Error('Need private key for signing'); } - const signingKey = await primaryKey.getSigningKey(signingKeyIDs[i], date, userID, config); - return createSignaturePacket(literalDataPacket, primaryKey, signingKey.keyPacket, { signatureType }, date, userID, notations, detached, config); + const signingKey = await primaryKey.getSigningKey(signingKeyIDs[i], date, signingUserID, config); + return createSignaturePacket(literalDataPacket, recipientKeys.length ? recipientKeys : [primaryKey], signingKey.keyPacket, { signatureType }, date, recipientUserIDs, notations, detached, config); })).then(signatureList => { packetlist.push(...signatureList); }); @@ -877,10 +870,6 @@ export async function readMessage({ armoredMessage, binaryMessage, config, ...re const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`); const streamType = util.isStream(input); - if (streamType) { - await stream.loadStreamsPonyfill(); - input = stream.toStream(input); - } if (armoredMessage) { const { type, data } = await unarmor(input, config); if (type !== enums.armor.message) { @@ -907,7 +896,7 @@ export async function readMessage({ armoredMessage, binaryMessage, config, ...re * @static */ export async function createMessage({ text, binary, filename, date = new Date(), format = text !== undefined ? 'utf8' : 'binary', ...rest }) { - let input = text !== undefined ? text : binary; + const input = text !== undefined ? text : binary; if (input === undefined) { throw new Error('createMessage: must pass options object containing `text` or `binary`'); } @@ -920,10 +909,6 @@ export async function createMessage({ text, binary, filename, date = new Date(), const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`); const streamType = util.isStream(input); - if (streamType) { - await stream.loadStreamsPonyfill(); - input = stream.toStream(input); - } const literalDataPacket = new LiteralDataPacket(date); if (text !== undefined) { literalDataPacket.setText(input, enums.write(enums.literal, format)); diff --git a/src/openpgp.js b/src/openpgp.js index e109f6dd..22ac9077 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -18,7 +18,7 @@ import * as stream from '@openpgp/web-stream-tools'; import { Message } from './message'; import { CleartextMessage } from './cleartext'; -import { generate, reformat, getPreferredAlgo } from './key'; +import { generate, reformat, getPreferredCompressionAlgo } from './key'; import defaultConfig from './config'; import util from './util'; import { checkKeyRequirements } from './key/helper'; @@ -32,15 +32,17 @@ import { checkKeyRequirements } from './key/helper'; /** - * Generates a new OpenPGP key pair. Supports RSA and ECC keys. By default, primary and subkeys will be of same type. + * Generates a new OpenPGP key pair. Supports RSA and ECC keys, as well as the newer Curve448 and Curve25519 keys. + * By default, primary and subkeys will be of same type. * The generated primary key will have signing capabilities. By default, one subkey with encryption capabilities is also generated. * @param {Object} options * @param {Object|Array} options.userIDs - User IDs as objects: `{ name: 'Jo Doe', email: 'info@jo.com' }` - * @param {'ecc'|'rsa'} [options.type='ecc'] - The primary key algorithm type: ECC (default) or RSA + * @param {'ecc'|'rsa'|'curve448'|'curve25519'} [options.type='ecc'] - The primary key algorithm type: ECC (default for v4 keys), RSA, Curve448 or Curve25519 (new format, default for v6 keys). + * Note: Curve448 and Curve25519 (new format) are not widely supported yet. * @param {String} [options.passphrase=(not protected)] - The passphrase used to encrypt the generated private key. If omitted or empty, the key won't be encrypted. * @param {Number} [options.rsaBits=4096] - Number of bits for RSA keys - * @param {String} [options.curve='curve25519'] - Elliptic curve for ECC keys: - * curve25519 (default), p256, p384, p521, secp256k1, + * @param {String} [options.curve='curve25519Legacy'] - Elliptic curve for ECC keys: + * curve25519Legacy (default), nistP256, nistP384, nistP521, secp256k1, * brainpoolP256r1, brainpoolP384r1, or brainpoolP512r1 * @param {Date} [options.date=current date] - Override the creation date of the key and the key signatures * @param {Number} [options.keyExpirationTime=0 (never expires)] - Number of seconds from the key creation time after which the key expires @@ -53,13 +55,20 @@ import { checkKeyRequirements } from './key/helper'; * @async * @static */ -export async function generateKey({ userIDs = [], passphrase, type = 'ecc', rsaBits = 4096, curve = 'curve25519', keyExpirationTime = 0, date = new Date(), subkeys = [{}], format = 'armored', config, ...rest }) { +export async function generateKey({ userIDs = [], passphrase, type, curve, rsaBits = 4096, keyExpirationTime = 0, date = new Date(), subkeys = [{}], format = 'armored', config, ...rest }) { config = { ...defaultConfig, ...config }; checkConfig(config); + if (!type && !curve) { + type = config.v6Keys ? 'curve25519' : 'ecc'; // default to new curve25519 for v6 keys (legacy curve25519 cannot be used with them) + curve = 'curve25519Legacy'; // unused with type != 'ecc' + } else { + type = type || 'ecc'; + curve = curve || 'curve25519Legacy'; + } userIDs = toArray(userIDs); const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`); - if (userIDs.length === 0) { - throw new Error('UserIDs are required for key generation'); + if (userIDs.length === 0 && !config.v6Keys) { + throw new Error('UserIDs are required for V4 keys'); } if (type === 'rsa' && rsaBits < config.minRSABits) { throw new Error(`rsaBits should be at least ${config.minRSABits}, got: ${rsaBits}`); @@ -102,8 +111,8 @@ export async function reformatKey({ privateKey, userIDs = [], passphrase, keyExp userIDs = toArray(userIDs); const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`); - if (userIDs.length === 0) { - throw new Error('UserIDs are required for key reformat'); + if (userIDs.length === 0 && privateKey.keyPacket.version !== 6) { + throw new Error('UserIDs are required for V4 keys'); } const options = { privateKey, userIDs, passphrase, keyExpirationTime, date }; @@ -278,13 +287,13 @@ export async function encrypt({ message, encryptionKeys, signingKeys, passwords, if (!signingKeys) { signingKeys = []; } - const streaming = message.fromStream; + try { if (signingKeys.length || signature) { // sign the message only if signing keys or signature is specified - message = await message.sign(signingKeys, signature, signingKeyIDs, date, signingUserIDs, signatureNotations, config); + message = await message.sign(signingKeys, encryptionKeys, signature, signingKeyIDs, date, signingUserIDs, encryptionKeyIDs, signatureNotations, config); } message = message.compress( - await getPreferredAlgo('compression', encryptionKeys, date, encryptionUserIDs, config), + await getPreferredCompressionAlgo(encryptionKeys, date, encryptionUserIDs, config), config ); message = await message.encrypt(encryptionKeys, passwords, sessionKey, wildcard, encryptionKeyIDs, date, encryptionUserIDs, config); @@ -292,7 +301,7 @@ export async function encrypt({ message, encryptionKeys, signingKeys, passwords, // serialize data const armor = format === 'armored'; const data = armor ? message.armor(config) : message.write(); - return convertStream(data, streaming, armor ? 'utf8' : 'binary'); + return await convertStream(data); } catch (err) { throw util.wrapError('Error encrypting message', err); } @@ -363,7 +372,7 @@ export async function decrypt({ message, decryptionKeys, passwords, sessionKeys, }) ]); } - result.data = await convertStream(result.data, message.fromStream, format); + result.data = await convertStream(result.data); return result; } catch (err) { throw util.wrapError('Error decrypting message', err); @@ -383,21 +392,23 @@ export async function decrypt({ message, decryptionKeys, passwords, sessionKeys, * @param {Object} options * @param {CleartextMessage|Message} options.message - (cleartext) message to be signed * @param {PrivateKey|PrivateKey[]} options.signingKeys - Array of keys or single key with decrypted secret key data to sign cleartext + * @param {Key|Key[]} options.recipientKeys - Array of keys or single to get the signing preferences from * @param {'armored'|'binary'|'object'} [options.format='armored'] - Format of the returned message * @param {Boolean} [options.detached=false] - If the return value should contain a detached signature * @param {KeyID|KeyID[]} [options.signingKeyIDs=latest-created valid signing (sub)keys] - Array of key IDs to use for signing. Each signingKeyIDs[i] corresponds to signingKeys[i] * @param {Date} [options.date=current date] - Override the creation date of the signature * @param {Object|Object[]} [options.signingUserIDs=primary user IDs] - Array of user IDs to sign with, one per key in `signingKeys`, e.g. `[{ name: 'Steve Sender', email: 'steve@openpgp.org' }]` + * @param {Object|Object[]} [options.recipientUserIDs=primary user IDs] - Array of user IDs to get the signing preferences from, one per key in `recipientKeys` * @param {Object|Object[]} [options.signatureNotations=[]] - Array of notations to add to the signatures, e.g. `[{ name: 'test@example.org', value: new TextEncoder().encode('test'), humanReadable: true, critical: false }]` * @param {Object} [options.config] - Custom configuration settings to overwrite those in [config]{@link module:config} * @returns {Promise>} Signed message (string if `armor` was true, the default; Uint8Array if `armor` was false). * @async * @static */ -export async function sign({ message, signingKeys, format = 'armored', detached = false, signingKeyIDs = [], date = new Date(), signingUserIDs = [], signatureNotations = [], config, ...rest }) { +export async function sign({ message, signingKeys, recipientKeys = [], format = 'armored', detached = false, signingKeyIDs = [], date = new Date(), signingUserIDs = [], recipientUserIDs = [], signatureNotations = [], config, ...rest }) { config = { ...defaultConfig, ...config }; checkConfig(config); checkCleartextOrMessage(message); checkOutputMessageFormat(format); - signingKeys = toArray(signingKeys); signingKeyIDs = toArray(signingKeyIDs); signingUserIDs = toArray(signingUserIDs); signatureNotations = toArray(signatureNotations); + signingKeys = toArray(signingKeys); signingKeyIDs = toArray(signingKeyIDs); signingUserIDs = toArray(signingUserIDs); recipientKeys = toArray(recipientKeys); recipientUserIDs = toArray(recipientUserIDs); signatureNotations = toArray(signatureNotations); if (rest.privateKeys) throw new Error('The `privateKeys` option has been removed from openpgp.sign, pass `signingKeys` instead'); if (rest.armor !== undefined) throw new Error('The `armor` option has been removed from openpgp.sign, pass `format` instead.'); @@ -413,9 +424,9 @@ export async function sign({ message, signingKeys, format = 'armored', detached try { let signature; if (detached) { - signature = await message.signDetached(signingKeys, undefined, signingKeyIDs, date, signingUserIDs, signatureNotations, config); + signature = await message.signDetached(signingKeys, recipientKeys, undefined, signingKeyIDs, date, signingUserIDs, recipientUserIDs, signatureNotations, config); } else { - signature = await message.sign(signingKeys, undefined, signingKeyIDs, date, signingUserIDs, signatureNotations, config); + signature = await message.sign(signingKeys, recipientKeys, undefined, signingKeyIDs, date, signingUserIDs, recipientUserIDs, signatureNotations, config); } if (format === 'object') return signature; @@ -429,7 +440,7 @@ export async function sign({ message, signingKeys, format = 'armored', detached ]); }); } - return convertStream(signature, message.fromStream, armor ? 'utf8' : 'binary'); + return await convertStream(signature); } catch (err) { throw util.wrapError('Error signing message', err); } @@ -492,7 +503,7 @@ export async function verify({ message, verificationKeys, expectSigned = false, }) ]); } - result.data = await convertStream(result.data, message.fromStream, format); + result.data = await convertStream(result.data); return result; } catch (err) { throw util.wrapError('Error verifying signed message', err); @@ -591,7 +602,7 @@ export async function decryptSessionKeys({ message, decryptionKeys, passwords, d const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`); try { - const sessionKeys = await message.decryptSessionKeys(decryptionKeys, passwords, date, config); + const sessionKeys = await message.decryptSessionKeys(decryptionKeys, passwords, undefined, date, config); return sessionKeys; } catch (err) { throw util.wrapError('Error decrypting session keys', err); @@ -663,25 +674,15 @@ function toArray(param) { /** * Convert data to or from Stream * @param {Object} data - the data to convert - * @param {'web'|'ponyfill'|'node'|false} streaming - Whether to return a ReadableStream, and of what type - * @param {'utf8'|'binary'} [encoding] - How to return data in Node Readable streams * @returns {Promise} The data in the respective format. * @async * @private */ -async function convertStream(data, streaming, encoding = 'utf8') { +async function convertStream(data) { const streamType = util.isStream(data); if (streamType === 'array') { return stream.readToEnd(data); } - if (streaming === 'node') { - data = stream.webToNode(data); - if (encoding !== 'binary') data.setEncoding(encoding); - return data; - } - if (streaming === 'web' && streamType === 'ponyfill') { - return stream.toNativeReadable(data); - } return data; } diff --git a/src/packet/aead_encrypted_data.js b/src/packet/aead_encrypted_data.js index 3e24323b..60bdc3a7 100644 --- a/src/packet/aead_encrypted_data.js +++ b/src/packet/aead_encrypted_data.js @@ -21,6 +21,7 @@ import enums from '../enums'; import util from '../util'; import defaultConfig from '../config'; import { UnsupportedError } from './packet'; +import { runAEAD } from './sym_encrypted_integrity_protected_data'; import LiteralDataPacket from './literal_data'; import CompressedDataPacket from './compressed_data'; @@ -101,7 +102,7 @@ class AEADEncryptedDataPacket { */ async decrypt(sessionKeyAlgorithm, key, config = defaultConfig) { this.packets = await PacketList.fromBinary( - await this.crypt('decrypt', key, stream.clone(this.encrypted)), + await runAEAD(this, 'decrypt', key, stream.clone(this.encrypted)), allowedPackets, config ); @@ -122,86 +123,7 @@ class AEADEncryptedDataPacket { this.iv = crypto.random.getRandomBytes(ivLength); // generate new random IV this.chunkSizeByte = config.aeadChunkSizeByte; const data = this.packets.write(); - this.encrypted = await this.crypt('encrypt', key, data); - } - - /** - * En/decrypt the payload. - * @param {encrypt|decrypt} fn - Whether to encrypt or decrypt - * @param {Uint8Array} key - The session key used to en/decrypt the payload - * @param {Uint8Array | ReadableStream} data - The data to en/decrypt - * @returns {Promise>} - * @async - */ - async crypt(fn, key, data) { - const mode = crypto.getAEADMode(this.aeadAlgorithm); - const modeInstance = await mode(this.cipherAlgorithm, key); - const tagLengthIfDecrypting = fn === 'decrypt' ? mode.tagLength : 0; - const tagLengthIfEncrypting = fn === 'encrypt' ? mode.tagLength : 0; - const chunkSize = 2 ** (this.chunkSizeByte + 6) + tagLengthIfDecrypting; // ((uint64_t)1 << (c + 6)) - const adataBuffer = new ArrayBuffer(21); - const adataArray = new Uint8Array(adataBuffer, 0, 13); - const adataTagArray = new Uint8Array(adataBuffer); - const adataView = new DataView(adataBuffer); - const chunkIndexArray = new Uint8Array(adataBuffer, 5, 8); - adataArray.set([0xC0 | AEADEncryptedDataPacket.tag, this.version, this.cipherAlgorithm, this.aeadAlgorithm, this.chunkSizeByte], 0); - let chunkIndex = 0; - let latestPromise = Promise.resolve(); - let cryptedBytes = 0; - let queuedBytes = 0; - const iv = this.iv; - return stream.transformPair(data, async (readable, writable) => { - if (util.isStream(readable) !== 'array') { - const buffer = new stream.TransformStream({}, { - highWaterMark: util.getHardwareConcurrency() * 2 ** (this.chunkSizeByte + 6), - size: array => array.length - }); - stream.pipe(buffer.readable, writable); - writable = buffer.writable; - } - const reader = stream.getReader(readable); - const writer = stream.getWriter(writable); - try { - while (true) { - let chunk = await reader.readBytes(chunkSize + tagLengthIfDecrypting) || new Uint8Array(); - const finalChunk = chunk.subarray(chunk.length - tagLengthIfDecrypting); - chunk = chunk.subarray(0, chunk.length - tagLengthIfDecrypting); - let cryptedPromise; - let done; - if (!chunkIndex || chunk.length) { - reader.unshift(finalChunk); - cryptedPromise = modeInstance[fn](chunk, mode.getNonce(iv, chunkIndexArray), adataArray); - queuedBytes += chunk.length - tagLengthIfDecrypting + tagLengthIfEncrypting; - } else { - // After the last chunk, we either encrypt a final, empty - // data chunk to get the final authentication tag or - // validate that final authentication tag. - adataView.setInt32(13 + 4, cryptedBytes); // Should be setInt64(13, ...) - cryptedPromise = modeInstance[fn](finalChunk, mode.getNonce(iv, chunkIndexArray), adataTagArray); - queuedBytes += tagLengthIfEncrypting; - done = true; - } - cryptedBytes += chunk.length - tagLengthIfDecrypting; - // eslint-disable-next-line no-loop-func - latestPromise = latestPromise.then(() => cryptedPromise).then(async crypted => { - await writer.ready; - await writer.write(crypted); - queuedBytes -= crypted.length; - }).catch(err => writer.abort(err)); - if (done || queuedBytes > writer.desiredSize) { - await latestPromise; // Respect backpressure - } - if (!done) { - adataView.setInt32(5 + 4, ++chunkIndex); // Should be setInt64(5, ...) - } else { - await writer.close(); - break; - } - } - } catch (e) { - await writer.abort(e); - } - }); + this.encrypted = await runAEAD(this, 'encrypt', key, data); } } diff --git a/src/packet/all_packets.js b/src/packet/all_packets.js index 6362c3af..1c3fd978 100644 --- a/src/packet/all_packets.js +++ b/src/packet/all_packets.js @@ -21,3 +21,4 @@ export { default as UserIDPacket } from './userid'; export { default as SecretSubkeyPacket } from './secret_subkey'; export { default as SignaturePacket } from './signature'; export { default as TrustPacket } from './trust'; +export { default as PaddingPacket } from './padding'; diff --git a/src/packet/compressed_data.js b/src/packet/compressed_data.js index e924a46c..03de3529 100644 --- a/src/packet/compressed_data.js +++ b/src/packet/compressed_data.js @@ -15,10 +15,7 @@ // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -import { Deflate } from '@openpgp/pako/lib/deflate'; -import { Inflate } from '@openpgp/pako/lib/inflate'; -import { Z_SYNC_FLUSH, Z_FINISH } from '@openpgp/pako/lib/zlib/constants'; -import { decode as BunzipDecode } from '@openpgp/seek-bzip'; +import { Inflate, Deflate, Zlib, Unzlib } from 'fflate'; import * as stream from '@openpgp/web-stream-tools'; import enums from '../enums'; import util from '../util'; @@ -69,11 +66,6 @@ class CompressedDataPacket { * @type {Uint8Array | ReadableStream} */ this.compressed = null; - - /** - * zip/zlib compression level, between 1 and 9 - */ - this.deflateLevel = config.deflateLevel; } /** @@ -115,12 +107,12 @@ class CompressedDataPacket { */ async decompress(config = defaultConfig) { const compressionName = enums.read(enums.compression, this.algorithm); - const decompressionFn = decompress_fns[compressionName]; + const decompressionFn = decompress_fns[compressionName]; // bzip decompression is async if (!decompressionFn) { throw new Error(`${compressionName} decompression not supported`); } - this.packets = await PacketList.fromBinary(decompressionFn(this.compressed), allowedPackets, config); + this.packets = await PacketList.fromBinary(await decompressionFn(this.compressed), allowedPackets, config); } /** @@ -133,7 +125,7 @@ class CompressedDataPacket { throw new Error(`${compressionName} compression not supported`); } - this.compressed = compressionFn(this.packets.write(), this.deflateLevel); + this.compressed = compressionFn(this.packets.write()); } } @@ -145,69 +137,98 @@ export default CompressedDataPacket; // // ////////////////////////// - -const nodeZlib = util.getNodeZlib(); - -function uncompressed(data) { - return data; -} - -function node_zlib(func, create, options = {}) { - return function (data) { +/** + * Zlib processor relying on Compression Stream API if available, or falling back to fflate otherwise. + * @param {function(): CompressionStream|function(): DecompressionStream} compressionStreamInstantiator + * @param {FunctionConstructor} ZlibStreamedConstructor - fflate constructor + * @returns {ReadableStream} compressed or decompressed data + */ +function zlib(compressionStreamInstantiator, ZlibStreamedConstructor) { + return data => { if (!util.isStream(data) || stream.isArrayStream(data)) { - return stream.fromAsync(() => stream.readToEnd(data).then(data => { + return stream.fromAsync(() => stream.readToEnd(data).then(inputData => { return new Promise((resolve, reject) => { - func(data, options, (err, result) => { - if (err) return reject(err); - resolve(result); - }); + const zlibStream = new ZlibStreamedConstructor(); + zlibStream.ondata = processedData => { + resolve(processedData); + }; + try { + zlibStream.push(inputData, true); // only one chunk to push + } catch (err) { + reject(err); + } }); })); } - return stream.nodeToWeb(stream.webToNode(data).pipe(create(options))); - }; -} -function pako_zlib(constructor, options = {}) { - return function(data) { - const obj = new constructor(options); - return stream.transform(data, value => { - if (value.length) { - obj.push(value, Z_SYNC_FLUSH); - return obj.result; + // Use Compression Streams API if available (see https://developer.mozilla.org/en-US/docs/Web/API/Compression_Streams_API) + if (compressionStreamInstantiator) { + try { + const compressorOrDecompressor = compressionStreamInstantiator(); + return data.pipeThrough(compressorOrDecompressor); + } catch (err) { + // If format is unsupported in Compression/DecompressionStream, then a TypeError in thrown, and we fallback to fflate. + if (err.name !== 'TypeError') { + throw err; + } } - }, () => { - if (constructor === Deflate) { - obj.push([], Z_FINISH); - return obj.result; + } + + // JS fallback + const inputReader = data.getReader(); + const zlibStream = new ZlibStreamedConstructor(); + + return new ReadableStream({ + async start(controller) { + zlibStream.ondata = async (value, isLast) => { + controller.enqueue(value); + if (isLast) { + controller.close(); + } + }; + + while (true) { + const { done, value } = await inputReader.read(); + if (done) { + zlibStream.push(new Uint8Array(), true); + return; + } else if (value.length) { + zlibStream.push(value); + } + } } }); }; } -function bzip2(func) { - return function(data) { - return stream.fromAsync(async () => func(await stream.readToEnd(data))); +function bzip2Decompress() { + return async function(data) { + const { decode: bunzipDecode } = await import('@openpgp/seek-bzip'); + return stream.fromAsync(async () => bunzipDecode(await stream.readToEnd(data))); }; } -const compress_fns = nodeZlib ? { - zip: /*#__PURE__*/ (compressed, level) => node_zlib(nodeZlib.deflateRaw, nodeZlib.createDeflateRaw, { level })(compressed), - zlib: /*#__PURE__*/ (compressed, level) => node_zlib(nodeZlib.deflate, nodeZlib.createDeflate, { level })(compressed) -} : { - zip: /*#__PURE__*/ (compressed, level) => pako_zlib(Deflate, { raw: true, level })(compressed), - zlib: /*#__PURE__*/ (compressed, level) => pako_zlib(Deflate, { level })(compressed) +/** + * Get Compression Stream API instatiators if the constructors are implemented. + * NB: the return instatiator functions will throw when called if the provided `compressionFormat` is not supported + * (supported formats cannot be determined in advance). + * @param {'deflate-raw'|'deflate'|'gzip'|string} compressionFormat + * @returns {{ compressor: function(): CompressionStream | false, decompressor: function(): DecompressionStream | false }} + */ +const getCompressionStreamInstantiators = compressionFormat => ({ + compressor: typeof CompressionStream !== 'undefined' && (() => new CompressionStream(compressionFormat)), + decompressor: typeof DecompressionStream !== 'undefined' && (() => new DecompressionStream(compressionFormat)) +}); + +const compress_fns = { + zip: /*#__PURE__*/ zlib(getCompressionStreamInstantiators('deflate-raw').compressor, Deflate), + zlib: /*#__PURE__*/ zlib(getCompressionStreamInstantiators('deflate').compressor, Zlib) }; -const decompress_fns = nodeZlib ? { - uncompressed: uncompressed, - zip: /*#__PURE__*/ node_zlib(nodeZlib.inflateRaw, nodeZlib.createInflateRaw), - zlib: /*#__PURE__*/ node_zlib(nodeZlib.inflate, nodeZlib.createInflate), - bzip2: /*#__PURE__*/ bzip2(BunzipDecode) -} : { - uncompressed: uncompressed, - zip: /*#__PURE__*/ pako_zlib(Inflate, { raw: true }), - zlib: /*#__PURE__*/ pako_zlib(Inflate), - bzip2: /*#__PURE__*/ bzip2(BunzipDecode) +const decompress_fns = { + uncompressed: data => data, + zip: /*#__PURE__*/ zlib(getCompressionStreamInstantiators('deflate-raw').decompressor, Inflate), + zlib: /*#__PURE__*/ zlib(getCompressionStreamInstantiators('deflate').decompressor, Unzlib), + bzip2: /*#__PURE__*/ bzip2Decompress() // NB: async due to dynamic lib import }; diff --git a/src/packet/one_pass_signature.js b/src/packet/one_pass_signature.js index 54e8697d..5676c8e6 100644 --- a/src/packet/one_pass_signature.js +++ b/src/packet/one_pass_signature.js @@ -22,8 +22,6 @@ import enums from '../enums'; import util from '../util'; import { UnsupportedError } from './packet'; -const VERSION = 3; - /** * Implementation of the One-Pass Signature Packets (Tag 4) * @@ -39,8 +37,22 @@ class OnePassSignaturePacket { return enums.packet.onePassSignature; } + static fromSignaturePacket(signaturePacket, isLast) { + const onePassSig = new OnePassSignaturePacket(); + onePassSig.version = signaturePacket.version === 6 ? 6 : 3; + onePassSig.signatureType = signaturePacket.signatureType; + onePassSig.hashAlgorithm = signaturePacket.hashAlgorithm; + onePassSig.publicKeyAlgorithm = signaturePacket.publicKeyAlgorithm; + onePassSig.issuerKeyID = signaturePacket.issuerKeyID; + onePassSig.salt = signaturePacket.salt; // v6 only + onePassSig.issuerFingerprint = signaturePacket.issuerFingerprint; // v6 only + + onePassSig.flags = isLast ? 1 : 0; + return onePassSig; + } + constructor() { - /** A one-octet version number. The current version is 3. */ + /** A one-octet version number. The current versions are 3 and 6. */ this.version = null; /** * A one-octet signature type. @@ -62,8 +74,12 @@ class OnePassSignaturePacket { * @type {enums.publicKey} */ this.publicKeyAlgorithm = null; - /** An eight-octet number holding the Key ID of the signing key. */ + /** Only for v6, a variable-length field containing the salt. */ + this.salt = null; + /** Only for v3 packets, an eight-octet number holding the Key ID of the signing key. */ this.issuerKeyID = null; + /** Only for v6 packets, 32 octets of the fingerprint of the signing key. */ + this.issuerFingerprint = null; /** * A one-octet number holding a flag showing whether the signature is nested. * A zero value indicates that the next packet is another One-Pass Signature packet @@ -79,9 +95,9 @@ class OnePassSignaturePacket { */ read(bytes) { let mypos = 0; - // A one-octet version number. The current version is 3. + // A one-octet version number. The current versions are 3 or 6. this.version = bytes[mypos++]; - if (this.version !== VERSION) { + if (this.version !== 3 && this.version !== 6) { throw new UnsupportedError(`Version ${this.version} of the one-pass signature packet is unsupported.`); } @@ -95,10 +111,30 @@ class OnePassSignaturePacket { // A one-octet number describing the public-key algorithm used. this.publicKeyAlgorithm = bytes[mypos++]; - // An eight-octet number holding the Key ID of the signing key. - this.issuerKeyID = new KeyID(); - this.issuerKeyID.read(bytes.subarray(mypos, mypos + 8)); - mypos += 8; + if (this.version === 6) { + // Only for v6 signatures, a variable-length field containing: + + // A one-octet salt size. The value MUST match the value defined + // for the hash algorithm as specified in Table 23 (Hash algorithm registry). + // To allow parsing unknown hash algos, we only check the expected salt length when verifying. + const saltLength = bytes[mypos++]; + + // The salt; a random value value of the specified size. + this.salt = bytes.subarray(mypos, mypos + saltLength); + mypos += saltLength; + + // Only for v6 packets, 32 octets of the fingerprint of the signing key. + this.issuerFingerprint = bytes.subarray(mypos, mypos + 32); + mypos += 32; + this.issuerKeyID = new KeyID(); + // For v6 the Key ID is the high-order 64 bits of the fingerprint. + this.issuerKeyID.read(this.issuerFingerprint); + } else { + // Only for v3 packets, an eight-octet number holding the Key ID of the signing key. + this.issuerKeyID = new KeyID(); + this.issuerKeyID.read(bytes.subarray(mypos, mypos + 8)); + mypos += 8; + } // A one-octet number holding a flag showing whether the signature // is nested. A zero value indicates that the next packet is @@ -113,11 +149,23 @@ class OnePassSignaturePacket { * @returns {Uint8Array} A Uint8Array representation of a one-pass signature packet. */ write() { - const start = new Uint8Array([VERSION, this.signatureType, this.hashAlgorithm, this.publicKeyAlgorithm]); - - const end = new Uint8Array([this.flags]); - - return util.concatUint8Array([start, this.issuerKeyID.write(), end]); + const arr = [new Uint8Array([ + this.version, + this.signatureType, + this.hashAlgorithm, + this.publicKeyAlgorithm + ])]; + if (this.version === 6) { + arr.push( + new Uint8Array([this.salt.length]), + this.salt, + this.issuerFingerprint + ); + } else { + arr.push(this.issuerKeyID.write()); + } + arr.push(new Uint8Array([this.flags])); + return util.concatUint8Array(arr); } calculateTrailer(...args) { @@ -133,7 +181,11 @@ class OnePassSignaturePacket { correspondingSig.signatureType !== this.signatureType || correspondingSig.hashAlgorithm !== this.hashAlgorithm || correspondingSig.publicKeyAlgorithm !== this.publicKeyAlgorithm || - !correspondingSig.issuerKeyID.equals(this.issuerKeyID) + !correspondingSig.issuerKeyID.equals(this.issuerKeyID) || + (this.version === 3 && correspondingSig.version === 6) || + (this.version === 6 && correspondingSig.version !== 6) || + (this.version === 6 && !util.equalsUint8Array(correspondingSig.issuerFingerprint, this.issuerFingerprint)) || + (this.version === 6 && !util.equalsUint8Array(correspondingSig.salt, this.salt)) ) { throw new Error('Corresponding signature packet does not match one-pass signature packet'); } diff --git a/src/packet/packet.js b/src/packet/packet.js index d18b6c28..6eea1209 100644 --- a/src/packet/packet.js +++ b/src/packet/packet.js @@ -18,7 +18,6 @@ /** * @fileoverview Functions for reading and writing packets * @module packet/packet - * @private */ import * as stream from '@openpgp/web-stream-tools'; @@ -153,7 +152,7 @@ export async function readPackets(input, callback) { writer = stream.getWriter(arrayStream); packet = arrayStream; } else { - const transform = new stream.TransformStream(); + const transform = new TransformStream(); writer = stream.getWriter(transform.writable); packet = transform.readable; } @@ -309,6 +308,19 @@ export class UnsupportedError extends Error { } } +// unknown packet types are handled differently depending on the packet criticality +export class UnknownPacketError extends UnsupportedError { + constructor(...params) { + super(...params); + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, UnsupportedError); + } + + this.name = 'UnknownPacketError'; + } +} + export class UnparseablePacket { constructor(tag, rawContent) { this.tag = tag; diff --git a/src/packet/packetlist.js b/src/packet/packetlist.js index b0fea7f4..2c8e2a08 100644 --- a/src/packet/packetlist.js +++ b/src/packet/packetlist.js @@ -4,7 +4,8 @@ import { writeTag, writeHeader, writePartialLength, writeSimpleLength, UnparseablePacket, - UnsupportedError + UnsupportedError, + UnknownPacketError } from './packet'; import util from '../util'; import enums from '../enums'; @@ -25,7 +26,7 @@ export function newPacketFromTag(tag, allowedPackets) { try { packetType = enums.read(enums.packet, tag); } catch (e) { - throw new UnsupportedError(`Unknown packet type with tag: ${tag}`); + throw new UnknownPacketError(`Unknown packet type with tag: ${tag}`); } throw new Error(`Packet not allowed in this context: ${packetType}`); } @@ -74,10 +75,11 @@ class PacketList extends Array { await writer.ready; const done = await readPackets(readable, async parsed => { try { - if (parsed.tag === enums.packet.marker || parsed.tag === enums.packet.trust) { + if (parsed.tag === enums.packet.marker || parsed.tag === enums.packet.trust || parsed.tag === enums.packet.padding) { // According to the spec, these packet types should be ignored and not cause parsing errors, even if not esplicitly allowed: // - Marker packets MUST be ignored when received: https://github.com/openpgpjs/openpgpjs/issues/1145 // - Trust packets SHOULD be ignored outside of keyrings (unsupported): https://datatracker.ietf.org/doc/html/rfc4880#section-5.10 + // - [Padding Packets] MUST be ignored when received: https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh#name-padding-packet-tag-21 return; } const packet = newPacketFromTag(parsed.tag, allowedPackets); @@ -86,6 +88,17 @@ class PacketList extends Array { await packet.read(parsed.packet, config); await writer.write(packet); } catch (e) { + // If an implementation encounters a critical packet where the packet type is unknown in a packet sequence, + // it MUST reject the whole packet sequence. On the other hand, an unknown non-critical packet MUST be ignored. + // Packet Tags from 0 to 39 are critical. Packet Tags from 40 to 63 are non-critical. + if (e instanceof UnknownPacketError) { + if (parsed.tag <= 39) { + await writer.abort(e); + } else { + return; + } + } + const throwUnsupportedError = !config.ignoreUnsupportedPackets && e instanceof UnsupportedError; const throwMalformedError = !config.ignoreMalformedPackets && !(e instanceof UnsupportedError); if (throwUnsupportedError || throwMalformedError || supportsStreaming(parsed.tag)) { diff --git a/src/packet/padding.js b/src/packet/padding.js new file mode 100644 index 00000000..8baec745 --- /dev/null +++ b/src/packet/padding.js @@ -0,0 +1,63 @@ +// OpenPGP.js - An OpenPGP implementation in javascript +// Copyright (C) 2022 Proton AG +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import crypto from '../crypto'; +import enums from '../enums'; + +/** + * Implementation of the Padding Packet + * + * {@link https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh#name-padding-packet-tag-21}: + * Padding Packet + */ +class PaddingPacket { + static get tag() { + return enums.packet.padding; + } + + constructor() { + this.padding = null; + } + + /** + * Read a padding packet + * @param {Uint8Array | ReadableStream} bytes + */ + read(bytes) { // eslint-disable-line @typescript-eslint/no-unused-vars + // Padding packets are ignored, so this function is never called. + } + + /** + * Write the padding packet + * @returns {Uint8Array} The padding packet. + */ + write() { + return this.padding; + } + + /** + * Create random padding. + * @param {Number} length - The length of padding to be generated. + * @throws {Error} if padding generation was not successful + * @async + */ + async createPadding(length) { + this.padding = await crypto.random.getRandomBytes(length); + } +} + +export default PaddingPacket; diff --git a/src/packet/public_key.js b/src/packet/public_key.js index 15e90857..c3b13071 100644 --- a/src/packet/public_key.js +++ b/src/packet/public_key.js @@ -47,7 +47,7 @@ class PublicKeyPacket { * Packet version * @type {Integer} */ - this.version = config.v5Keys ? 5 : 4; + this.version = config.v6Keys ? 6 : 4; /** * Key creation date. * @type {Date} @@ -104,12 +104,15 @@ class PublicKeyPacket { * @returns {Object} This object with attributes set by the parser * @async */ - async read(bytes) { + async read(bytes, config = defaultConfig) { let pos = 0; - // A one-octet version number (3, 4 or 5). + // A one-octet version number (4, 5 or 6). this.version = bytes[pos++]; + if (this.version === 5 && !config.enableParsingV5Entities) { + throw new UnsupportedError('Support for parsing v5 entities is disabled; turn on `config.enableParsingV5Entities` if needed'); + } - if (this.version === 4 || this.version === 5) { + if (this.version === 4 || this.version === 5 || this.version === 6) { // - A four-octet number denoting the time that the key was created. this.created = util.readDate(bytes.subarray(pos, pos + 4)); pos += 4; @@ -117,13 +120,24 @@ class PublicKeyPacket { // - A one-octet number denoting the public-key algorithm of this key. this.algorithm = bytes[pos++]; - if (this.version === 5) { + if (this.version >= 5) { // - A four-octet scalar octet count for the following key material. pos += 4; } // - A series of values comprising the key material. const { read, publicParams } = crypto.parsePublicKeyParams(this.algorithm, bytes.subarray(pos)); + // The deprecated OIDs for Ed25519Legacy and Curve25519Legacy are used in legacy version 4 keys and signatures. + // Implementations MUST NOT accept or generate v6 key material using the deprecated OIDs. + if ( + this.version === 6 && + publicParams.oid && ( + publicParams.oid.getName() === enums.curve.curve25519Legacy || + publicParams.oid.getName() === enums.curve.ed25519Legacy + ) + ) { + throw new Error('Legacy curve25519 cannot be used with v6 keys'); + } this.publicParams = publicParams; pos += read; @@ -147,7 +161,7 @@ class PublicKeyPacket { arr.push(new Uint8Array([this.algorithm])); const params = crypto.serializeParams(this.algorithm, this.publicParams); - if (this.version === 5) { + if (this.version >= 5) { // A four-octet scalar octet count for the following key material arr.push(util.writeNumber(params.length, 4)); } @@ -163,10 +177,9 @@ class PublicKeyPacket { writeForHash(version) { const bytes = this.writePublicKey(); - if (version === 5) { - return util.concatUint8Array([new Uint8Array([0x9A]), util.writeNumber(bytes.length, 4), bytes]); - } - return util.concatUint8Array([new Uint8Array([0x99]), util.writeNumber(bytes.length, 2), bytes]); + const versionOctet = 0x95 + version; + const lengthOctets = version >= 5 ? 4 : 2; + return util.concatUint8Array([new Uint8Array([versionOctet]), util.writeNumber(bytes.length, lengthOctets), bytes]); } /** @@ -201,7 +214,7 @@ class PublicKeyPacket { await this.computeFingerprint(); this.keyID = new KeyID(); - if (this.version === 5) { + if (this.version >= 5) { this.keyID.read(this.fingerprint.subarray(0, 8)); } else if (this.version === 4) { this.keyID.read(this.fingerprint.subarray(12, 20)); @@ -216,7 +229,7 @@ class PublicKeyPacket { async computeFingerprint() { const toHash = this.writeForHash(this.version); - if (this.version === 5) { + if (this.version >= 5) { this.fingerprint = await crypto.hash.sha256(toHash); } else if (this.version === 4) { this.fingerprint = await crypto.hash.sha1(toHash); diff --git a/src/packet/public_key_encrypted_session_key.js b/src/packet/public_key_encrypted_session_key.js index 231c62c7..eaa5dacd 100644 --- a/src/packet/public_key_encrypted_session_key.js +++ b/src/packet/public_key_encrypted_session_key.js @@ -21,8 +21,6 @@ import enums from '../enums'; import util from '../util'; import { UnsupportedError } from './packet'; -const VERSION = 3; - /** * Public-Key Encrypted Session Key Packets (Tag 1) * @@ -45,9 +43,16 @@ class PublicKeyEncryptedSessionKeyPacket { } constructor() { - this.version = 3; + this.version = null; + // For version 3, but also used internally by v6 in e.g. `getEncryptionKeyIDs()` this.publicKeyID = new KeyID(); + + // For version 6: + this.publicKeyVersion = null; + this.publicKeyFingerprint = null; + + // For all versions: this.publicKeyAlgorithm = null; this.sessionKey = null; @@ -61,22 +66,74 @@ class PublicKeyEncryptedSessionKeyPacket { this.encrypted = {}; } + static fromObject({ + version, encryptionKeyPacket, anonymousRecipient, sessionKey, sessionKeyAlgorithm + }) { + const pkesk = new PublicKeyEncryptedSessionKeyPacket(); + + if (version !== 3 && version !== 6) { + throw new Error('Unsupported PKESK version'); + } + + pkesk.version = version; + + if (version === 6) { + pkesk.publicKeyVersion = anonymousRecipient ? null : encryptionKeyPacket.version; + pkesk.publicKeyFingerprint = anonymousRecipient ? null : encryptionKeyPacket.getFingerprintBytes(); + } + + pkesk.publicKeyID = anonymousRecipient ? KeyID.wildcard() : encryptionKeyPacket.getKeyID(); + pkesk.publicKeyAlgorithm = encryptionKeyPacket.algorithm; + pkesk.sessionKey = sessionKey; + pkesk.sessionKeyAlgorithm = sessionKeyAlgorithm; + + return pkesk; + } + /** * Parsing function for a publickey encrypted session key packet (tag 1). * * @param {Uint8Array} bytes - Payload of a tag 1 packet */ read(bytes) { - let i = 0; - this.version = bytes[i++]; - if (this.version !== VERSION) { + let offset = 0; + this.version = bytes[offset++]; + if (this.version !== 3 && this.version !== 6) { throw new UnsupportedError(`Version ${this.version} of the PKESK packet is unsupported.`); } - i += this.publicKeyID.read(bytes.subarray(i)); - this.publicKeyAlgorithm = bytes[i++]; - this.encrypted = crypto.parseEncSessionKeyParams(this.publicKeyAlgorithm, bytes.subarray(i), this.version); - if (this.publicKeyAlgorithm === enums.publicKey.x25519) { - this.sessionKeyAlgorithm = enums.write(enums.symmetric, this.encrypted.C.algorithm); + if (this.version === 6) { + // A one-octet size of the following two fields: + // - A one octet key version number. + // - The fingerprint of the public key or subkey to which the session key is encrypted. + // The size may also be zero. + const versionAndFingerprintLength = bytes[offset++]; + if (versionAndFingerprintLength) { + this.publicKeyVersion = bytes[offset++]; + const fingerprintLength = versionAndFingerprintLength - 1; + this.publicKeyFingerprint = bytes.subarray(offset, offset + fingerprintLength); offset += fingerprintLength; + if (this.publicKeyVersion >= 5) { + // For v5/6 the Key ID is the high-order 64 bits of the fingerprint. + this.publicKeyID.read(this.publicKeyFingerprint); + } else { + // For v4 The Key ID is the low-order 64 bits of the fingerprint. + this.publicKeyID.read(this.publicKeyFingerprint.subarray(-8)); + } + } else { + // The size may also be zero, and the key version and + // fingerprint omitted for an "anonymous recipient" + this.publicKeyID = KeyID.wildcard(); + } + } else { + offset += this.publicKeyID.read(bytes.subarray(offset, offset + 8)); + } + this.publicKeyAlgorithm = bytes[offset++]; + this.encrypted = crypto.parseEncSessionKeyParams(this.publicKeyAlgorithm, bytes.subarray(offset)); + if (this.publicKeyAlgorithm === enums.publicKey.x25519 || this.publicKeyAlgorithm === enums.publicKey.x448) { + if (this.version === 3) { + this.sessionKeyAlgorithm = enums.write(enums.symmetric, this.encrypted.C.algorithm); + } else if (this.encrypted.C.algorithm !== null) { + throw new Error('Unexpected cleartext symmetric algorithm'); + } } } @@ -87,11 +144,27 @@ class PublicKeyEncryptedSessionKeyPacket { */ write() { const arr = [ - new Uint8Array([this.version]), - this.publicKeyID.write(), + new Uint8Array([this.version]) + ]; + + if (this.version === 6) { + if (this.publicKeyFingerprint !== null) { + arr.push(new Uint8Array([ + this.publicKeyFingerprint.length + 1, + this.publicKeyVersion] + )); + arr.push(this.publicKeyFingerprint); + } else { + arr.push(new Uint8Array([0])); + } + } else { + arr.push(this.publicKeyID.write()); + } + + arr.push( new Uint8Array([this.publicKeyAlgorithm]), crypto.serializeParams(this.publicKeyAlgorithm, this.encrypted) - ]; + ); return util.concatUint8Array(arr); } @@ -104,9 +177,13 @@ class PublicKeyEncryptedSessionKeyPacket { */ async encrypt(key) { const algo = enums.write(enums.publicKey, this.publicKeyAlgorithm); - const encoded = encodeSessionKey(this.version, algo, this.sessionKeyAlgorithm, this.sessionKey); + // No symmetric encryption algorithm identifier is passed to the public-key algorithm for a + // v6 PKESK packet, as it is included in the v2 SEIPD packet. + const sessionKeyAlgorithm = this.version === 3 ? this.sessionKeyAlgorithm : null; + const fingerprint = key.version === 5 ? key.getFingerprintBytes().subarray(0, 20) : key.getFingerprintBytes(); + const encoded = encodeSessionKey(this.version, algo, sessionKeyAlgorithm, this.sessionKey); this.encrypted = await crypto.publicKeyEncrypt( - algo, this.sessionKeyAlgorithm, key.publicParams, encoded, key.getFingerprintBytes()); + algo, sessionKeyAlgorithm, key.publicParams, encoded, fingerprint); } /** @@ -126,13 +203,19 @@ class PublicKeyEncryptedSessionKeyPacket { const randomPayload = randomSessionKey ? encodeSessionKey(this.version, this.publicKeyAlgorithm, randomSessionKey.sessionKeyAlgorithm, randomSessionKey.sessionKey) : null; - const decryptedData = await crypto.publicKeyDecrypt(this.publicKeyAlgorithm, key.publicParams, key.privateParams, this.encrypted, key.getFingerprintBytes(), randomPayload); + const fingerprint = key.version === 5 ? key.getFingerprintBytes().subarray(0, 20) : key.getFingerprintBytes(); + const decryptedData = await crypto.publicKeyDecrypt(this.publicKeyAlgorithm, key.publicParams, key.privateParams, this.encrypted, fingerprint, randomPayload); const { sessionKey, sessionKeyAlgorithm } = decodeSessionKey(this.version, this.publicKeyAlgorithm, decryptedData, randomSessionKey); - // v3 Montgomery curves have cleartext cipher algo - if (this.publicKeyAlgorithm !== enums.publicKey.x25519) { - this.sessionKeyAlgorithm = sessionKeyAlgorithm; + if (this.version === 3) { + // v3 Montgomery curves have cleartext cipher algo + const hasEncryptedAlgo = this.publicKeyAlgorithm !== enums.publicKey.x25519 && this.publicKeyAlgorithm !== enums.publicKey.x448; + this.sessionKeyAlgorithm = hasEncryptedAlgo ? sessionKeyAlgorithm : this.sessionKeyAlgorithm; + + if (sessionKey.length !== crypto.getCipherParams(this.sessionKeyAlgorithm).keySize) { + throw new Error('Unexpected session key size'); + } } this.sessionKey = sessionKey; } @@ -146,15 +229,15 @@ function encodeSessionKey(version, keyAlgo, cipherAlgo, sessionKeyData) { case enums.publicKey.rsaEncrypt: case enums.publicKey.rsaEncryptSign: case enums.publicKey.elgamal: - case enums.publicKey.ecdh: { + case enums.publicKey.ecdh: // add checksum return util.concatUint8Array([ - new Uint8Array([cipherAlgo]), + new Uint8Array(version === 6 ? [] : [cipherAlgo]), sessionKeyData, util.writeChecksum(sessionKeyData.subarray(sessionKeyData.length % 8)) ]); - } case enums.publicKey.x25519: + case enums.publicKey.x448: return sessionKeyData; default: throw new Error('Unsupported public key algorithm'); @@ -173,7 +256,9 @@ function decodeSessionKey(version, keyAlgo, decryptedData, randomSessionKey) { const checksum = decryptedData.subarray(decryptedData.length - 2); const computedChecksum = util.writeChecksum(result.subarray(result.length % 8)); const isValidChecksum = computedChecksum[0] === checksum[0] & computedChecksum[1] === checksum[1]; - const decryptedSessionKey = { sessionKeyAlgorithm: result[0], sessionKey: result.subarray(1) }; + const decryptedSessionKey = version === 6 ? + { sessionKeyAlgorithm: null, sessionKey: result } : + { sessionKeyAlgorithm: result[0], sessionKey: result.subarray(1) }; if (randomSessionKey) { // We must not leak info about the validity of the decrypted checksum or cipher algo. // The decrypted session key must be of the same algo and size as the random session key, otherwise we discard it and use the random data. @@ -182,14 +267,15 @@ function decodeSessionKey(version, keyAlgo, decryptedData, randomSessionKey) { decryptedSessionKey.sessionKey.length === randomSessionKey.sessionKey.length; return { sessionKey: util.selectUint8Array(isValidPayload, decryptedSessionKey.sessionKey, randomSessionKey.sessionKey), - sessionKeyAlgorithm: util.selectUint8( + sessionKeyAlgorithm: version === 6 ? null : util.selectUint8( isValidPayload, decryptedSessionKey.sessionKeyAlgorithm, randomSessionKey.sessionKeyAlgorithm ) }; } else { - const isValidPayload = isValidChecksum && enums.read(enums.symmetric, decryptedSessionKey.sessionKeyAlgorithm); + const isValidPayload = isValidChecksum && ( + version === 6 || enums.read(enums.symmetric, decryptedSessionKey.sessionKeyAlgorithm)); if (isValidPayload) { return decryptedSessionKey; } else { @@ -198,7 +284,9 @@ function decodeSessionKey(version, keyAlgo, decryptedData, randomSessionKey) { } } case enums.publicKey.x25519: + case enums.publicKey.x448: return { + sessionKeyAlgorithm: null, sessionKey: decryptedData }; default: diff --git a/src/packet/public_subkey.js b/src/packet/public_subkey.js index 731fc222..a0b664e8 100644 --- a/src/packet/public_subkey.js +++ b/src/packet/public_subkey.js @@ -35,7 +35,7 @@ class PublicSubkeyPacket extends PublicKeyPacket { * @param {Date} [date] - Creation date * @param {Object} [config] - Full configuration, defaults to openpgp.config */ - // eslint-disable-next-line no-useless-constructor + // eslint-disable-next-line @typescript-eslint/no-useless-constructor constructor(date, config) { super(date, config); } diff --git a/src/packet/secret_key.js b/src/packet/secret_key.js index a0df643a..ddddae5f 100644 --- a/src/packet/secret_key.js +++ b/src/packet/secret_key.js @@ -16,12 +16,13 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import PublicKeyPacket from './public_key'; -import S2K from '../type/s2k'; +import { newS2KFromConfig, newS2KFromType } from '../type/s2k'; import crypto from '../crypto'; import enums from '../enums'; import util from '../util'; import defaultConfig from '../config'; -import { UnsupportedError } from './packet'; +import { UnsupportedError, writeTag } from './packet'; +import computeHKDF from '../crypto/hkdf'; /** * A Secret-Key packet contains all the information that is found in a @@ -68,11 +69,27 @@ class SecretKeyPacket extends PublicKeyPacket { * @type {enums.aead} */ this.aead = null; + /** + * Whether the key is encrypted using the legacy AEAD format proposal from RFC4880bis + * (i.e. it was encrypted with the flag `config.aeadProtect` in OpenPGP.js v5 or older). + * This value is only relevant to know how to decrypt the key: + * if AEAD is enabled, a v4 key is always re-encrypted using the standard AEAD mechanism. + * @type {Boolean} + * @private + */ + this.isLegacyAEAD = null; /** * Decrypted private parameters, referenced by name * @type {Object} */ this.privateParams = null; + /** + * `true` for keys whose integrity is already confirmed, based on + * the AEAD encryption mechanism + * @type {Boolean} + * @private + */ + this.usedModernAEAD = null; } // 5.5.3. Secret-Key Packet Formats @@ -83,9 +100,9 @@ class SecretKeyPacket extends PublicKeyPacket { * @param {Uint8Array} bytes - Input string to read the packet from * @async */ - async read(bytes) { + async read(bytes, config = defaultConfig) { // - A Public-Key or Public-Subkey packet, as described above. - let i = await this.readPublicKey(bytes); + let i = await this.readPublicKey(bytes, config); const startOfSecretKeyData = i; // - One octet indicating string-to-key usage conventions. Zero @@ -100,6 +117,14 @@ class SecretKeyPacket extends PublicKeyPacket { i++; } + // - Only for a version 6 packet where the secret key material is + // encrypted (that is, where the previous octet is not zero), a one- + // octet scalar octet count of the cumulative length of all the + // following optional string-to-key parameter fields. + if (this.version === 6 && this.s2kUsage) { + i++; + } + try { // - [Optional] If string-to-key usage octet was 255, 254, or 253, a // one-octet symmetric encryption algorithm. @@ -112,10 +137,17 @@ class SecretKeyPacket extends PublicKeyPacket { this.aead = bytes[i++]; } + // - [Optional] Only for a version 6 packet, and if string-to-key usage + // octet was 255, 254, or 253, an one-octet count of the following field. + if (this.version === 6) { + i++; + } + // - [Optional] If string-to-key usage octet was 255, 254, or 253, a // string-to-key specifier. The length of the string-to-key // specifier is implied by its type, as described above. - this.s2k = new S2K(); + const s2kType = bytes[i++]; + this.s2k = newS2KFromType(s2kType); i += this.s2k.read(bytes.subarray(i, bytes.length)); if (this.s2k.type === 'gnu-dummy') { @@ -125,14 +157,37 @@ class SecretKeyPacket extends PublicKeyPacket { this.symmetric = this.s2kUsage; } - // - [Optional] If secret data is encrypted (string-to-key usage octet - // not zero), an Initial Vector (IV) of the same length as the - // cipher's block size. + if (this.s2kUsage) { - this.iv = bytes.subarray( - i, - i + crypto.getCipher(this.symmetric).blockSize - ); + // OpenPGP.js up to v5 used to support encrypting v4 keys using AEAD as specified by draft RFC4880bis (https://www.ietf.org/archive/id/draft-ietf-openpgp-rfc4880bis-10.html#section-5.5.3-3.5). + // This legacy format is incompatible, but fundamentally indistinguishable, from that of the crypto-refresh for v4 keys (v5 keys are always in legacy format). + // While parsing the key may succeed (if IV and AES block sizes match), key decryption will always + // fail if the key was parsed according to the wrong format, since the keys are processed differently. + // Thus, for v4 keys, we rely on the caller to instruct us to process the key as legacy, via config flag. + this.isLegacyAEAD = this.s2kUsage === 253 && ( + this.version === 5 || (this.version === 4 && config.parseAEADEncryptedV4KeysAsLegacy)); + // - crypto-refresh: If string-to-key usage octet was 255, 254 [..], an initialization vector (IV) + // of the same length as the cipher's block size. + // - RFC4880bis (v5 keys, regardless of AEAD): an Initial Vector (IV) of the same length as the + // cipher's block size. If string-to-key usage octet was 253 the IV is used as the nonce for the AEAD algorithm. + // If the AEAD algorithm requires a shorter nonce, the high-order bits of the IV are used and the remaining bits MUST be zero + if (this.s2kUsage !== 253 || this.isLegacyAEAD) { + this.iv = bytes.subarray( + i, + i + crypto.getCipherParams(this.symmetric).blockSize + ); + this.usedModernAEAD = false; + } else { + // crypto-refresh: If string-to-key usage octet was 253 (that is, the secret data is AEAD-encrypted), + // an initialization vector (IV) of size specified by the AEAD algorithm (see Section 5.13.2), which + // is used as the nonce for the AEAD algorithm. + this.iv = bytes.subarray( + i, + i + crypto.getAEADMode(this.aead).ivLength + ); + // the non-legacy AEAD encryption mechanism also authenticates public key params; no need for manual validation. + this.usedModernAEAD = true; + } i += this.iv.length; } @@ -156,12 +211,20 @@ class SecretKeyPacket extends PublicKeyPacket { this.isEncrypted = !!this.s2kUsage; if (!this.isEncrypted) { - const cleartext = this.keyMaterial.subarray(0, -2); - if (!util.equalsUint8Array(util.writeChecksum(cleartext), this.keyMaterial.subarray(-2))) { - throw new Error('Key checksum mismatch'); + let cleartext; + if (this.version === 6) { + cleartext = this.keyMaterial; + } else { + cleartext = this.keyMaterial.subarray(0, -2); + if (!util.equalsUint8Array(util.writeChecksum(cleartext), this.keyMaterial.subarray(-2))) { + throw new Error('Key checksum mismatch'); + } } try { - const { privateParams } = crypto.parsePrivateKeyParams(this.algorithm, cleartext, this.publicParams); + const { read, privateParams } = crypto.parsePrivateKeyParams(this.algorithm, cleartext, this.publicParams); + if (read < cleartext.length) { + throw new Error('Error reading MPIs'); + } this.privateParams = privateParams; } catch (err) { if (err instanceof UnsupportedError) throw err; @@ -199,10 +262,18 @@ class SecretKeyPacket extends PublicKeyPacket { optionalFieldsArr.push(this.aead); } + const s2k = this.s2k.write(); + + // - [Optional] Only for a version 6 packet, and if string-to-key usage + // octet was 255, 254, or 253, an one-octet count of the following field. + if (this.version === 6) { + optionalFieldsArr.push(s2k.length); + } + // - [Optional] If string-to-key usage octet was 255, 254, or 253, a // string-to-key specifier. The length of the string-to-key // specifier is implied by its type, as described above. - optionalFieldsArr.push(...this.s2k.write()); + optionalFieldsArr.push(...s2k); } // - [Optional] If secret data is encrypted (string-to-key usage octet @@ -212,7 +283,7 @@ class SecretKeyPacket extends PublicKeyPacket { optionalFieldsArr.push(...this.iv); } - if (this.version === 5) { + if (this.version === 5 || (this.version === 6 && this.s2kUsage)) { arr.push(new Uint8Array([optionalFieldsArr.length])); } arr.push(new Uint8Array(optionalFieldsArr)); @@ -227,7 +298,7 @@ class SecretKeyPacket extends PublicKeyPacket { } arr.push(this.keyMaterial); - if (!this.s2kUsage) { + if (!this.s2kUsage && this.version !== 6) { arr.push(util.writeChecksum(this.keyMaterial)); } } @@ -279,12 +350,14 @@ class SecretKeyPacket extends PublicKeyPacket { delete this.unparseableKeyMaterial; this.isEncrypted = null; this.keyMaterial = null; - this.s2k = new S2K(config); + this.s2k = newS2KFromType(enums.s2k.gnu, config); this.s2k.algorithm = 0; this.s2k.c = 0; this.s2k.type = 'gnu-dummy'; this.s2kUsage = 254; this.symmetric = enums.symmetric.aes256; + this.isLegacyAEAD = null; + this.usedModernAEAD = null; } /** @@ -310,23 +383,35 @@ class SecretKeyPacket extends PublicKeyPacket { throw new Error('A non-empty passphrase is required for key encryption.'); } - this.s2k = new S2K(config); - this.s2k.salt = crypto.random.getRandomBytes(8); + this.s2k = newS2KFromConfig(config); + this.s2k.generateSalt(); const cleartext = crypto.serializeParams(this.algorithm, this.privateParams); this.symmetric = enums.symmetric.aes256; - const key = await produceEncryptionKey(this.s2k, passphrase, this.symmetric); - const { blockSize } = crypto.getCipher(this.symmetric); - this.iv = crypto.random.getRandomBytes(blockSize); + const { blockSize } = crypto.getCipherParams(this.symmetric); if (config.aeadProtect) { this.s2kUsage = 253; - this.aead = enums.aead.eax; + this.aead = config.preferredAEADAlgorithm; const mode = crypto.getAEADMode(this.aead); + this.isLegacyAEAD = this.version === 5; // v4 is always re-encrypted with standard format instead. + this.usedModernAEAD = !this.isLegacyAEAD; // legacy AEAD does not guarantee integrity of public key material + + const serializedPacketTag = writeTag(this.constructor.tag); + const key = await produceEncryptionKey(this.version, this.s2k, passphrase, this.symmetric, this.aead, serializedPacketTag, this.isLegacyAEAD); + const modeInstance = await mode(this.symmetric, key); - this.keyMaterial = await modeInstance.encrypt(cleartext, this.iv.subarray(0, mode.ivLength), new Uint8Array()); + this.iv = this.isLegacyAEAD ? crypto.random.getRandomBytes(blockSize) : crypto.random.getRandomBytes(mode.ivLength); + const associateData = this.isLegacyAEAD ? + new Uint8Array() : + util.concatUint8Array([serializedPacketTag, this.writePublicKey()]); + + this.keyMaterial = await modeInstance.encrypt(cleartext, this.iv.subarray(0, mode.ivLength), associateData); } else { this.s2kUsage = 254; + this.usedModernAEAD = false; + const key = await produceEncryptionKey(this.version, this.s2k, passphrase, this.symmetric); + this.iv = crypto.random.getRandomBytes(blockSize); this.keyMaterial = await crypto.mode.cfb.encrypt(this.symmetric, key, util.concatUint8Array([ cleartext, await crypto.hash.sha1(cleartext, config) @@ -357,8 +442,10 @@ class SecretKeyPacket extends PublicKeyPacket { } let key; + const serializedPacketTag = writeTag(this.constructor.tag); // relevant for AEAD only if (this.s2kUsage === 254 || this.s2kUsage === 253) { - key = await produceEncryptionKey(this.s2k, passphrase, this.symmetric); + key = await produceEncryptionKey( + this.version, this.s2k, passphrase, this.symmetric, this.aead, serializedPacketTag, this.isLegacyAEAD); } else if (this.s2kUsage === 255) { throw new Error('Encrypted private key is authenticated using an insecure two-byte hash'); } else { @@ -370,7 +457,10 @@ class SecretKeyPacket extends PublicKeyPacket { const mode = crypto.getAEADMode(this.aead); const modeInstance = await mode(this.symmetric, key); try { - cleartext = await modeInstance.decrypt(this.keyMaterial, this.iv.subarray(0, mode.ivLength), new Uint8Array()); + const associateData = this.isLegacyAEAD ? + new Uint8Array() : + util.concatUint8Array([serializedPacketTag, this.writePublicKey()]); + cleartext = await modeInstance.decrypt(this.keyMaterial, this.iv.subarray(0, mode.ivLength), associateData); } catch (err) { if (err.message === 'Authentication tag mismatch') { throw new Error('Incorrect key passphrase: ' + err.message); @@ -397,6 +487,9 @@ class SecretKeyPacket extends PublicKeyPacket { this.isEncrypted = false; this.keyMaterial = null; this.s2kUsage = 0; + this.aead = null; + this.symmetric = null; + this.isLegacyAEAD = null; } /** @@ -413,6 +506,11 @@ class SecretKeyPacket extends PublicKeyPacket { throw new Error('Key is not decrypted'); } + if (this.usedModernAEAD) { + // key integrity confirmed by successful AEAD decryption + return; + } + let validParams; try { // this can throw if some parameters are undefined @@ -426,6 +524,14 @@ class SecretKeyPacket extends PublicKeyPacket { } async generate(bits, curve) { + // The deprecated OIDs for Ed25519Legacy and Curve25519Legacy are used in legacy version 4 keys and signatures. + // Implementations MUST NOT accept or generate v6 key material using the deprecated OIDs. + if (this.version === 6 && ( + (this.algorithm === enums.publicKey.ecdh && curve === enums.curve.curve25519Legacy) || + this.algorithm === enums.publicKey.eddsaLegacy + )) { + throw new Error(`Cannot generate v6 keys of type 'ecc' with curve ${curve}. Generate a key of type 'curve25519' instead`); + } const { privateParams, publicParams } = await crypto.generateParams(this.algorithm, bits, curve); this.privateParams = privateParams; this.publicParams = publicParams; @@ -450,9 +556,34 @@ class SecretKeyPacket extends PublicKeyPacket { } } -async function produceEncryptionKey(s2k, passphrase, algorithm) { - const { keySize } = crypto.getCipher(algorithm); - return s2k.produceKey(passphrase, keySize); +/** + * Derive encryption key + * @param {Number} keyVersion - key derivation differs for v5 keys + * @param {module:type/s2k} s2k + * @param {String} passphrase + * @param {module:enums.symmetric} cipherAlgo + * @param {module:enums.aead} [aeadMode] - for AEAD-encrypted keys only (excluding v5) + * @param {Uint8Array} [serializedPacketTag] - for AEAD-encrypted keys only (excluding v5) + * @param {Boolean} [isLegacyAEAD] - for AEAD-encrypted keys from RFC4880bis (v4 and v5 only) + * @returns encryption key + */ +async function produceEncryptionKey(keyVersion, s2k, passphrase, cipherAlgo, aeadMode, serializedPacketTag, isLegacyAEAD) { + if (s2k.type === 'argon2' && !aeadMode) { + throw new Error('Using Argon2 S2K without AEAD is not allowed'); + } + if (s2k.type === 'simple' && keyVersion === 6) { + throw new Error('Using Simple S2K with version 6 keys is not allowed'); + } + const { keySize } = crypto.getCipherParams(cipherAlgo); + const derivedKey = await s2k.produceKey(passphrase, keySize); + if (!aeadMode || keyVersion === 5 || isLegacyAEAD) { + return derivedKey; + } + const info = util.concatUint8Array([ + serializedPacketTag, + new Uint8Array([keyVersion, cipherAlgo, aeadMode]) + ]); + return computeHKDF(enums.hash.sha256, derivedKey, new Uint8Array(), info, keySize); } export default SecretKeyPacket; diff --git a/src/packet/signature.js b/src/packet/signature.js index 8bd0347a..87e0d304 100644 --- a/src/packet/signature.js +++ b/src/packet/signature.js @@ -26,11 +26,19 @@ import defaultConfig from '../config'; // Symbol to store cryptographic validity of the signature, to avoid recomputing multiple times on verification. const verified = Symbol('verified'); +// A salt notation is used to randomize signatures. +// This is to protect EdDSA signatures in particular, which are known to be vulnerable to fault attacks +// leading to secret key extraction if two signatures over the same data can be collected (see https://github.com/jedisct1/libsodium/issues/170). +// For simplicity, we add the salt to all algos, as it may also serve as protection in case of weaknesses in the hash algo, potentially hindering e.g. +// some chosen-prefix attacks. +// v6 signatures do not need to rely on this notation, as they already include a separate, built-in salt. +const SALT_NOTATION_NAME = 'salt@notations.openpgpjs.org'; + // GPG puts the Issuer and Signature subpackets in the unhashed area. // Tampering with those invalidates the signature, so we still trust them and parse them. // All other unhashed subpackets are ignored. const allowedUnhashedSubpackets = new Set([ - enums.signatureSubpacket.issuer, + enums.signatureSubpacket.issuerKeyID, enums.signatureSubpacket.issuerFingerprint, enums.signatureSubpacket.embeddedSignature ]); @@ -59,7 +67,9 @@ class SignaturePacket { this.signatureData = null; this.unhashedSubpackets = []; + this.unknownSubpackets = []; this.signedHashValue = null; + this.salt = null; this.created = null; this.signatureExpirationTime = null; @@ -96,6 +106,7 @@ class SignaturePacket { this.issuerKeyVersion = null; this.issuerFingerprint = null; this.preferredAEADAlgorithms = null; + this.preferredCipherSuites = null; this.revoked = null; this[verified] = null; @@ -106,11 +117,14 @@ class SignaturePacket { * @param {String} bytes - Payload of a tag 2 packet * @returns {SignaturePacket} Object representation. */ - read(bytes) { + read(bytes, config = defaultConfig) { let i = 0; this.version = bytes[i++]; + if (this.version === 5 && !config.enableParsingV5Entities) { + throw new UnsupportedError('Support for v5 entities is disabled; turn on `config.enableParsingV5Entities` if needed'); + } - if (this.version !== 4 && this.version !== 5) { + if (this.version !== 4 && this.version !== 5 && this.version !== 6) { throw new UnsupportedError(`Version ${this.version} of the signature packet is unsupported.`); } @@ -139,7 +153,24 @@ class SignaturePacket { this.signedHashValue = bytes.subarray(i, i + 2); i += 2; - this.params = crypto.signature.parseSignatureParams(this.publicKeyAlgorithm, bytes.subarray(i, bytes.length)); + // Only for v6 signatures, a variable-length field containing: + if (this.version === 6) { + // A one-octet salt size. The value MUST match the value defined + // for the hash algorithm as specified in Table 23 (Hash algorithm registry). + // To allow parsing unknown hash algos, we only check the expected salt length when verifying. + const saltLength = bytes[i++]; + + // The salt; a random value value of the specified size. + this.salt = bytes.subarray(i, i + saltLength); + i += saltLength; + } + + const signatureMaterial = bytes.subarray(i, bytes.length); + const { read, signatureParams } = crypto.signature.parseSignatureParams(this.publicKeyAlgorithm, signatureMaterial); + if (read < signatureMaterial.length) { + throw new Error('Error reading MPIs'); + } + this.params = signatureParams; } /** @@ -159,6 +190,10 @@ class SignaturePacket { arr.push(this.signatureData); arr.push(this.writeUnhashedSubPackets()); arr.push(this.signedHashValue); + if (this.version === 6) { + arr.push(new Uint8Array([this.salt.length])); + arr.push(this.salt); + } arr.push(this.writeParams()); return util.concat(arr); } @@ -172,19 +207,41 @@ class SignaturePacket { * @throws {Error} if signing failed * @async */ - async sign(key, data, date = new Date(), detached = false) { - if (key.version === 5) { - this.version = 5; - } else { - this.version = 4; - } - const arr = [new Uint8Array([this.version, this.signatureType, this.publicKeyAlgorithm, this.hashAlgorithm])]; + async sign(key, data, date = new Date(), detached = false, config) { + this.version = key.version; this.created = util.normalizeDate(date); this.issuerKeyVersion = key.version; this.issuerFingerprint = key.getFingerprintBytes(); this.issuerKeyID = key.getKeyID(); + const arr = [new Uint8Array([this.version, this.signatureType, this.publicKeyAlgorithm, this.hashAlgorithm])]; + + // add randomness to the signature + if (this.version === 6) { + const saltLength = saltLengthForHash(this.hashAlgorithm); + if (this.salt === null) { + this.salt = crypto.random.getRandomBytes(saltLength); + } else if (saltLength !== this.salt.length) { + throw new Error('Provided salt does not have the required length'); + } + } else if (config.nonDeterministicSignaturesViaNotation) { + const saltNotations = this.rawNotations.filter(({ name }) => (name === SALT_NOTATION_NAME)); + // since re-signing the same object is not supported, it's not expected to have multiple salt notations, + // but we guard against it as a sanity check + if (saltNotations.length === 0) { + const saltValue = crypto.random.getRandomBytes(saltLengthForHash(this.hashAlgorithm)); + this.rawNotations.push({ + name: SALT_NOTATION_NAME, + value: saltValue, + humanReadable: false, + critical: false + }); + } else { + throw new Error('Unexpected existing salt notation'); + } + } + // Add hashed subpackets arr.push(this.writeHashedSubPackets()); @@ -255,10 +312,10 @@ class SignaturePacket { bytes = util.concat([bytes, this.revocationKeyFingerprint]); arr.push(writeSubPacket(sub.revocationKey, false, bytes)); } - if (!this.issuerKeyID.isNull() && this.issuerKeyVersion !== 5) { + if (!this.issuerKeyID.isNull() && this.issuerKeyVersion < 5) { // If the version of [the] key is greater than 4, this subpacket // MUST NOT be included in the signature. - arr.push(writeSubPacket(sub.issuer, true, this.issuerKeyID.write())); + arr.push(writeSubPacket(sub.issuerKeyID, true, this.issuerKeyID.write())); } this.rawNotations.forEach(({ name, value, humanReadable, critical }) => { bytes = [new Uint8Array([humanReadable ? 0x80 : 0, 0, 0, 0])]; @@ -320,15 +377,19 @@ class SignaturePacket { if (this.issuerFingerprint !== null) { bytes = [new Uint8Array([this.issuerKeyVersion]), this.issuerFingerprint]; bytes = util.concat(bytes); - arr.push(writeSubPacket(sub.issuerFingerprint, this.version === 5, bytes)); + arr.push(writeSubPacket(sub.issuerFingerprint, this.version >= 5, bytes)); } if (this.preferredAEADAlgorithms !== null) { bytes = util.stringToUint8Array(util.uint8ArrayToString(this.preferredAEADAlgorithms)); arr.push(writeSubPacket(sub.preferredAEADAlgorithms, false, bytes)); } + if (this.preferredCipherSuites !== null) { + bytes = new Uint8Array([].concat(...this.preferredCipherSuites)); + arr.push(writeSubPacket(sub.preferredCipherSuites, false, bytes)); + } const result = util.concat(arr); - const length = util.writeNumber(result.length, 2); + const length = util.writeNumber(result.length, this.version === 6 ? 4 : 2); return util.concat([length, result]); } @@ -338,19 +399,17 @@ class SignaturePacket { * @returns {Uint8Array} Subpacket data. */ writeUnhashedSubPackets() { - const arr = []; - this.unhashedSubpackets.forEach(data => { - arr.push(writeSimpleLength(data.length)); - arr.push(data); + const arr = this.unhashedSubpackets.map(({ type, critical, body }) => { + return writeSubPacket(type, critical, body); }); const result = util.concat(arr); - const length = util.writeNumber(result.length, 2); + const length = util.writeNumber(result.length, this.version === 6 ? 4 : 2); return util.concat([length, result]); } - // V4 signature sub packets + // Signature subpackets readSubPacket(bytes, hashed = true) { let mypos = 0; @@ -358,15 +417,19 @@ class SignaturePacket { const critical = !!(bytes[mypos] & 0x80); const type = bytes[mypos] & 0x7F; + mypos++; + if (!hashed) { - this.unhashedSubpackets.push(bytes.subarray(mypos, bytes.length)); + this.unhashedSubpackets.push({ + type, + critical, + body: bytes.subarray(mypos, bytes.length) + }); if (!allowedUnhashedSubpackets.has(type)) { return; } } - mypos++; - // subpacket type switch (type) { case enums.signatureSubpacket.signatureCreationTime: @@ -422,9 +485,21 @@ class SignaturePacket { this.revocationKeyFingerprint = bytes.subarray(mypos, mypos + 20); break; - case enums.signatureSubpacket.issuer: + case enums.signatureSubpacket.issuerKeyID: // Issuer - this.issuerKeyID.read(bytes.subarray(mypos, bytes.length)); + if (this.version === 4) { + this.issuerKeyID.read(bytes.subarray(mypos, bytes.length)); + } else if (hashed) { + // If the version of the key is greater than 4, this subpacket MUST NOT be included in the signature, + // since the Issuer Fingerprint subpacket is to be used instead. + // The `issuerKeyID` value will be set when reading the issuerFingerprint packet. + // For this reason, if the issuer Key ID packet is present but unhashed, we simply ignore it, + // to avoid situations where `.getSigningKeyIDs()` returns a keyID potentially different from the (signed) + // issuerFingerprint. + // If the packet is hashed, then we reject the signature, to avoid verifying data different from + // what was parsed. + throw new Error('Unexpected Issuer Key ID subpacket'); + } break; case enums.signatureSubpacket.notationData: { @@ -509,7 +584,7 @@ class SignaturePacket { // Issuer Fingerprint this.issuerKeyVersion = bytes[mypos++]; this.issuerFingerprint = bytes.subarray(mypos, bytes.length); - if (this.issuerKeyVersion === 5) { + if (this.issuerKeyVersion >= 5) { this.issuerKeyID.read(this.issuerFingerprint); } else { this.issuerKeyID.read(this.issuerFingerprint.subarray(-8)); @@ -519,22 +594,30 @@ class SignaturePacket { // Preferred AEAD Algorithms this.preferredAEADAlgorithms = [...bytes.subarray(mypos, bytes.length)]; break; - default: { - const err = new Error(`Unknown signature subpacket type ${type}`); - if (critical) { - throw err; - } else { - util.printDebug(err); + case enums.signatureSubpacket.preferredCipherSuites: + // Preferred AEAD Cipher Suites + this.preferredCipherSuites = []; + for (let i = mypos; i < bytes.length; i += 2) { + this.preferredCipherSuites.push([bytes[i], bytes[i + 1]]); } - } + break; + default: + this.unknownSubpackets.push({ + type, + critical, + body: bytes.subarray(mypos, bytes.length) + }); + break; } } readSubPackets(bytes, trusted = true, config) { - // Two-octet scalar octet count for following subpacket data. - const subpacketLength = util.readNumber(bytes.subarray(0, 2)); + const subpacketLengthBytes = this.version === 6 ? 4 : 2; - let i = 2; + // Two-octet scalar octet count for following subpacket data. + const subpacketLength = util.readNumber(bytes.subarray(0, subpacketLengthBytes)); + + let i = subpacketLengthBytes; // subpacket data set (zero or more subpackets) while (i < 2 + subpacketLength) { @@ -645,10 +728,15 @@ class SignaturePacket { toHash(signatureType, data, detached = false) { const bytes = this.toSign(signatureType, data); - return util.concat([bytes, this.signatureData, this.calculateTrailer(data, detached)]); + return util.concat([this.salt || new Uint8Array(), bytes, this.signatureData, this.calculateTrailer(data, detached)]); } async hash(signatureType, data, toHash, detached = false) { + if (this.version === 6 && this.salt.length !== saltLengthForHash(this.hashAlgorithm)) { + // avoid hashing unexpected salt size + throw new Error('Signature salt does not have the expected length'); + } + if (!toHash) toHash = this.toHash(signatureType, data, detached); return crypto.hash.digest(this.hashAlgorithm, toHash); } @@ -718,6 +806,11 @@ class SignaturePacket { [enums.signature.binary, enums.signature.text].includes(this.signatureType)) { throw new Error('Insecure message hash algorithm: ' + enums.read(enums.hash, this.hashAlgorithm).toUpperCase()); } + this.unknownSubpackets.forEach(({ type, critical }) => { + if (critical) { + throw new Error(`Unknown critical signature subpacket type ${type}`); + } + }); this.rawNotations.forEach(({ name, critical }) => { if (critical && (config.knownNotations.indexOf(name) < 0)) { throw new Error(`Unknown critical notation: ${name}`); @@ -769,3 +862,22 @@ function writeSubPacket(type, critical, data) { arr.push(data); return util.concat(arr); } + +/** + * Select the required salt length for the given hash algorithm, as per Table 23 (Hash algorithm registry) of the crypto refresh. + * @see {@link https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh#section-9.5|Crypto Refresh Section 9.5} + * @param {enums.hash} hashAlgorithm - Hash algorithm. + * @returns {Integer} Salt length. + * @private + */ +function saltLengthForHash(hashAlgorithm) { + switch (hashAlgorithm) { + case enums.hash.sha256: return 16; + case enums.hash.sha384: return 24; + case enums.hash.sha512: return 32; + case enums.hash.sha224: return 16; + case enums.hash.sha3_256: return 16; + case enums.hash.sha3_512: return 32; + default: throw new Error('Unsupported hash function'); + } +} diff --git a/src/packet/sym_encrypted_integrity_protected_data.js b/src/packet/sym_encrypted_integrity_protected_data.js index d1099a9f..c2762c83 100644 --- a/src/packet/sym_encrypted_integrity_protected_data.js +++ b/src/packet/sym_encrypted_integrity_protected_data.js @@ -17,6 +17,7 @@ import * as stream from '@openpgp/web-stream-tools'; import crypto from '../crypto'; +import computeHKDF from '../crypto/hkdf'; import enums from '../enums'; import util from '../util'; import defaultConfig from '../config'; @@ -36,8 +37,6 @@ const allowedPackets = /*#__PURE__*/ util.constructAllowedPackets([ SignaturePacket ]); -const VERSION = 1; // A one-octet version number of the data packet. - /** * Implementation of the Sym. Encrypted Integrity Protected Data Packet (Tag 18) * @@ -53,29 +52,70 @@ class SymEncryptedIntegrityProtectedDataPacket { return enums.packet.symEncryptedIntegrityProtectedData; } + static fromObject({ version, aeadAlgorithm }) { + if (version !== 1 && version !== 2) { + throw new Error('Unsupported SEIPD version'); + } + + const seip = new SymEncryptedIntegrityProtectedDataPacket(); + seip.version = version; + if (version === 2) { + seip.aeadAlgorithm = aeadAlgorithm; + } + + return seip; + } + constructor() { - this.version = VERSION; + this.version = null; + + // The following 4 fields are for V2 only. + /** @type {enums.symmetric} */ + this.cipherAlgorithm = null; + /** @type {enums.aead} */ + this.aeadAlgorithm = null; + this.chunkSizeByte = null; + this.salt = null; + this.encrypted = null; this.packets = null; } async read(bytes) { await stream.parse(bytes, async reader => { - const version = await reader.readByte(); - // - A one-octet version number. The only currently defined value is 1. - if (version !== VERSION) { - throw new UnsupportedError(`Version ${version} of the SEIP packet is unsupported.`); + this.version = await reader.readByte(); + // - A one-octet version number with value 1 or 2. + if (this.version !== 1 && this.version !== 2) { + throw new UnsupportedError(`Version ${this.version} of the SEIP packet is unsupported.`); } + if (this.version === 2) { + // - A one-octet cipher algorithm. + this.cipherAlgorithm = await reader.readByte(); + // - A one-octet AEAD algorithm. + this.aeadAlgorithm = await reader.readByte(); + // - A one-octet chunk size. + this.chunkSizeByte = await reader.readByte(); + // - Thirty-two octets of salt. The salt is used to derive the message key and must be unique. + this.salt = await reader.readBytes(32); + } + + // For V1: // - Encrypted data, the output of the selected symmetric-key cipher // operating in Cipher Feedback mode with shift amount equal to the // block size of the cipher (CFB-n where n is the block size). + // For V2: + // - Encrypted data, the output of the selected symmetric-key cipher operating in the given AEAD mode. + // - A final, summary authentication tag for the AEAD mode. this.encrypted = reader.remainder(); }); } write() { - return util.concat([new Uint8Array([VERSION]), this.encrypted]); + if (this.version === 2) { + return util.concat([new Uint8Array([this.version, this.cipherAlgorithm, this.aeadAlgorithm, this.chunkSizeByte]), this.salt, this.encrypted]); + } + return util.concat([new Uint8Array([this.version]), this.encrypted]); } /** @@ -88,18 +128,35 @@ class SymEncryptedIntegrityProtectedDataPacket { * @async */ async encrypt(sessionKeyAlgorithm, key, config = defaultConfig) { - const { blockSize } = crypto.getCipher(sessionKeyAlgorithm); + // We check that the session key size matches the one expected by the symmetric algorithm. + // This is especially important for SEIPDv2 session keys, as a key derivation step is run where the resulting key will always match the expected cipher size, + // but we want to ensure that the input key isn't e.g. too short. + // The check is done here, instead of on encrypted session key (ESK) encryption, because v6 ESK packets do not store the session key algorithm, + // which is instead included in the SEIPDv2 data. + const { blockSize, keySize } = crypto.getCipherParams(sessionKeyAlgorithm); + if (key.length !== keySize) { + throw new Error('Unexpected session key size'); + } let bytes = this.packets.write(); if (stream.isArrayStream(bytes)) bytes = await stream.readToEnd(bytes); - const prefix = await crypto.getPrefixRandom(sessionKeyAlgorithm); - const mdc = new Uint8Array([0xD3, 0x14]); // modification detection code packet - const tohash = util.concat([prefix, bytes, mdc]); - const hash = await crypto.hash.sha1(stream.passiveClone(tohash)); - const plaintext = util.concat([tohash, hash]); + if (this.version === 2) { + this.cipherAlgorithm = sessionKeyAlgorithm; - this.encrypted = await crypto.mode.cfb.encrypt(sessionKeyAlgorithm, key, plaintext, new Uint8Array(blockSize), config); + this.salt = crypto.random.getRandomBytes(32); + this.chunkSizeByte = config.aeadChunkSizeByte; + this.encrypted = await runAEAD(this, 'encrypt', key, bytes); + } else { + const prefix = await crypto.getPrefixRandom(sessionKeyAlgorithm); + const mdc = new Uint8Array([0xD3, 0x14]); // modification detection code packet + + const tohash = util.concat([prefix, bytes, mdc]); + const hash = await crypto.hash.sha1(stream.passiveClone(tohash)); + const plaintext = util.concat([tohash, hash]); + + this.encrypted = await crypto.mode.cfb.encrypt(sessionKeyAlgorithm, key, plaintext, new Uint8Array(blockSize), config); + } return true; } @@ -113,33 +170,167 @@ class SymEncryptedIntegrityProtectedDataPacket { * @async */ async decrypt(sessionKeyAlgorithm, key, config = defaultConfig) { - const { blockSize } = crypto.getCipher(sessionKeyAlgorithm); + // We check that the session key size matches the one expected by the symmetric algorithm. + // This is especially important for SEIPDv2 session keys, as a key derivation step is run where the resulting key will always match the expected cipher size, + // but we want to ensure that the input key isn't e.g. too short. + // The check is done here, instead of on encrypted session key (ESK) decryption, because v6 ESK packets do not store the session key algorithm, + // which is instead included in the SEIPDv2 data. + if (key.length !== crypto.getCipherParams(sessionKeyAlgorithm).keySize) { + throw new Error('Unexpected session key size'); + } + let encrypted = stream.clone(this.encrypted); if (stream.isArrayStream(encrypted)) encrypted = await stream.readToEnd(encrypted); - const decrypted = await crypto.mode.cfb.decrypt(sessionKeyAlgorithm, key, encrypted, new Uint8Array(blockSize)); - // there must be a modification detection code packet as the - // last packet and everything gets hashed except the hash itself - const realHash = stream.slice(stream.passiveClone(decrypted), -20); - const tohash = stream.slice(decrypted, 0, -20); - const verifyHash = Promise.all([ - stream.readToEnd(await crypto.hash.sha1(stream.passiveClone(tohash))), - stream.readToEnd(realHash) - ]).then(([hash, mdc]) => { - if (!util.equalsUint8Array(hash, mdc)) { - throw new Error('Modification detected.'); + let packetbytes; + if (this.version === 2) { + if (this.cipherAlgorithm !== sessionKeyAlgorithm) { + // sanity check + throw new Error('Unexpected session key algorithm'); + } + packetbytes = await runAEAD(this, 'decrypt', key, encrypted); + } else { + const { blockSize } = crypto.getCipherParams(sessionKeyAlgorithm); + const decrypted = await crypto.mode.cfb.decrypt(sessionKeyAlgorithm, key, encrypted, new Uint8Array(blockSize)); + + // there must be a modification detection code packet as the + // last packet and everything gets hashed except the hash itself + const realHash = stream.slice(stream.passiveClone(decrypted), -20); + const tohash = stream.slice(decrypted, 0, -20); + const verifyHash = Promise.all([ + stream.readToEnd(await crypto.hash.sha1(stream.passiveClone(tohash))), + stream.readToEnd(realHash) + ]).then(([hash, mdc]) => { + if (!util.equalsUint8Array(hash, mdc)) { + throw new Error('Modification detected.'); + } + return new Uint8Array(); + }); + const bytes = stream.slice(tohash, blockSize + 2); // Remove random prefix + packetbytes = stream.slice(bytes, 0, -2); // Remove MDC packet + packetbytes = stream.concat([packetbytes, stream.fromAsync(() => verifyHash)]); + if (!util.isStream(encrypted) || !config.allowUnauthenticatedStream) { + packetbytes = await stream.readToEnd(packetbytes); } - return new Uint8Array(); - }); - const bytes = stream.slice(tohash, blockSize + 2); // Remove random prefix - let packetbytes = stream.slice(bytes, 0, -2); // Remove MDC packet - packetbytes = stream.concat([packetbytes, stream.fromAsync(() => verifyHash)]); - if (!util.isStream(encrypted) || !config.allowUnauthenticatedStream) { - packetbytes = await stream.readToEnd(packetbytes); } + this.packets = await PacketList.fromBinary(packetbytes, allowedPackets, config); return true; } } export default SymEncryptedIntegrityProtectedDataPacket; + +/** + * En/decrypt the payload. + * @param {encrypt|decrypt} fn - Whether to encrypt or decrypt + * @param {Uint8Array} key - The session key used to en/decrypt the payload + * @param {Uint8Array | ReadableStream} data - The data to en/decrypt + * @returns {Promise>} + * @async + */ +export async function runAEAD(packet, fn, key, data) { + const isSEIPDv2 = packet instanceof SymEncryptedIntegrityProtectedDataPacket && packet.version === 2; + const isAEADP = !isSEIPDv2 && packet.constructor.tag === enums.packet.aeadEncryptedData; // no `instanceof` to avoid importing the corresponding class (circular import) + if (!isSEIPDv2 && !isAEADP) throw new Error('Unexpected packet type'); + + const mode = crypto.getAEADMode(packet.aeadAlgorithm); + const tagLengthIfDecrypting = fn === 'decrypt' ? mode.tagLength : 0; + const tagLengthIfEncrypting = fn === 'encrypt' ? mode.tagLength : 0; + const chunkSize = 2 ** (packet.chunkSizeByte + 6) + tagLengthIfDecrypting; // ((uint64_t)1 << (c + 6)) + const chunkIndexSizeIfAEADEP = isAEADP ? 8 : 0; + const adataBuffer = new ArrayBuffer(13 + chunkIndexSizeIfAEADEP); + const adataArray = new Uint8Array(adataBuffer, 0, 5 + chunkIndexSizeIfAEADEP); + const adataTagArray = new Uint8Array(adataBuffer); + const adataView = new DataView(adataBuffer); + const chunkIndexArray = new Uint8Array(adataBuffer, 5, 8); + adataArray.set([0xC0 | packet.constructor.tag, packet.version, packet.cipherAlgorithm, packet.aeadAlgorithm, packet.chunkSizeByte], 0); + let chunkIndex = 0; + let latestPromise = Promise.resolve(); + let cryptedBytes = 0; + let queuedBytes = 0; + let iv; + let ivView; + if (isSEIPDv2) { + const { keySize } = crypto.getCipherParams(packet.cipherAlgorithm); + const { ivLength } = mode; + const info = new Uint8Array(adataBuffer, 0, 5); + const derived = await computeHKDF(enums.hash.sha256, key, packet.salt, info, keySize + ivLength); + key = derived.subarray(0, keySize); + iv = derived.subarray(keySize); // The last 8 bytes of HKDF output are unneeded, but this avoids one copy. + iv.fill(0, iv.length - 8); + ivView = new DataView(iv.buffer, iv.byteOffset, iv.byteLength); + } else { // AEADEncryptedDataPacket + iv = packet.iv; + // ivView is unused in this case + } + const modeInstance = await mode(packet.cipherAlgorithm, key); + return stream.transformPair(data, async (readable, writable) => { + if (util.isStream(readable) !== 'array') { + const buffer = new TransformStream({}, { + highWaterMark: util.getHardwareConcurrency() * 2 ** (packet.chunkSizeByte + 6), + size: array => array.length + }); + stream.pipe(buffer.readable, writable); + writable = buffer.writable; + } + const reader = stream.getReader(readable); + const writer = stream.getWriter(writable); + try { + while (true) { + let chunk = await reader.readBytes(chunkSize + tagLengthIfDecrypting) || new Uint8Array(); + const finalChunk = chunk.subarray(chunk.length - tagLengthIfDecrypting); + chunk = chunk.subarray(0, chunk.length - tagLengthIfDecrypting); + let cryptedPromise; + let done; + let nonce; + if (isSEIPDv2) { // SEIPD V2 + nonce = iv; + } else { // AEADEncryptedDataPacket + nonce = iv.slice(); + for (let i = 0; i < 8; i++) { + nonce[iv.length - 8 + i] ^= chunkIndexArray[i]; + } + } + if (!chunkIndex || chunk.length) { + reader.unshift(finalChunk); + cryptedPromise = modeInstance[fn](chunk, nonce, adataArray); + cryptedPromise.catch(() => {}); + queuedBytes += chunk.length - tagLengthIfDecrypting + tagLengthIfEncrypting; + } else { + // After the last chunk, we either encrypt a final, empty + // data chunk to get the final authentication tag or + // validate that final authentication tag. + adataView.setInt32(5 + chunkIndexSizeIfAEADEP + 4, cryptedBytes); // Should be setInt64(5 + chunkIndexSizeIfAEADEP, ...) + cryptedPromise = modeInstance[fn](finalChunk, nonce, adataTagArray); + cryptedPromise.catch(() => {}); + queuedBytes += tagLengthIfEncrypting; + done = true; + } + cryptedBytes += chunk.length - tagLengthIfDecrypting; + // eslint-disable-next-line @typescript-eslint/no-loop-func + latestPromise = latestPromise.then(() => cryptedPromise).then(async crypted => { + await writer.ready; + await writer.write(crypted); + queuedBytes -= crypted.length; + }).catch(err => writer.abort(err)); + if (done || queuedBytes > writer.desiredSize) { + await latestPromise; // Respect backpressure + } + if (!done) { + if (isSEIPDv2) { // SEIPD V2 + ivView.setInt32(iv.length - 4, ++chunkIndex); // Should be setInt64(iv.length - 8, ...) + } else { // AEADEncryptedDataPacket + adataView.setInt32(5 + 4, ++chunkIndex); // Should be setInt64(5, ...) + } + } else { + await writer.close(); + break; + } + } + } catch (e) { + await writer.ready.catch(() => {}); + await writer.abort(e); + } + }); +} diff --git a/src/packet/sym_encrypted_session_key.js b/src/packet/sym_encrypted_session_key.js index a383c711..4ea592f9 100644 --- a/src/packet/sym_encrypted_session_key.js +++ b/src/packet/sym_encrypted_session_key.js @@ -15,9 +15,10 @@ // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -import S2K from '../type/s2k'; +import { newS2KFromConfig, newS2KFromType } from '../type/s2k'; import defaultConfig from '../config'; import crypto from '../crypto'; +import computeHKDF from '../crypto/hkdf'; import enums from '../enums'; import util from '../util'; import { UnsupportedError } from './packet'; @@ -44,7 +45,7 @@ class SymEncryptedSessionKeyPacket { * @param {Object} [config] - Full configuration, defaults to openpgp.config */ constructor(config = defaultConfig) { - this.version = config.aeadProtect ? 5 : 4; + this.version = config.aeadProtect ? 6 : 4; this.sessionKey = null; /** * Algorithm to encrypt the session key with @@ -55,7 +56,7 @@ class SymEncryptedSessionKeyPacket { * Algorithm to encrypt the message with * @type {enums.symmetric} */ - this.sessionKeyAlgorithm = enums.symmetric.aes256; + this.sessionKeyAlgorithm = null; /** * AEAD mode to encrypt the session key with (if AEAD protection is enabled) * @type {enums.aead} @@ -74,25 +75,36 @@ class SymEncryptedSessionKeyPacket { read(bytes) { let offset = 0; - // A one-octet version number. The only currently defined version is 4. + // A one-octet version number with value 4, 5 or 6. this.version = bytes[offset++]; - if (this.version !== 4 && this.version !== 5) { + if (this.version !== 4 && this.version !== 5 && this.version !== 6) { throw new UnsupportedError(`Version ${this.version} of the SKESK packet is unsupported.`); } + if (this.version === 6) { + // A one-octet scalar octet count of the following 5 fields. + offset++; + } + // A one-octet number describing the symmetric algorithm used. const algo = bytes[offset++]; - if (this.version === 5) { + if (this.version >= 5) { // A one-octet AEAD algorithm. this.aeadAlgorithm = bytes[offset++]; + + if (this.version === 6) { + // A one-octet scalar octet count of the following field. + offset++; + } } // A string-to-key (S2K) specifier, length as defined above. - this.s2k = new S2K(); + const s2kType = bytes[offset++]; + this.s2k = newS2KFromType(s2kType); offset += this.s2k.read(bytes.subarray(offset, bytes.length)); - if (this.version === 5) { + if (this.version >= 5) { const mode = crypto.getAEADMode(this.aeadAlgorithm); // A starting initialization vector of size specified by the AEAD @@ -102,7 +114,7 @@ class SymEncryptedSessionKeyPacket { // The encrypted session key itself, which is decrypted with the // string-to-key object. This is optional in version 4. - if (this.version === 5 || offset < bytes.length) { + if (this.version >= 5 || offset < bytes.length) { this.encrypted = bytes.subarray(offset, bytes.length); this.sessionKeyEncryptionAlgorithm = algo; } else { @@ -122,10 +134,15 @@ class SymEncryptedSessionKeyPacket { let bytes; - if (this.version === 5) { - bytes = util.concatUint8Array([new Uint8Array([this.version, algo, this.aeadAlgorithm]), this.s2k.write(), this.iv, this.encrypted]); + const s2k = this.s2k.write(); + if (this.version === 6) { + const s2kLen = s2k.length; + const fieldsLen = 3 + s2kLen + this.iv.length; + bytes = util.concatUint8Array([new Uint8Array([this.version, fieldsLen, algo, this.aeadAlgorithm, s2kLen]), s2k, this.iv, this.encrypted]); + } else if (this.version === 5) { + bytes = util.concatUint8Array([new Uint8Array([this.version, algo, this.aeadAlgorithm]), s2k, this.iv, this.encrypted]); } else { - bytes = util.concatUint8Array([new Uint8Array([this.version, algo]), this.s2k.write()]); + bytes = util.concatUint8Array([new Uint8Array([this.version, algo]), s2k]); if (this.encrypted !== null) { bytes = util.concatUint8Array([bytes, this.encrypted]); @@ -146,20 +163,25 @@ class SymEncryptedSessionKeyPacket { this.sessionKeyEncryptionAlgorithm : this.sessionKeyAlgorithm; - const { blockSize, keySize } = crypto.getCipher(algo); + const { blockSize, keySize } = crypto.getCipherParams(algo); const key = await this.s2k.produceKey(passphrase, keySize); - if (this.version === 5) { + if (this.version >= 5) { const mode = crypto.getAEADMode(this.aeadAlgorithm); const adata = new Uint8Array([0xC0 | SymEncryptedSessionKeyPacket.tag, this.version, this.sessionKeyEncryptionAlgorithm, this.aeadAlgorithm]); - const modeInstance = await mode(algo, key); + const encryptionKey = this.version === 6 ? await computeHKDF(enums.hash.sha256, key, new Uint8Array(), adata, keySize) : key; + const modeInstance = await mode(algo, encryptionKey); this.sessionKey = await modeInstance.decrypt(this.encrypted, this.iv, adata); } else if (this.encrypted !== null) { const decrypted = await crypto.mode.cfb.decrypt(algo, key, this.encrypted, new Uint8Array(blockSize)); this.sessionKeyAlgorithm = enums.write(enums.symmetric, decrypted[0]); this.sessionKey = decrypted.subarray(1, decrypted.length); + if (this.sessionKey.length !== crypto.getCipherParams(this.sessionKeyAlgorithm).keySize) { + throw new Error('Unexpected session key size'); + } } else { + // session key size is checked as part of SEIPDv2 decryption, where we know the expected symmetric algo this.sessionKey = key; } } @@ -178,28 +200,29 @@ class SymEncryptedSessionKeyPacket { this.sessionKeyEncryptionAlgorithm = algo; - this.s2k = new S2K(config); - this.s2k.salt = crypto.random.getRandomBytes(8); + this.s2k = newS2KFromConfig(config); + this.s2k.generateSalt(); - const { blockSize, keySize } = crypto.getCipher(algo); - const encryptionKey = await this.s2k.produceKey(passphrase, keySize); + const { blockSize, keySize } = crypto.getCipherParams(algo); + const key = await this.s2k.produceKey(passphrase, keySize); if (this.sessionKey === null) { this.sessionKey = crypto.generateSessionKey(this.sessionKeyAlgorithm); } - if (this.version === 5) { + if (this.version >= 5) { const mode = crypto.getAEADMode(this.aeadAlgorithm); this.iv = crypto.random.getRandomBytes(mode.ivLength); // generate new random IV - const associatedData = new Uint8Array([0xC0 | SymEncryptedSessionKeyPacket.tag, this.version, this.sessionKeyEncryptionAlgorithm, this.aeadAlgorithm]); + const adata = new Uint8Array([0xC0 | SymEncryptedSessionKeyPacket.tag, this.version, this.sessionKeyEncryptionAlgorithm, this.aeadAlgorithm]); + const encryptionKey = this.version === 6 ? await computeHKDF(enums.hash.sha256, key, new Uint8Array(), adata, keySize) : key; const modeInstance = await mode(algo, encryptionKey); - this.encrypted = await modeInstance.encrypt(this.sessionKey, this.iv, associatedData); + this.encrypted = await modeInstance.encrypt(this.sessionKey, this.iv, adata); } else { const toEncrypt = util.concatUint8Array([ new Uint8Array([this.sessionKeyAlgorithm]), this.sessionKey ]); - this.encrypted = await crypto.mode.cfb.encrypt(algo, encryptionKey, toEncrypt, new Uint8Array(blockSize), config); + this.encrypted = await crypto.mode.cfb.encrypt(algo, key, toEncrypt, new Uint8Array(blockSize), config); } } } diff --git a/src/packet/symmetrically_encrypted_data.js b/src/packet/symmetrically_encrypted_data.js index ff4fd3d3..41e3cae7 100644 --- a/src/packet/symmetrically_encrypted_data.js +++ b/src/packet/symmetrically_encrypted_data.js @@ -86,7 +86,7 @@ class SymmetricallyEncryptedDataPacket { throw new Error('Message is not authenticated.'); } - const { blockSize } = crypto.getCipher(sessionKeyAlgorithm); + const { blockSize } = crypto.getCipherParams(sessionKeyAlgorithm); const encrypted = await stream.readToEnd(stream.clone(this.encrypted)); const decrypted = await crypto.mode.cfb.decrypt(sessionKeyAlgorithm, key, encrypted.subarray(blockSize + 2), @@ -107,7 +107,7 @@ class SymmetricallyEncryptedDataPacket { */ async encrypt(sessionKeyAlgorithm, key, config = defaultConfig) { const data = this.packets.write(); - const { blockSize } = crypto.getCipher(sessionKeyAlgorithm); + const { blockSize } = crypto.getCipherParams(sessionKeyAlgorithm); const prefix = await crypto.getPrefixRandom(sessionKeyAlgorithm); const FRE = await crypto.mode.cfb.encrypt(sessionKeyAlgorithm, key, prefix, new Uint8Array(blockSize), config); diff --git a/src/packet/userid.js b/src/packet/userid.js index 4c89fecc..4a092d8c 100644 --- a/src/packet/userid.js +++ b/src/packet/userid.js @@ -15,8 +15,6 @@ // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -import emailAddresses from 'email-addresses'; - import enums from '../enums'; import util from '../util'; import defaultConfig from '../config'; @@ -79,12 +77,27 @@ class UserIDPacket { if (userID.length > config.maxUserIDLength) { throw new Error('User ID string is too long'); } - try { - const { name, address: email, comments } = emailAddresses.parseOneAddress({ input: userID, atInDisplayName: true }); - this.comment = comments.replace(/^\(|\)$/g, ''); - this.name = name; - this.email = email; - } catch (e) {} + + /** + * We support the conventional cases described in https://www.ietf.org/id/draft-dkg-openpgp-userid-conventions-00.html#section-4.1, + * as well comments placed between the name (if present) and the bracketed email address: + * - name (comment) + * - email + * In the first case, the `email` is the only required part, and it must contain the `@` symbol. + * The `name` and `comment` parts can include any letters, whitespace, and symbols, except for `(` and `)`, + * since they interfere with `comment` parsing. + */ + const re = /^(?[^()]+\s+)?(?\([^()]+\)\s+)?(?<\S+@\S+>)$/; + const matches = re.exec(userID); + if (matches !== null) { + const { name, comment, email } = matches.groups; + this.comment = comment?.replace(/^\(|\)|\s$/g, '').trim() || ''; // remove parenthesis and separating whiltespace + this.name = name?.trim() || ''; + this.email = email.substring(1, email.length - 1); // remove brackets + } else if (/^[^\s@]+@[^\s@]+$/.test(userID)) { // unbracketed email: enforce single @ and no whitespace + this.email = userID; + } + this.userID = userID; } diff --git a/src/signature.js b/src/signature.js index ae9e5623..f3e14e85 100644 --- a/src/signature.js +++ b/src/signature.js @@ -49,7 +49,9 @@ export class Signature { * @returns {ReadableStream} ASCII armor. */ armor(config = defaultConfig) { - return armor(enums.armor.signature, this.write(), undefined, undefined, undefined, config); + // An ASCII-armored sequence of Signature packets that only includes v6 Signature packets MUST NOT contain a CRC24 footer. + const emitChecksum = this.packets.some(packet => packet.constructor.tag === SignaturePacket.tag && packet.version !== 6); + return armor(enums.armor.signature, this.write(), undefined, undefined, undefined, emitChecksum, config); } /** diff --git a/src/type/ecdh_symkey.js b/src/type/ecdh_symkey.js index 71d05983..e23fdd84 100644 --- a/src/type/ecdh_symkey.js +++ b/src/type/ecdh_symkey.js @@ -19,7 +19,6 @@ * Encoded symmetric key for ECDH (incl. legacy x25519) * * @module type/ecdh_symkey - * @private */ import util from '../util'; diff --git a/src/type/ecdh_x_symkey.js b/src/type/ecdh_x_symkey.js index ecef5715..2f3ee8c9 100644 --- a/src/type/ecdh_x_symkey.js +++ b/src/type/ecdh_x_symkey.js @@ -27,7 +27,7 @@ class ECDHXSymmetricKey { let followLength = bytes[read++]; this.algorithm = followLength % 2 ? bytes[read++] : null; // session key size is always even followLength -= followLength % 2; - this.wrappedKey = bytes.subarray(read, read + followLength); read += followLength; + this.wrappedKey = util.readExactSubarray(bytes, read, read + followLength); read += followLength; } /** diff --git a/src/type/keyid.js b/src/type/keyid.js index 2abf6ae0..76da3e00 100644 --- a/src/type/keyid.js +++ b/src/type/keyid.js @@ -17,7 +17,6 @@ /** * @module type/keyid - * @private */ import util from '../util'; diff --git a/src/type/oid.js b/src/type/oid.js index 20278c8a..bd45672b 100644 --- a/src/type/oid.js +++ b/src/type/oid.js @@ -30,12 +30,23 @@ * is constructed by omitting the first two octets. Only the truncated * sequence of octets is the valid representation of a curve OID. * @module type/oid - * @private */ import util from '../util'; import enums from '../enums'; +const knownOIDs = { + '2a8648ce3d030107': enums.curve.nistP256, + '2b81040022': enums.curve.nistP384, + '2b81040023': enums.curve.nistP521, + '2b8104000a': enums.curve.secp256k1, + '2b06010401da470f01': enums.curve.ed25519Legacy, + '2b060104019755010501': enums.curve.curve25519Legacy, + '2b2403030208010107': enums.curve.brainpoolP256r1, + '2b240303020801010b': enums.curve.brainpoolP384r1, + '2b240303020801010d': enums.curve.brainpoolP512r1 +}; + class OID { constructor(oid) { if (oid instanceof OID) { @@ -89,15 +100,16 @@ class OID { /** * If a known curve object identifier, return the canonical name of the curve - * @returns {string} String with the canonical name of the curve. + * @returns {enums.curve} String with the canonical name of the curve + * @throws if unknown */ getName() { - const hex = this.toHex(); - if (enums.curve[hex]) { - return enums.write(enums.curve, hex); - } else { + const name = knownOIDs[this.toHex()]; + if (!name) { throw new Error('Unknown curve object identifier.'); } + + return name; } } diff --git a/src/type/s2k/argon2.js b/src/type/s2k/argon2.js new file mode 100644 index 00000000..84200a6b --- /dev/null +++ b/src/type/s2k/argon2.js @@ -0,0 +1,149 @@ +import defaultConfig from '../../config'; +import enums from '../../enums'; +import util from '../../util'; +import crypto from '../../crypto'; + +const ARGON2_TYPE = 0x02; // id +const ARGON2_VERSION = 0x13; +const ARGON2_SALT_SIZE = 16; + +export class Argon2OutOfMemoryError extends Error { + constructor(...params) { + super(...params); + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, Argon2OutOfMemoryError); + } + + this.name = 'Argon2OutOfMemoryError'; + } +} + +// cache argon wasm module +let loadArgonWasmModule; +let argon2Promise; +// reload wasm module above this treshold, to deallocated used memory +const ARGON2_WASM_MEMORY_THRESHOLD_RELOAD = 2 << 19; + +class Argon2S2K { + /** + * @param {Object} [config] - Full configuration, defaults to openpgp.config + */ + constructor(config = defaultConfig) { + const { passes, parallelism, memoryExponent } = config.s2kArgon2Params; + + this.type = 'argon2'; + /** + * 16 bytes of salt + * @type {Uint8Array} + */ + this.salt = null; + /** + * number of passes + * @type {Integer} + */ + this.t = passes; + /** + * degree of parallelism (lanes) + * @type {Integer} + */ + this.p = parallelism; + /** + * exponent indicating memory size + * @type {Integer} + */ + this.encodedM = memoryExponent; + } + + generateSalt() { + this.salt = crypto.random.getRandomBytes(ARGON2_SALT_SIZE); + } + + /** + * Parsing function for argon2 string-to-key specifier. + * @param {Uint8Array} bytes - Payload of argon2 string-to-key specifier + * @returns {Integer} Actual length of the object. + */ + read(bytes) { + let i = 0; + + this.salt = bytes.subarray(i, i + 16); + i += 16; + + this.t = bytes[i++]; + this.p = bytes[i++]; + this.encodedM = bytes[i++]; // memory size exponent, one-octect + + return i; + } + + /** + * Serializes s2k information + * @returns {Uint8Array} Binary representation of s2k. + */ + write() { + const arr = [ + new Uint8Array([enums.write(enums.s2k, this.type)]), + this.salt, + new Uint8Array([this.t, this.p, this.encodedM]) + ]; + + return util.concatUint8Array(arr); + } + + /** + * Produces a key using the specified passphrase and the defined + * hashAlgorithm + * @param {String} passphrase - Passphrase containing user input + * @returns {Promise} Produced key with a length corresponding to `keySize` + * @throws {Argon2OutOfMemoryError|Errors} + * @async + */ + async produceKey(passphrase, keySize) { + const decodedM = 2 << (this.encodedM - 1); + + try { + // on first load, the argon2 lib is imported and the WASM module is initialized. + // the two steps need to be atomic to avoid race conditions causing multiple wasm modules + // being loaded when `argon2Promise` is not initialized. + loadArgonWasmModule = loadArgonWasmModule || (await import('argon2id')).default; + argon2Promise = argon2Promise || loadArgonWasmModule(); + + // important to keep local ref to argon2 in case the module is reloaded by another instance + const argon2 = await argon2Promise; + + const passwordBytes = util.encodeUTF8(passphrase); + const hash = argon2({ + version: ARGON2_VERSION, + type: ARGON2_TYPE, + password: passwordBytes, + salt: this.salt, + tagLength: keySize, + memorySize: decodedM, + parallelism: this.p, + passes: this.t + }); + + // a lot of memory was used, reload to deallocate + if (decodedM > ARGON2_WASM_MEMORY_THRESHOLD_RELOAD) { + // it will be awaited if needed at the next `produceKey` invocation + argon2Promise = loadArgonWasmModule(); + argon2Promise.catch(() => {}); + } + return hash; + } catch (e) { + if (e.message && ( + e.message.includes('Unable to grow instance memory') || // Chrome + e.message.includes('failed to grow memory') || // Firefox + e.message.includes('WebAssembly.Memory.grow') || // Safari + e.message.includes('Out of memory') // Safari iOS + )) { + throw new Argon2OutOfMemoryError('Could not allocate required memory for Argon2'); + } else { + throw e; + } + } + } +} + +export default Argon2S2K; diff --git a/src/type/s2k.js b/src/type/s2k/generic.js similarity index 92% rename from src/type/s2k.js rename to src/type/s2k/generic.js index 250c7e25..76b81f5b 100644 --- a/src/type/s2k.js +++ b/src/type/s2k/generic.js @@ -25,20 +25,19 @@ * private keyring, and to convert passphrases to encryption keys for * symmetrically encrypted messages. * @module type/s2k - * @private */ -import defaultConfig from '../config'; -import crypto from '../crypto'; -import enums from '../enums'; -import { UnsupportedError } from '../packet/packet'; -import util from '../util'; +import defaultConfig from '../../config'; +import crypto from '../../crypto'; +import enums from '../../enums'; +import { UnsupportedError } from '../../packet/packet'; +import util from '../../util'; -class S2K { +class GenericS2K { /** * @param {Object} [config] - Full configuration, defaults to openpgp.config */ - constructor(config = defaultConfig) { + constructor(s2kType, config = defaultConfig) { /** * Hash function identifier, or 0 for gnu-dummy keys * @type {module:enums.hash | 0} @@ -48,7 +47,7 @@ class S2K { * enums.s2k identifier or 'gnu-dummy' * @type {String} */ - this.type = 'iterated'; + this.type = enums.read(enums.s2k, s2kType); /** @type {Integer} */ this.c = config.s2kIterationCountByte; /** Eight bytes of salt in a binary string. @@ -57,6 +56,14 @@ class S2K { this.salt = null; } + generateSalt() { + switch (this.type) { + case 'salted': + case 'iterated': + this.salt = crypto.random.getRandomBytes(8); + } + } + getCount() { // Exponent bias, defined in RFC4880 const expbias = 6; @@ -71,11 +78,6 @@ class S2K { */ read(bytes) { let i = 0; - try { - this.type = enums.read(enums.s2k, bytes[i++]); - } catch (err) { - throw new UnsupportedError('Unknown S2K type.'); - } this.algorithm = bytes[i++]; switch (this.type) { @@ -196,4 +198,4 @@ class S2K { } } -export default S2K; +export default GenericS2K; diff --git a/src/type/s2k/index.js b/src/type/s2k/index.js new file mode 100644 index 00000000..8a725c18 --- /dev/null +++ b/src/type/s2k/index.js @@ -0,0 +1,46 @@ +import defaultConfig from '../../config'; +import Argon2S2K, { Argon2OutOfMemoryError } from './argon2'; +import GenericS2K from './generic'; +import enums from '../../enums'; +import { UnsupportedError } from '../../packet/packet'; + +const allowedS2KTypesForEncryption = new Set([enums.s2k.argon2, enums.s2k.iterated]); + +/** + * Instantiate a new S2K instance of the given type + * @param {module:enums.s2k} type + * @oaram {Object} [config] + * @returns {Object} New s2k object + * @throws {Error} for unknown or unsupported types + */ +export function newS2KFromType(type, config = defaultConfig) { + switch (type) { + case enums.s2k.argon2: + return new Argon2S2K(config); + case enums.s2k.iterated: + case enums.s2k.gnu: + case enums.s2k.salted: + case enums.s2k.simple: + return new GenericS2K(type, config); + default: + throw new UnsupportedError('Unsupported S2K type'); + } +} + +/** + * Instantiate a new S2K instance based on the config settings + * @oaram {Object} config + * @returns {Object} New s2k object + * @throws {Error} for unknown or unsupported types + */ +export function newS2KFromConfig(config) { + const { s2kType } = config; + + if (!allowedS2KTypesForEncryption.has(s2kType)) { + throw new Error('The provided `config.s2kType` value is not allowed'); + } + + return newS2KFromType(s2kType, config); +} + +export { Argon2OutOfMemoryError }; diff --git a/src/util.js b/src/util.js index c720d596..d13052c0 100644 --- a/src/util.js +++ b/src/util.js @@ -20,12 +20,12 @@ /** * This object contains utility functions * @module util - * @private */ import * as stream from '@openpgp/web-stream-tools'; -import { getBigInteger } from './biginteger'; +import { createRequire } from 'module'; // Must be stripped in browser built import enums from './enums'; +import defaultConfig from './config'; const debugMode = (() => { try { @@ -39,6 +39,8 @@ const util = { return typeof data === 'string' || data instanceof String; }, + nodeRequire: createRequire(import.meta.url), + isArray: function(data) { return data instanceof Array; }, @@ -47,6 +49,35 @@ const util = { isStream: stream.isStream, + /** + * Load noble-curves lib on demand and return the requested curve function + * @param {enums.publicKey} publicKeyAlgo + * @param {enums.curve} [curveName] - for algos supporting different curves (e.g. ECDSA) + * @returns curve implementation + * @throws on unrecognized curve, or curve not implemented by noble-curve + */ + getNobleCurve: async (publicKeyAlgo, curveName) => { + if (!defaultConfig.useEllipticFallback) { + throw new Error('This curve is only supported in the full build of OpenPGP.js'); + } + + const { nobleCurves } = await import('./crypto/public_key/elliptic/noble_curves'); + switch (publicKeyAlgo) { + case enums.publicKey.ecdh: + case enums.publicKey.ecdsa: { + const curve = nobleCurves.get(curveName); + if (!curve) throw new Error('Unsupported curve'); + return curve; + } + case enums.publicKey.x448: + return nobleCurves.get('x448'); + case enums.publicKey.ed448: + return nobleCurves.get('ed448'); + default: + throw new Error('Unsupported curve'); + } + }, + readNumber: function (bytes) { let n = 0; for (let i = 0; i < bytes.length; i++) { @@ -88,7 +119,26 @@ const util = { readMPI: function (bytes) { const bits = (bytes[0] << 8) | bytes[1]; const bytelen = (bits + 7) >>> 3; - return bytes.subarray(2, 2 + bytelen); + // There is a decryption oracle risk here by enforcing the MPI length using `readExactSubarray` in the context of SEIPDv1 encrypted signatures, + // where unauthenticated streamed decryption is done (via `config.allowUnauthenticatedStream`), since the decrypted signature data being processed + // has not been authenticated (yet). + // However, such config setting is known to be insecure, and there are other packet parsing errors that can cause similar issues. + // Also, AEAD is also not affected. + return util.readExactSubarray(bytes, 2, 2 + bytelen); + }, + + /** + * Read exactly `end - start` bytes from input. + * This is a stricter version of `.subarray`. + * @param {Uint8Array} input - Input data to parse + * @returns {Uint8Array} subarray of size always equal to `end - start` + * @throws if the input array is too short. + */ + readExactSubarray: function (input, start, end) { + if (input.length < (end - start)) { + throw new Error('Input array too short'); + } + return input.subarray(start, end); }, /** @@ -98,6 +148,9 @@ const util = { * @returns {Uint8Array} Padded bytes. */ leftPad(bytes, length) { + if (bytes.length > length) { + throw new Error('Input array too long'); + } const padded = new Uint8Array(length); const offset = length - bytes.length; padded.set(bytes, offset); @@ -153,18 +206,10 @@ const util = { * @returns {String} Hexadecimal representation of the array. */ uint8ArrayToHex: function (bytes) { - const r = []; - const e = bytes.length; - let c = 0; - let h; - while (c < e) { - h = bytes[c++].toString(16); - while (h.length < 2) { - h = '0' + h; - } - r.push('' + h); - } - return r.join(''); + const hexAlphabet = '0123456789abcdef'; + let s = ''; + bytes.forEach(v => { s += hexAlphabet[v >> 4] + hexAlphabet[v & 15]; }); + return s; }, /** @@ -375,32 +420,30 @@ const util = { }, /** - * Get native Web Cryptography api, only the current version of the spec. - * @returns {Object} The SubtleCrypto api or 'undefined'. + * Get native Web Cryptography API. + * @returns {Object} The SubtleCrypto API + * @throws if the API is not available */ getWebCrypto: function() { - return typeof globalThis !== 'undefined' && globalThis.crypto && globalThis.crypto.subtle; + const globalWebCrypto = typeof globalThis !== 'undefined' && globalThis.crypto && globalThis.crypto.subtle; + // Fallback for Node 16, which does not expose WebCrypto as a global + const webCrypto = globalWebCrypto || this.getNodeCrypto()?.webcrypto.subtle; + if (!webCrypto) { + throw new Error('The WebCrypto API is not available'); + } + return webCrypto; }, - /** - * Get BigInteger class - * It wraps the native BigInt type if it's available - * Otherwise it relies on bn.js - * @returns {BigInteger} - * @async - */ - getBigInteger, - /** * Get native Node.js crypto api. * @returns {Object} The crypto module or 'undefined'. */ getNodeCrypto: function() { - return require('crypto'); + return this.nodeRequire('crypto'); }, getNodeZlib: function() { - return require('zlib'); + return this.nodeRequire('zlib'); }, /** @@ -409,7 +452,7 @@ const util = { * @returns {Function} The Buffer constructor or 'undefined'. */ getNodeBuffer: function() { - return (require('buffer') || {}).Buffer; + return (this.nodeRequire('buffer') || {}).Buffer; }, getHardwareConcurrency: function() { @@ -417,15 +460,24 @@ const util = { return navigator.hardwareConcurrency || 1; } - const os = require('os'); // Assume we're on Node.js. + const os = this.nodeRequire('os'); // Assume we're on Node.js. return os.cpus().length; }, + /** + * Test email format to ensure basic compliance: + * - must include a single @ + * - no control or space unicode chars allowed + * - no backslash and square brackets (as the latter can mess with the userID parsing) + * - cannot end with a punctuation char + * These checks are not meant to be exhaustive; applications are strongly encouraged to implement stricter validation, + * e.g. based on the W3C HTML spec (https://html.spec.whatwg.org/multipage/input.html#email-state-(type=email)). + */ isEmailAddress: function(data) { if (!util.isString(data)) { return false; } - const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+([a-zA-Z]{2,}[0-9]*|xn--[a-zA-Z\-0-9]+)))$/; + const re = /^[^\p{C}\p{Z}@<>\\]+@[^\p{C}\p{Z}@<>\\]+[^\p{C}\p{Z}\p{P}]$/u; return re.test(data); }, diff --git a/test/benchmarks/memory_usage.js b/test/benchmarks/memory_usage.js index 58166105..50673b67 100644 --- a/test/benchmarks/memory_usage.js +++ b/test/benchmarks/memory_usage.js @@ -1,9 +1,10 @@ /* eslint-disable no-console */ -const assert = require('assert'); -const path = require('path'); -const { writeFileSync, unlinkSync } = require('fs'); -const { fork } = require('child_process'); -const openpgp = require('../..'); +import assert from 'assert'; +import path from 'path'; +import { writeFileSync, unlinkSync } from 'fs'; +import { fork } from 'child_process'; +import { fileURLToPath } from 'url'; +import * as openpgp from 'openpgp'; /** * Benchmark max memory usage recorded during execution of the given function. @@ -12,11 +13,12 @@ const openpgp = require('../..'); * @returns {NodeJS.MemoryUsage} memory usage snapshot with max RSS (sizes in bytes) */ const benchmark = async function(fn) { - const tmpFileName = path.join(__dirname, 'tmp.js'); + const __dirname = path.dirname(fileURLToPath(import.meta.url)); + const tmpFileName = path.join(__dirname, 'tmp.cjs'); // the code to execute must be written to a file writeFileSync(tmpFileName, ` const assert = require('assert'); -const openpgp = require('../..'); +const openpgp = require('openpgp'); let maxMemoryComsumption; let activeSampling = false; @@ -117,6 +119,7 @@ class MemoryBenchamrkSuite { const armoredEncryptedMessage = await openpgp.encrypt({ message: plaintextMessage, passwords, config }); const encryptedMessage = await openpgp.readMessage({ armoredMessage: armoredEncryptedMessage }); assert.ok(encryptedMessage.packets[1] instanceof openpgp.SymEncryptedIntegrityProtectedDataPacket); + assert.ok(encryptedMessage.packets[1].version === 1); await openpgp.decrypt({ message: encryptedMessage, passwords, config }); }); @@ -129,6 +132,7 @@ class MemoryBenchamrkSuite { const armoredEncryptedMessage = await openpgp.encrypt({ message: plaintextMessage, passwords, config }); const encryptedMessage = await openpgp.readMessage({ armoredMessage: armoredEncryptedMessage }); assert.ok(encryptedMessage.packets[1] instanceof openpgp.SymEncryptedIntegrityProtectedDataPacket); + assert.ok(encryptedMessage.packets[1].version === 1); await openpgp.decrypt({ message: encryptedMessage, passwords, config }); }); @@ -140,7 +144,8 @@ class MemoryBenchamrkSuite { const armoredEncryptedMessage = await openpgp.encrypt({ message: plaintextMessage, passwords, config }); const encryptedMessage = await openpgp.readMessage({ armoredMessage: armoredEncryptedMessage }); - assert.ok(encryptedMessage.packets[1] instanceof openpgp.AEADEncryptedDataPacket); + assert.ok(encryptedMessage.packets[1] instanceof openpgp.SymEncryptedIntegrityProtectedDataPacket); + assert.ok(encryptedMessage.packets[1].version === 2); await openpgp.decrypt({ message: encryptedMessage, passwords, config }); }); @@ -152,7 +157,8 @@ class MemoryBenchamrkSuite { const armoredEncryptedMessage = await openpgp.encrypt({ message: plaintextMessage, passwords, config }); const encryptedMessage = await openpgp.readMessage({ armoredMessage: armoredEncryptedMessage }); - assert.ok(encryptedMessage.packets[1] instanceof openpgp.AEADEncryptedDataPacket); + assert.ok(encryptedMessage.packets[1] instanceof openpgp.SymEncryptedIntegrityProtectedDataPacket); + assert.ok(encryptedMessage.packets[1].version === 2); await openpgp.decrypt({ message: encryptedMessage, passwords, config }); }); @@ -167,19 +173,18 @@ class MemoryBenchamrkSuite { const passwords = 'password'; const config = { aeadProtect: false, preferredCompressionAlgorithm: openpgp.enums.compression.uncompressed }; - const inputStream = require('stream').Readable.from(largeDataGenerator({ chunk: new Uint8Array(ONE_MEGABYTE), numberOfChunks: 1 })); + const inputStream = require('stream/web').ReadableStream.from(largeDataGenerator({ chunk: new Uint8Array(ONE_MEGABYTE), numberOfChunks: 1 })); const plaintextMessage = await openpgp.createMessage({ binary: inputStream }); assert(plaintextMessage.fromStream); const armoredEncryptedMessage = await openpgp.encrypt({ message: plaintextMessage, passwords, config }); const encryptedMessage = await openpgp.readMessage({ armoredMessage: armoredEncryptedMessage }); assert.ok(encryptedMessage.packets[1] instanceof openpgp.SymEncryptedIntegrityProtectedDataPacket); + assert.ok(encryptedMessage.packets[1].version === 1); const { data: decryptedData } = await openpgp.decrypt({ message: encryptedMessage, passwords, config }); // read out output stream to trigger decryption - await new Promise(resolve => { - decryptedData.pipe(require('fs').createWriteStream('/dev/null')); - decryptedData.on('end', resolve); - }); + const sink = require('stream').Writable.toWeb(require('fs').createWriteStream('/dev/null')); + await decryptedData.pipeTo(sink); }); suite.add('openpgp.encrypt/decrypt (CFB, text, with streaming)', async () => { @@ -192,19 +197,18 @@ class MemoryBenchamrkSuite { const passwords = 'password'; const config = { aeadProtect: false, preferredCompressionAlgorithm: openpgp.enums.compression.uncompressed }; - const inputStream = require('stream').Readable.from(largeDataGenerator({ chunk: 'a'.repeat(ONE_MEGABYTE / 2), numberOfChunks: 1 })); + const inputStream = require('stream/web').ReadableStream.from(largeDataGenerator({ chunk: 'a'.repeat(ONE_MEGABYTE / 2), numberOfChunks: 1 })); const plaintextMessage = await openpgp.createMessage({ text: inputStream }); assert(plaintextMessage.fromStream); const armoredEncryptedMessage = await openpgp.encrypt({ message: plaintextMessage, passwords, config }); const encryptedMessage = await openpgp.readMessage({ armoredMessage: armoredEncryptedMessage }); assert.ok(encryptedMessage.packets[1] instanceof openpgp.SymEncryptedIntegrityProtectedDataPacket); + assert.ok(encryptedMessage.packets[1].version === 1); const { data: decryptedData } = await openpgp.decrypt({ message: encryptedMessage, passwords, config }); // read out output stream to trigger decryption - await new Promise(resolve => { - decryptedData.pipe(require('fs').createWriteStream('/dev/null')); - decryptedData.on('end', resolve); - }); + const sink = require('stream').Writable.toWeb(require('fs').createWriteStream('/dev/null')); + await decryptedData.pipeTo(sink); }); suite.add('openpgp.encrypt/decrypt (AEAD, binary, with streaming)', async () => { @@ -217,19 +221,18 @@ class MemoryBenchamrkSuite { const passwords = 'password'; const config = { aeadProtect: true, preferredCompressionAlgorithm: openpgp.enums.compression.uncompressed }; - const inputStream = require('stream').Readable.from(largeDataGenerator({ chunk: new Uint8Array(ONE_MEGABYTE), numberOfChunks: 1 })); + const inputStream = require('stream/web').ReadableStream.from(largeDataGenerator({ chunk: new Uint8Array(ONE_MEGABYTE), numberOfChunks: 1 })); const plaintextMessage = await openpgp.createMessage({ binary:inputStream }); assert(plaintextMessage.fromStream); const armoredEncryptedMessage = await openpgp.encrypt({ message: plaintextMessage, passwords, config }); const encryptedMessage = await openpgp.readMessage({ armoredMessage: armoredEncryptedMessage }); - assert.ok(encryptedMessage.packets[1] instanceof openpgp.AEADEncryptedDataPacket); + assert.ok(encryptedMessage.packets[1] instanceof openpgp.SymEncryptedIntegrityProtectedDataPacket); + assert.ok(encryptedMessage.packets[1].version === 2); const { data: decryptedData } = await openpgp.decrypt({ message: encryptedMessage, passwords, config }); // read out output stream to trigger decryption - await new Promise(resolve => { - decryptedData.pipe(require('fs').createWriteStream('/dev/null')); - decryptedData.on('end', resolve); - }); + const sink = require('stream').Writable.toWeb(require('fs').createWriteStream('/dev/null')); + await decryptedData.pipeTo(sink); }); suite.add('openpgp.encrypt/decrypt (AEAD, text, with streaming)', async () => { @@ -242,19 +245,18 @@ class MemoryBenchamrkSuite { const passwords = 'password'; const config = { aeadProtect: true, preferredCompressionAlgorithm: openpgp.enums.compression.uncompressed }; - const inputStream = require('stream').Readable.from(largeDataGenerator({ chunk: 'a'.repeat(ONE_MEGABYTE / 2), numberOfChunks: 1 })); + const inputStream = require('stream/web').ReadableStream.from(largeDataGenerator({ chunk: 'a'.repeat(ONE_MEGABYTE / 2), numberOfChunks: 1 })); const plaintextMessage = await openpgp.createMessage({ text: inputStream }); assert(plaintextMessage.fromStream); const armoredEncryptedMessage = await openpgp.encrypt({ message: plaintextMessage, passwords, config }); const encryptedMessage = await openpgp.readMessage({ armoredMessage: armoredEncryptedMessage }); - assert.ok(encryptedMessage.packets[1] instanceof openpgp.AEADEncryptedDataPacket); + assert.ok(encryptedMessage.packets[1] instanceof openpgp.SymEncryptedIntegrityProtectedDataPacket); + assert.ok(encryptedMessage.packets[1].version === 2); const { data: decryptedData } = await openpgp.decrypt({ message: encryptedMessage, passwords, config }); // read out output stream to trigger decryption - await new Promise(resolve => { - decryptedData.pipe(require('fs').createWriteStream('/dev/null')); - decryptedData.on('end', resolve); - }); + const sink = require('stream').Writable.toWeb(require('fs').createWriteStream('/dev/null')); + await decryptedData.pipeTo(sink); }); suite.add('openpgp.encrypt/decrypt (CFB, text @ 10MB, with streaming)', async () => { @@ -267,19 +269,18 @@ class MemoryBenchamrkSuite { const passwords = 'password'; const config = { aeadProtect: false, preferredCompressionAlgorithm: openpgp.enums.compression.uncompressed }; - const inputStream = require('stream').Readable.from(largeDataGenerator({ chunk: 'a'.repeat(ONE_MEGABYTE / 2), numberOfChunks: 20 })); + const inputStream = require('stream/web').ReadableStream.from(largeDataGenerator({ chunk: 'a'.repeat(ONE_MEGABYTE / 2), numberOfChunks: 20 })); const plaintextMessage = await openpgp.createMessage({ text: inputStream }); assert(plaintextMessage.fromStream); const armoredEncryptedMessage = await openpgp.encrypt({ message: plaintextMessage, passwords, config }); const encryptedMessage = await openpgp.readMessage({ armoredMessage: armoredEncryptedMessage }); assert.ok(encryptedMessage.packets[1] instanceof openpgp.SymEncryptedIntegrityProtectedDataPacket); + assert.ok(encryptedMessage.packets[1].version === 1); const { data: decryptedData } = await openpgp.decrypt({ message: encryptedMessage, passwords, config }); // read out output stream to trigger decryption - await new Promise(resolve => { - decryptedData.pipe(require('fs').createWriteStream('/dev/null')); - decryptedData.on('end', resolve); - }); + const sink = require('stream').Writable.toWeb(require('fs').createWriteStream('/dev/null')); + await decryptedData.pipeTo(sink); }); suite.add('openpgp.encrypt/decrypt (CFB, text @ 10MB, with unauthenticated streaming)', async () => { @@ -292,23 +293,22 @@ class MemoryBenchamrkSuite { const passwords = 'password'; const config = { aeadProtect: false, preferredCompressionAlgorithm: openpgp.enums.compression.uncompressed }; - const inputStream = require('stream').Readable.from(largeDataGenerator({ chunk: 'a'.repeat(ONE_MEGABYTE / 2), numberOfChunks: 20 })); + const inputStream = require('stream/web').ReadableStream.from(largeDataGenerator({ chunk: 'a'.repeat(ONE_MEGABYTE / 2), numberOfChunks: 20 })); const plaintextMessage = await openpgp.createMessage({ text: inputStream }); assert(plaintextMessage.fromStream); const armoredEncryptedMessage = await openpgp.encrypt({ message: plaintextMessage, passwords, config }); const encryptedMessage = await openpgp.readMessage({ armoredMessage: armoredEncryptedMessage }); assert.ok(encryptedMessage.packets[1] instanceof openpgp.SymEncryptedIntegrityProtectedDataPacket); + assert.ok(encryptedMessage.packets[1].version === 1); const { data: decryptedData } = await openpgp.decrypt({ message: encryptedMessage, passwords, config: { ...config, allowUnauthenticatedStream: true } }); // read out output stream to trigger decryption - await new Promise(resolve => { - decryptedData.pipe(require('fs').createWriteStream('/dev/null')); - decryptedData.on('end', resolve); - }); + const sink = require('stream').Writable.toWeb(require('fs').createWriteStream('/dev/null')); + await decryptedData.pipeTo(sink); }); suite.add('openpgp.encrypt/decrypt (AEAD, text @ 10MB, with streaming)', async () => { @@ -321,19 +321,18 @@ class MemoryBenchamrkSuite { const passwords = 'password'; const config = { aeadProtect: true, preferredCompressionAlgorithm: openpgp.enums.compression.uncompressed }; - const inputStream = require('stream').Readable.from(largeDataGenerator({ chunk: 'a'.repeat(ONE_MEGABYTE / 2), numberOfChunks: 20 })); + const inputStream = require('stream/web').ReadableStream.from(largeDataGenerator({ chunk: 'a'.repeat(ONE_MEGABYTE / 2), numberOfChunks: 20 })); const plaintextMessage = await openpgp.createMessage({ text: inputStream }); assert(plaintextMessage.fromStream); const armoredEncryptedMessage = await openpgp.encrypt({ message: plaintextMessage, passwords, config }); const encryptedMessage = await openpgp.readMessage({ armoredMessage: armoredEncryptedMessage }); - assert.ok(encryptedMessage.packets[1] instanceof openpgp.AEADEncryptedDataPacket); + assert.ok(encryptedMessage.packets[1] instanceof openpgp.SymEncryptedIntegrityProtectedDataPacket); + assert.ok(encryptedMessage.packets[1].version === 2); const { data: decryptedData } = await openpgp.decrypt({ message: encryptedMessage, passwords, config }); // read out output stream to trigger decryption - await new Promise(resolve => { - decryptedData.pipe(require('fs').createWriteStream('/dev/null')); - decryptedData.on('end', resolve); - }); + const sink = require('stream').Writable.toWeb(require('fs').createWriteStream('/dev/null')); + await decryptedData.pipeTo(sink); }); const stats = await suite.run(); diff --git a/test/benchmarks/time.js b/test/benchmarks/time.js index e2a32241..24ba752a 100644 --- a/test/benchmarks/time.js +++ b/test/benchmarks/time.js @@ -1,5 +1,6 @@ -const Benchmark = require('benchmark'); -const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../..'); +import Benchmark from 'benchmark'; +import { readToEnd } from '@openpgp/web-stream-tools'; +import * as openpgp from 'openpgp'; const wrapAsync = func => ({ fn: async deferred => { @@ -25,6 +26,22 @@ const onError = err => { (async () => { const suite = new Benchmark.Suite(); const { armoredKey, privateKey, publicKey, armoredEncryptedMessage, armoredSignedMessage } = await getTestData(); + function* largeDataGenerator({ chunk, numberOfChunks }) { + for (let chunkNumber = 0; chunkNumber < numberOfChunks; chunkNumber++) { + yield chunk; + } + } + + const streamFromGenerator = it => new ReadableStream({ + pull: controller => { + const { value, done } = it.next(); + if (done) { + controller.close(); + } else { + controller.enqueue(value); + } + } + }); suite.add('openpgp.readKey', wrapAsync(async () => { await openpgp.readKey({ armoredKey }); @@ -48,6 +65,14 @@ const onError = err => { await openpgp.sign({ message, signingKeys: privateKey }); })); + suite.add('openpgp.sign (stream)', wrapAsync(async () => { + const inputStream = streamFromGenerator(largeDataGenerator({ chunk: new Uint8Array(10000), numberOfChunks: 10 })); + const message = await openpgp.createMessage({ binary: inputStream }); + const signed = await openpgp.sign({ message, signingKeys: privateKey }); + + await readToEnd(signed); + })); + suite.add('openpgp.decrypt', wrapAsync(async () => { const message = await openpgp.readMessage({ armoredMessage: armoredEncryptedMessage }); await openpgp.decrypt({ message, decryptionKeys: privateKey }); diff --git a/test/crypto/aes_kw.js b/test/crypto/aes_kw.js index 130cfc5e..d85fd1f6 100644 --- a/test/crypto/aes_kw.js +++ b/test/crypto/aes_kw.js @@ -1,42 +1,49 @@ -const { expect } = require('chai'); +import { expect } from 'chai'; -const aesKW = require('../../src/crypto/aes_kw'); -const util = require('../../src/util'); +import * as aesKW from '../../src/crypto/aes_kw.js'; +import util from '../../src/util.js'; +import enums from '../../src/enums.js'; -module.exports = () => describe('AES Key Wrap and Unwrap', function () { +export default () => describe('AES Key Wrap and Unwrap', function () { const test_vectors = [ [ '128 bits of Key Data with a 128-bit KEK', + enums.symmetric.aes128, '000102030405060708090A0B0C0D0E0F', '00112233445566778899AABBCCDDEEFF', '1FA68B0A8112B447 AEF34BD8FB5A7B82 9D3E862371D2CFE5' ], [ '128 bits of Key Data with a 192-bit KEK', + enums.symmetric.aes192, '000102030405060708090A0B0C0D0E0F1011121314151617', '00112233445566778899AABBCCDDEEFF', '96778B25AE6CA435 F92B5B97C050AED2 468AB8A17AD84E5D' ], [ '128 bits of Key Data with a 256-bit KEK', + enums.symmetric.aes256, '000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F', '00112233445566778899AABBCCDDEEFF', '64E8C3F9CE0F5BA2 63E9777905818A2A 93C8191E7D6E8AE7' ], [ '192 bits of Key Data with a 192-bit KEK', + enums.symmetric.aes192, '000102030405060708090A0B0C0D0E0F1011121314151617', '00112233445566778899AABBCCDDEEFF0001020304050607', '031D33264E15D332 68F24EC260743EDC E1C6C7DDEE725A93 6BA814915C6762D2' ], [ '192 bits of Key Data with a 256-bit KEK', + enums.symmetric.aes256, '000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F', '00112233445566778899AABBCCDDEEFF0001020304050607', 'A8F9BC1612C68B3F F6E6F4FBE30E71E4 769C8B80A32CB895 8CD5D17D6B254DA1' ], [ '256 bits of Key Data with a 256-bit KEK', + enums.symmetric.aes256, '000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F', '00112233445566778899AABBCCDDEEFF000102030405060708090A0B0C0D0E0F', '28C9F404C4B810F4 CBCCB35CFB87F826 3F5786E2D80ED326 CBC7F0E71A99F43B FB988B9B7A02DD21' @@ -44,15 +51,15 @@ module.exports = () => describe('AES Key Wrap and Unwrap', function () { ]; test_vectors.forEach(function(test) { - it(test[0], function(done) { - const kek = util.hexToUint8Array(test[1]); - const input = test[2].replace(/\s/g, ''); - const input_bin = util.uint8ArrayToString(util.hexToUint8Array(input)); - const output = test[3].replace(/\s/g, ''); - const output_bin = util.uint8ArrayToString(util.hexToUint8Array(output)); - expect(util.uint8ArrayToHex(aesKW.wrap(kek, input_bin)).toUpperCase()).to.equal(output); - expect(util.uint8ArrayToHex(aesKW.unwrap(kek, output_bin)).toUpperCase()).to.equal(input); - done(); + it(test[0], async function() { + const algo = test[1]; + const kek = util.hexToUint8Array(test[2]); + const input = test[3].replace(/\s/g, ''); + const input_bin = util.hexToUint8Array(input); + const output = test[4].replace(/\s/g, ''); + const output_bin = util.hexToUint8Array(output); + expect(util.uint8ArrayToHex(await aesKW.wrap(algo, kek, input_bin)).toUpperCase()).to.equal(output); + expect(util.uint8ArrayToHex(await aesKW.unwrap(algo, kek, output_bin)).toUpperCase()).to.equal(input); }); }); }); diff --git a/test/crypto/biginteger.js b/test/crypto/biginteger.js new file mode 100644 index 00000000..aeabd943 --- /dev/null +++ b/test/crypto/biginteger.js @@ -0,0 +1,96 @@ +import { expect } from 'chai'; + +import BN from 'bn.js'; +import { bigIntToUint8Array, bitLength, byteLength, gcd, getBit, modExp, modInv } from '../../src/crypto/biginteger'; +import { getRandomBytes } from '../../src/crypto/random'; + +async function getRandomBN(min, max) { + if (max.cmp(min) <= 0) { + throw new Error('Illegal parameter value: max <= min'); + } + + const modulus = max.sub(min); + const bytes = modulus.byteLength(); + const r = new BN(getRandomBytes(bytes + 8)); + return r.mod(modulus).add(min); +} + + +export default () => describe('BigInt', () => { + it('bitLength is correct', function() { + const n = BigInt(127); + const expected = 7; + expect(bitLength(n)).to.equal(expected); + expect(bitLength(n + BigInt(1))).to.equal(expected + 1); + }); + + it('byteLength is correct', function() { + const n = BigInt(65535); + const expected = 2; + expect(byteLength(n)).to.equal(expected); + expect(byteLength(n + BigInt(1))).to.equal(expected + 1); + }); + + it('toUint8Array is correct', function() { + const nString = '417653931840771530406225971293556769925351769207235721650257629558293828796031115397206059067934284452829611906818956352854418342467914729341523414945427019410284762464062112274326172407819051167058569790660930309496043254270888417520676082271432948852231332576271876251597199882908964994070268531832274431027'; + const n = BigInt(nString); + const paddedSize = Number(byteLength(n)) + 1; + // big endian, unpadded + let expected = new BN(nString).toArrayLike(Uint8Array); + expect(bigIntToUint8Array(n)).to.deep.equal(expected); + // big endian, padded + expected = new BN(nString).toArrayLike(Uint8Array, 'be', paddedSize); + expect(bigIntToUint8Array(n, 'be', paddedSize)).to.deep.equal(expected); + // little endian, unpadded + expected = new BN(nString).toArrayLike(Uint8Array, 'le'); + expect(bigIntToUint8Array(n, 'le')).to.deep.equal(expected); + //little endian, padded + expected = new BN(nString).toArrayLike(Uint8Array, 'le', paddedSize); + expect(bigIntToUint8Array(n, 'le', paddedSize)).to.deep.equal(expected); + }); + + it('modExp is correct (large values)', function() { + const stringX = '417653931840771530406225971293556769925351769207235721650257629558293828796031115397206059067934284452829611906818956352854418342467914729341523414945427019410284762464062112274326172407819051167058569790660930309496043254270888417520676082271432948852231332576271876251597199882908964994070268531832274431027'; + const stringE = '21139356010872569239159922781526379521587348169074209285187910481667533072168468011617194695181255483288792585413365359733692097084373249198758148704369207793873998901870577262254971784191473102265830193058813215898765238784670469696574407580179153118937858890572095234316482449291777882525949871374961971753'; + const stringN = '129189808515414783602892982235788912674846062846614219472827821758734760420002631653235573915244294540972376140705505703576175711417114803419704967903726436285518767606681184247119430411311152556442947708732584954518890222684529678365388350886907287414896703685680210648760841628375425909680236584021041565183'; + const x = BigInt(stringX); + const e = BigInt(stringE); + const n = BigInt(stringN); + + const got = modExp(x, e, n); + const expected = new BN(stringX).toRed(BN.red(new BN(stringN))).redPow(new BN(stringE)); + // different formats, it's easier to compare strings + expect(got.toString(), expected.toString()); + }); + + it('gcd is correct', async function() { + const aBN = await getRandomBN(new BN(2), new BN(200)); + const bBN = await getRandomBN(new BN(2), new BN(200)); + if (aBN.isEven()) aBN.iaddn(1); + const a = BigInt(aBN.toString()); + const b = BigInt(bBN.toString()); + const expected = aBN.gcd(bBN); + expect(gcd(a, b).toString()).to.equal(expected.toString()); + }); + + it('modular inversion is correct', async function() { + const moduloBN = new BN(229); // this is a prime + const baseBN = await getRandomBN(new BN(2), moduloBN); + const a = BigInt(baseBN.toString()); + const n = BigInt(moduloBN.toString()); + const expected = baseBN.invm(moduloBN); + expect(modInv(a, n).toString()).to.equal(expected.toString()); + // test negative operand + const expectedNegated = baseBN.neg().invm(moduloBN); + expect(modInv(-a, n).toString()).to.equal(expectedNegated.toString()); + expect(() => modInv(a * n, n)).to.throw(/Inverse does not exist/); + }); + + it('getBit is correct', async function() { + const i = 5; + const nBN = await getRandomBN(new BN(2), new BN(200)); + const n = BigInt(nBN.toString()); + const expected = nBN.testn(5) ? 1 : 0; + expect(getBit(n, i)).to.equal(expected); + }); +}); diff --git a/test/crypto/brainpool_rfc7027.js b/test/crypto/brainpool_rfc7027.js new file mode 100644 index 00000000..b28ff9cb --- /dev/null +++ b/test/crypto/brainpool_rfc7027.js @@ -0,0 +1,83 @@ +import { expect } from 'chai'; + +import { brainpoolP256r1 } from '../../src/crypto/public_key/elliptic/brainpool/brainpoolP256r1'; +import { brainpoolP384r1 } from '../../src/crypto/public_key/elliptic/brainpool/brainpoolP384r1'; +import { brainpoolP512r1 } from '../../src/crypto/public_key/elliptic/brainpool/brainpoolP512r1'; +import util from '../../src/util'; + +const rfc7027 = [ + { + 'curve': 'brainpoolP256r1', + 'dA': '81DB1EE100150FF2EA338D708271BE38300CB54241D79950F77B063039804F1D', + 'QAx': '44106E913F92BC02A1705D9953A8414DB95E1AAA49E81D9E85F929A8E3100BE5', + 'QAy': '8AB4846F11CACCB73CE49CBDD120F5A900A69FD32C272223F789EF10EB089BDC', + 'dB': '55E40BC41E37E3E2AD25C3C6654511FFA8474A91A0032087593852D3E7D76BD3', + 'QBx': '8D2D688C6CF93E1160AD04CC4429117DC2C41825E1E9FCA0ADDD34E6F1B39F7B', + 'QBy': '990C57520812BE512641E47034832106BC7D3E8DD0E4C7F1136D7006547CEC6A', + 'Zx': '89AFC39D41D3B327814B80940B042590F96556EC91E6AE7939BCE31F3A18BF2B', + 'Zy': '49C27868F4ECA2179BFD7D59B1E3BF34C1DBDE61AE12931648F43E59632504DE' + }, + { + 'curve': 'brainpoolP384r1', + 'dA': '1E20F5E048A5886F1F157C74E91BDE2B98C8B52D58E5003D57053FC4B0BD65D6F15EB5D1EE1610DF870795143627D042', + 'QAx': '68B665DD91C195800650CDD363C625F4E742E8134667B767B1B476793588F885AB698C852D4A6E77A252D6380FCAF068', + 'QAy': '55BC91A39C9EC01DEE36017B7D673A931236D2F1F5C83942D049E3FA20607493E0D038FF2FD30C2AB67D15C85F7FAA59', + 'dB': '032640BC6003C59260F7250C3DB58CE647F98E1260ACCE4ACDA3DD869F74E01F8BA5E0324309DB6A9831497ABAC96670', + 'QBx': '4D44326F269A597A5B58BBA565DA5556ED7FD9A8A9EB76C25F46DB69D19DC8CE6AD18E404B15738B2086DF37E71D1EB4', + 'QBy': '62D692136DE56CBE93BF5FA3188EF58BC8A3A0EC6C1E151A21038A42E9185329B5B275903D192F8D4E1F32FE9CC78C48', + 'Zx': '0BD9D3A7EA0B3D519D09D8E48D0785FB744A6B355E6304BC51C229FBBCE239BBADF6403715C35D4FB2A5444F575D4F42', + 'Zy': '0DF213417EBE4D8E40A5F76F66C56470C489A3478D146DECF6DF0D94BAE9E598157290F8756066975F1DB34B2324B7BD' + }, + { + 'curve': 'brainpoolP512r1', + 'dA': '16302FF0DBBB5A8D733DAB7141C1B45ACBC8715939677F6A56850A38BD87BD59B09E80279609FF333EB9D4C061231FB26F92EEB04982A5F1D1764CAD57665422', + 'QAx': '0A420517E406AAC0ACDCE90FCD71487718D3B953EFD7FBEC5F7F27E28C6149999397E91E029E06457DB2D3E640668B392C2A7E737A7F0BF04436D11640FD09FD', + 'QAy': '72E6882E8DB28AAD36237CD25D580DB23783961C8DC52DFA2EC138AD472A0FCEF3887CF62B623B2A87DE5C588301EA3E5FC269B373B60724F5E82A6AD147FDE7', + 'dB': '230E18E1BCC88A362FA54E4EA3902009292F7F8033624FD471B5D8ACE49D12CFABBC19963DAB8E2F1EBA00BFFB29E4D72D13F2224562F405CB80503666B25429', + 'QBx': '9D45F66DE5D67E2E6DB6E93A59CE0BB48106097FF78A081DE781CDB31FCE8CCBAAEA8DD4320C4119F1E9CD437A2EAB3731FA9668AB268D871DEDA55A5473199F', + 'QBy': '2FDC313095BCDD5FB3A91636F07A959C8E86B5636A1E930E8396049CB481961D365CC11453A06C719835475B12CB52FC3C383BCE35E27EF194512B71876285FA', + 'Zx': 'A7927098655F1F9976FA50A9D566865DC530331846381C87256BAF3226244B76D36403C024D7BBF0AA0803EAFF405D3D24F11A9B5C0BEF679FE1454B21C4CD1F', + 'Zy': '7DB71C3DEF63212841C463E881BDCF055523BD368240E6C3143BD8DEF8B3B3223B95E0F53082FF5E412F4222537A43DF1C6D25729DDB51620A832BE6A26680A2' + } +]; + +const hexToBigint = hex => BigInt(`0x${hex}`); + +// prettier-ignore +const BRAINPOOL = { + brainpoolP256r1, + brainpoolP384r1, + brainpoolP512r1 +}; + +export default () => describe('Brainpool curves (RFC7027)', () => { + it('Field orders', () => { + const vectors = { + brainpoolP256r1: BigInt('0xa9fb57dba1eea9bc3e660a909d838d726e3bf623d52620282013481d1f6e5377'), + brainpoolP384r1: BigInt('0x8cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b412b1da197fb71123acd3a729901d1a71874700133107ec53'), + brainpoolP512r1: BigInt('0xaadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca703308717d4d9b009bc66842aecda12ae6a380e62881ff2f2d82c68528aa6056583a48f3') + }; + for (const n of Object.keys(vectors)) { expect(BRAINPOOL[n].CURVE.Fp.ORDER).to.deep.equal(vectors[n]); } + }); + + for (const v of rfc7027) { + it(v.curve, () => { + const curve = BRAINPOOL[v.curve]; + const secKeyA = util.hexToUint8Array(v.dA); + const pubKeyA = curve.getPublicKey(secKeyA); + const pubPointA = curve.ProjectivePoint.fromHex(pubKeyA); + expect(pubPointA.x).to.equal(hexToBigint(v.QAx)); + expect(pubPointA.y).to.equal(hexToBigint(v.QAy)); + const secKeyB = hexToBigint(v.dB); + const pubKeyB = curve.getPublicKey(secKeyB); + const pubPointB = curve.ProjectivePoint.fromHex(pubKeyB); + expect(pubPointB.x).to.equal(hexToBigint(v.QBx)); + expect(pubPointB.y).to.equal(hexToBigint(v.QBy)); + const shared = curve.getSharedSecret(secKeyA, pubKeyB); + const sharedPoint = curve.ProjectivePoint.fromHex(shared); + expect(sharedPoint.x).to.equal(hexToBigint(v.Zx)); + expect(sharedPoint.y).to.equal(hexToBigint(v.Zy)); + expect(shared).to.deep.equal(curve.getSharedSecret(secKeyB, pubKeyA)); + }); + } +}); diff --git a/test/crypto/cipher/aes.js b/test/crypto/cipher/aes.js deleted file mode 100644 index 798e8662..00000000 --- a/test/crypto/cipher/aes.js +++ /dev/null @@ -1,84 +0,0 @@ -const { expect } = require('chai'); - -const { aes128: AES128 } = require('../../../src/crypto/cipher'); - -module.exports = () => describe('AES Rijndael cipher test with test vectors from ecb_tbl.txt', function() { - function test_aes(input, key, output) { - const aes = new AES128(new Uint8Array(key)); - - const encrypted = aes.encrypt(new Uint8Array(input)); - expect(encrypted).to.deep.equal(new Uint8Array(output)); - - const decrypted = aes.decrypt(new Uint8Array(output)); - expect(decrypted).to.deep.equal(new Uint8Array(input)); - } - - const testvectors128 = [[[0x00,0x01,0x02,0x03,0x05,0x06,0x07,0x08,0x0A,0x0B,0x0C,0x0D,0x0F,0x10,0x11,0x12],[0x50,0x68,0x12,0xA4,0x5F,0x08,0xC8,0x89,0xB9,0x7F,0x59,0x80,0x03,0x8B,0x83,0x59],[0xD8,0xF5,0x32,0x53,0x82,0x89,0xEF,0x7D,0x06,0xB5,0x06,0xA4,0xFD,0x5B,0xE9,0xC9]], - [[0x14,0x15,0x16,0x17,0x19,0x1A,0x1B,0x1C,0x1E,0x1F,0x20,0x21,0x23,0x24,0x25,0x26],[0x5C,0x6D,0x71,0xCA,0x30,0xDE,0x8B,0x8B,0x00,0x54,0x99,0x84,0xD2,0xEC,0x7D,0x4B],[0x59,0xAB,0x30,0xF4,0xD4,0xEE,0x6E,0x4F,0xF9,0x90,0x7E,0xF6,0x5B,0x1F,0xB6,0x8C]], - [[0x28,0x29,0x2A,0x2B,0x2D,0x2E,0x2F,0x30,0x32,0x33,0x34,0x35,0x37,0x38,0x39,0x3A],[0x53,0xF3,0xF4,0xC6,0x4F,0x86,0x16,0xE4,0xE7,0xC5,0x61,0x99,0xF4,0x8F,0x21,0xF6],[0xBF,0x1E,0xD2,0xFC,0xB2,0xAF,0x3F,0xD4,0x14,0x43,0xB5,0x6D,0x85,0x02,0x5C,0xB1]], - [[0x3C,0x3D,0x3E,0x3F,0x41,0x42,0x43,0x44,0x46,0x47,0x48,0x49,0x4B,0x4C,0x4D,0x4E],[0xA1,0xEB,0x65,0xA3,0x48,0x71,0x65,0xFB,0x0F,0x1C,0x27,0xFF,0x99,0x59,0xF7,0x03],[0x73,0x16,0x63,0x2D,0x5C,0x32,0x23,0x3E,0xDC,0xB0,0x78,0x05,0x60,0xEA,0xE8,0xB2]], - [[0x50,0x51,0x52,0x53,0x55,0x56,0x57,0x58,0x5A,0x5B,0x5C,0x5D,0x5F,0x60,0x61,0x62],[0x35,0x53,0xEC,0xF0,0xB1,0x73,0x95,0x58,0xB0,0x8E,0x35,0x0A,0x98,0xA3,0x9B,0xFA],[0x40,0x8C,0x07,0x3E,0x3E,0x25,0x38,0x07,0x2B,0x72,0x62,0x5E,0x68,0xB8,0x36,0x4B]], - [[0x64,0x65,0x66,0x67,0x69,0x6A,0x6B,0x6C,0x6E,0x6F,0x70,0x71,0x73,0x74,0x75,0x76],[0x67,0x42,0x99,0x69,0x49,0x0B,0x97,0x11,0xAE,0x2B,0x01,0xDC,0x49,0x7A,0xFD,0xE8],[0xE1,0xF9,0x4D,0xFA,0x77,0x65,0x97,0xBE,0xAC,0xA2,0x62,0xF2,0xF6,0x36,0x6F,0xEA]], - [[0x78,0x79,0x7A,0x7B,0x7D,0x7E,0x7F,0x80,0x82,0x83,0x84,0x85,0x87,0x88,0x89,0x8A],[0x93,0x38,0x5C,0x1F,0x2A,0xEC,0x8B,0xED,0x19,0x2F,0x5A,0x8E,0x16,0x1D,0xD5,0x08],[0xF2,0x9E,0x98,0x6C,0x6A,0x1C,0x27,0xD7,0xB2,0x9F,0xFD,0x7E,0xE9,0x2B,0x75,0xF1]], - [[0x8C,0x8D,0x8E,0x8F,0x91,0x92,0x93,0x94,0x96,0x97,0x98,0x99,0x9B,0x9C,0x9D,0x9E],[0xB5,0xBF,0x94,0x6B,0xE1,0x9B,0xEB,0x8D,0xB3,0x98,0x3B,0x5F,0x4C,0x6E,0x8D,0xDB],[0x13,0x1C,0x88,0x6A,0x57,0xF8,0xC2,0xE7,0x13,0xAB,0xA6,0x95,0x5E,0x2B,0x55,0xB5]], - [[0xA0,0xA1,0xA2,0xA3,0xA5,0xA6,0xA7,0xA8,0xAA,0xAB,0xAC,0xAD,0xAF,0xB0,0xB1,0xB2],[0x41,0x32,0x1E,0xE1,0x0E,0x21,0xBD,0x90,0x72,0x27,0xC4,0x45,0x0F,0xF4,0x23,0x24],[0xD2,0xAB,0x76,0x62,0xDF,0x9B,0x8C,0x74,0x02,0x10,0xE5,0xEE,0xB6,0x1C,0x19,0x9D]], - [[0xB4,0xB5,0xB6,0xB7,0xB9,0xBA,0xBB,0xBC,0xBE,0xBF,0xC0,0xC1,0xC3,0xC4,0xC5,0xC6],[0x00,0xA8,0x2F,0x59,0xC9,0x1C,0x84,0x86,0xD1,0x2C,0x0A,0x80,0x12,0x4F,0x60,0x89],[0x14,0xC1,0x05,0x54,0xB2,0x85,0x9C,0x48,0x4C,0xAB,0x58,0x69,0xBB,0xE7,0xC4,0x70]], - [[0xC8,0xC9,0xCA,0xCB,0xCD,0xCE,0xCF,0xD0,0xD2,0xD3,0xD4,0xD5,0xD7,0xD8,0xD9,0xDA],[0x7C,0xE0,0xFD,0x07,0x67,0x54,0x69,0x1B,0x4B,0xBD,0x9F,0xAF,0x8A,0x13,0x72,0xFE],[0xDB,0x4D,0x49,0x8F,0x0A,0x49,0xCF,0x55,0x44,0x5D,0x50,0x2C,0x1F,0x9A,0xB3,0xB5]], - [[0xDC,0xDD,0xDE,0xDF,0xE1,0xE2,0xE3,0xE4,0xE6,0xE7,0xE8,0xE9,0xEB,0xEC,0xED,0xEE],[0x23,0x60,0x5A,0x82,0x43,0xD0,0x77,0x64,0x54,0x1B,0xC5,0xAD,0x35,0x5B,0x31,0x29],[0x6D,0x96,0xFE,0xF7,0xD6,0x65,0x90,0xA7,0x7A,0x77,0xBB,0x20,0x56,0x66,0x7F,0x7F]], - [[0xF0,0xF1,0xF2,0xF3,0xF5,0xF6,0xF7,0xF8,0xFA,0xFB,0xFC,0xFD,0xFE,0x01,0x00,0x02],[0x12,0xA8,0xCF,0xA2,0x3E,0xA7,0x64,0xFD,0x87,0x62,0x32,0xB4,0xE8,0x42,0xBC,0x44],[0x31,0x6F,0xB6,0x8E,0xDB,0xA7,0x36,0xC5,0x3E,0x78,0x47,0x7B,0xF9,0x13,0x72,0x5C]], - [[0x04,0x05,0x06,0x07,0x09,0x0A,0x0B,0x0C,0x0E,0x0F,0x10,0x11,0x13,0x14,0x15,0x16],[0xBC,0xAF,0x32,0x41,0x5E,0x83,0x08,0xB3,0x72,0x3E,0x5F,0xDD,0x85,0x3C,0xCC,0x80],[0x69,0x36,0xF2,0xB9,0x3A,0xF8,0x39,0x7F,0xD3,0xA7,0x71,0xFC,0x01,0x1C,0x8C,0x37]], - [[0x2C,0x2D,0x2E,0x2F,0x31,0x32,0x33,0x34,0x36,0x37,0x38,0x39,0x3B,0x3C,0x3D,0x3E],[0x89,0xAF,0xAE,0x68,0x5D,0x80,0x1A,0xD7,0x47,0xAC,0xE9,0x1F,0xC4,0x9A,0xDD,0xE0],[0xF3,0xF9,0x2F,0x7A,0x9C,0x59,0x17,0x9C,0x1F,0xCC,0x2C,0x2B,0xA0,0xB0,0x82,0xCD]]]; - - const testvectors192 = [[[0x00,0x01,0x02,0x03,0x05,0x06,0x07,0x08,0x0A,0x0B,0x0C,0x0D,0x0F,0x10,0x11,0x12,0x14,0x15,0x16,0x17,0x19,0x1A,0x1B,0x1C],[0x2D,0x33,0xEE,0xF2,0xC0,0x43,0x0A,0x8A,0x9E,0xBF,0x45,0xE8,0x09,0xC4,0x0B,0xB6],[0xDF,0xF4,0x94,0x5E,0x03,0x36,0xDF,0x4C,0x1C,0x56,0xBC,0x70,0x0E,0xFF,0x83,0x7F]], - [[0x1E,0x1F,0x20,0x21,0x23,0x24,0x25,0x26,0x28,0x29,0x2A,0x2B,0x2D,0x2E,0x2F,0x30,0x32,0x33,0x34,0x35,0x37,0x38,0x39,0x3A],[0x6A,0xA3,0x75,0xD1,0xFA,0x15,0x5A,0x61,0xFB,0x72,0x35,0x3E,0x0A,0x5A,0x87,0x56],[0xB6,0xFD,0xDE,0xF4,0x75,0x27,0x65,0xE3,0x47,0xD5,0xD2,0xDC,0x19,0x6D,0x12,0x52]], - [[0x3C,0x3D,0x3E,0x3F,0x41,0x42,0x43,0x44,0x46,0x47,0x48,0x49,0x4B,0x4C,0x4D,0x4E,0x50,0x51,0x52,0x53,0x55,0x56,0x57,0x58],[0xBC,0x37,0x36,0x51,0x8B,0x94,0x90,0xDC,0xB8,0xED,0x60,0xEB,0x26,0x75,0x8E,0xD4],[0xD2,0x36,0x84,0xE3,0xD9,0x63,0xB3,0xAF,0xCF,0x1A,0x11,0x4A,0xCA,0x90,0xCB,0xD6]], - [[0x5A,0x5B,0x5C,0x5D,0x5F,0x60,0x61,0x62,0x64,0x65,0x66,0x67,0x69,0x6A,0x6B,0x6C,0x6E,0x6F,0x70,0x71,0x73,0x74,0x75,0x76],[0xAA,0x21,0x44,0x02,0xB4,0x6C,0xFF,0xB9,0xF7,0x61,0xEC,0x11,0x26,0x3A,0x31,0x1E],[0x3A,0x7A,0xC0,0x27,0x75,0x3E,0x2A,0x18,0xC2,0xCE,0xAB,0x9E,0x17,0xC1,0x1F,0xD0]], - [[0x78,0x79,0x7A,0x7B,0x7D,0x7E,0x7F,0x80,0x82,0x83,0x84,0x85,0x87,0x88,0x89,0x8A,0x8C,0x8D,0x8E,0x8F,0x91,0x92,0x93,0x94],[0x02,0xAE,0xA8,0x6E,0x57,0x2E,0xEA,0xB6,0x6B,0x2C,0x3A,0xF5,0xE9,0xA4,0x6F,0xD6],[0x8F,0x67,0x86,0xBD,0x00,0x75,0x28,0xBA,0x26,0x60,0x3C,0x16,0x01,0xCD,0xD0,0xD8]], - [[0x96,0x97,0x98,0x99,0x9B,0x9C,0x9D,0x9E,0xA0,0xA1,0xA2,0xA3,0xA5,0xA6,0xA7,0xA8,0xAA,0xAB,0xAC,0xAD,0xAF,0xB0,0xB1,0xB2],[0xE2,0xAE,0xF6,0xAC,0xC3,0x3B,0x96,0x5C,0x4F,0xA1,0xF9,0x1C,0x75,0xFF,0x6F,0x36],[0xD1,0x7D,0x07,0x3B,0x01,0xE7,0x15,0x02,0xE2,0x8B,0x47,0xAB,0x55,0x11,0x68,0xB3]], - [[0xB4,0xB5,0xB6,0xB7,0xB9,0xBA,0xBB,0xBC,0xBE,0xBF,0xC0,0xC1,0xC3,0xC4,0xC5,0xC6,0xC8,0xC9,0xCA,0xCB,0xCD,0xCE,0xCF,0xD0],[0x06,0x59,0xDF,0x46,0x42,0x71,0x62,0xB9,0x43,0x48,0x65,0xDD,0x94,0x99,0xF9,0x1D],[0xA4,0x69,0xDA,0x51,0x71,0x19,0xFA,0xB9,0x58,0x76,0xF4,0x1D,0x06,0xD4,0x0F,0xFA]], - [[0xD2,0xD3,0xD4,0xD5,0xD7,0xD8,0xD9,0xDA,0xDC,0xDD,0xDE,0xDF,0xE1,0xE2,0xE3,0xE4,0xE6,0xE7,0xE8,0xE9,0xEB,0xEC,0xED,0xEE],[0x49,0xA4,0x42,0x39,0xC7,0x48,0xFE,0xB4,0x56,0xF5,0x9C,0x27,0x6A,0x56,0x58,0xDF],[0x60,0x91,0xAA,0x3B,0x69,0x5C,0x11,0xF5,0xC0,0xB6,0xAD,0x26,0xD3,0xD8,0x62,0xFF]], - [[0xF0,0xF1,0xF2,0xF3,0xF5,0xF6,0xF7,0xF8,0xFA,0xFB,0xFC,0xFD,0xFE,0x01,0x00,0x02,0x04,0x05,0x06,0x07,0x09,0x0A,0x0B,0x0C],[0x66,0x20,0x8F,0x6E,0x9D,0x04,0x52,0x5B,0xDE,0xDB,0x27,0x33,0xB6,0xA6,0xBE,0x37],[0x70,0xF9,0xE6,0x7F,0x9F,0x8D,0xF1,0x29,0x41,0x31,0x66,0x2D,0xC6,0xE6,0x93,0x64]], - [[0x0E,0x0F,0x10,0x11,0x13,0x14,0x15,0x16,0x18,0x19,0x1A,0x1B,0x1D,0x1E,0x1F,0x20,0x22,0x23,0x24,0x25,0x27,0x28,0x29,0x2A],[0x33,0x93,0xF8,0xDF,0xC7,0x29,0xC9,0x7F,0x54,0x80,0xB9,0x50,0xBC,0x96,0x66,0xB0],[0xD1,0x54,0xDC,0xAF,0xAD,0x8B,0x20,0x7F,0xA5,0xCB,0xC9,0x5E,0x99,0x96,0xB5,0x59]], - [[0x2C,0x2D,0x2E,0x2F,0x31,0x32,0x33,0x34,0x36,0x37,0x38,0x39,0x3B,0x3C,0x3D,0x3E,0x40,0x41,0x42,0x43,0x45,0x46,0x47,0x48],[0x60,0x68,0x34,0xC8,0xCE,0x06,0x3F,0x32,0x34,0xCF,0x11,0x45,0x32,0x5D,0xBD,0x71],[0x49,0x34,0xD5,0x41,0xE8,0xB4,0x6F,0xA3,0x39,0xC8,0x05,0xA7,0xAE,0xB9,0xE5,0xDA]], - [[0x4A,0x4B,0x4C,0x4D,0x4F,0x50,0x51,0x52,0x54,0x55,0x56,0x57,0x59,0x5A,0x5B,0x5C,0x5E,0x5F,0x60,0x61,0x63,0x64,0x65,0x66],[0xFE,0xC1,0xC0,0x4F,0x52,0x9B,0xBD,0x17,0xD8,0xCE,0xCF,0xCC,0x47,0x18,0xB1,0x7F],[0x62,0x56,0x4C,0x73,0x8F,0x3E,0xFE,0x18,0x6E,0x1A,0x12,0x7A,0x0C,0x4D,0x3C,0x61]], - [[0x68,0x69,0x6A,0x6B,0x6D,0x6E,0x6F,0x70,0x72,0x73,0x74,0x75,0x77,0x78,0x79,0x7A,0x7C,0x7D,0x7E,0x7F,0x81,0x82,0x83,0x84],[0x32,0xDF,0x99,0xB4,0x31,0xED,0x5D,0xC5,0xAC,0xF8,0xCA,0xF6,0xDC,0x6C,0xE4,0x75],[0x07,0x80,0x5A,0xA0,0x43,0x98,0x6E,0xB2,0x36,0x93,0xE2,0x3B,0xEF,0x8F,0x34,0x38]], - [[0x86,0x87,0x88,0x89,0x8B,0x8C,0x8D,0x8E,0x90,0x91,0x92,0x93,0x95,0x96,0x97,0x98,0x9A,0x9B,0x9C,0x9D,0x9F,0xA0,0xA1,0xA2],[0x7F,0xDC,0x2B,0x74,0x6F,0x3F,0x66,0x52,0x96,0x94,0x3B,0x83,0x71,0x0D,0x1F,0x82],[0xDF,0x0B,0x49,0x31,0x03,0x8B,0xAD,0xE8,0x48,0xDE,0xE3,0xB4,0xB8,0x5A,0xA4,0x4B]], - [[0xA4,0xA5,0xA6,0xA7,0xA9,0xAA,0xAB,0xAC,0xAE,0xAF,0xB0,0xB1,0xB3,0xB4,0xB5,0xB6,0xB8,0xB9,0xBA,0xBB,0xBD,0xBE,0xBF,0xC0],[0x8F,0xBA,0x15,0x10,0xA3,0xC5,0xB8,0x7E,0x2E,0xAA,0x3F,0x7A,0x91,0x45,0x5C,0xA2],[0x59,0x2D,0x5F,0xDE,0xD7,0x65,0x82,0xE4,0x14,0x3C,0x65,0x09,0x93,0x09,0x47,0x7C]]]; - - const testvectors256 = [[[0x00,0x01,0x02,0x03,0x05,0x06,0x07,0x08,0x0A,0x0B,0x0C,0x0D,0x0F,0x10,0x11,0x12,0x14,0x15,0x16,0x17,0x19,0x1A,0x1B,0x1C,0x1E,0x1F,0x20,0x21,0x23,0x24,0x25,0x26],[0x83,0x4E,0xAD,0xFC,0xCA,0xC7,0xE1,0xB3,0x06,0x64,0xB1,0xAB,0xA4,0x48,0x15,0xAB],[0x19,0x46,0xDA,0xBF,0x6A,0x03,0xA2,0xA2,0xC3,0xD0,0xB0,0x50,0x80,0xAE,0xD6,0xFC]], - [[0x28,0x29,0x2A,0x2B,0x2D,0x2E,0x2F,0x30,0x32,0x33,0x34,0x35,0x37,0x38,0x39,0x3A,0x3C,0x3D,0x3E,0x3F,0x41,0x42,0x43,0x44,0x46,0x47,0x48,0x49,0x4B,0x4C,0x4D,0x4E],[0xD9,0xDC,0x4D,0xBA,0x30,0x21,0xB0,0x5D,0x67,0xC0,0x51,0x8F,0x72,0xB6,0x2B,0xF1],[0x5E,0xD3,0x01,0xD7,0x47,0xD3,0xCC,0x71,0x54,0x45,0xEB,0xDE,0xC6,0x2F,0x2F,0xB4]], - [[0x50,0x51,0x52,0x53,0x55,0x56,0x57,0x58,0x5A,0x5B,0x5C,0x5D,0x5F,0x60,0x61,0x62,0x64,0x65,0x66,0x67,0x69,0x6A,0x6B,0x6C,0x6E,0x6F,0x70,0x71,0x73,0x74,0x75,0x76],[0xA2,0x91,0xD8,0x63,0x01,0xA4,0xA7,0x39,0xF7,0x39,0x21,0x73,0xAA,0x3C,0x60,0x4C],[0x65,0x85,0xC8,0xF4,0x3D,0x13,0xA6,0xBE,0xAB,0x64,0x19,0xFC,0x59,0x35,0xB9,0xD0]], - [[0x78,0x79,0x7A,0x7B,0x7D,0x7E,0x7F,0x80,0x82,0x83,0x84,0x85,0x87,0x88,0x89,0x8A,0x8C,0x8D,0x8E,0x8F,0x91,0x92,0x93,0x94,0x96,0x97,0x98,0x99,0x9B,0x9C,0x9D,0x9E],[0x42,0x64,0xB2,0x69,0x64,0x98,0xDE,0x4D,0xF7,0x97,0x88,0xA9,0xF8,0x3E,0x93,0x90],[0x2A,0x5B,0x56,0xA5,0x96,0x68,0x0F,0xCC,0x0E,0x05,0xF5,0xE0,0xF1,0x51,0xEC,0xAE]], - [[0xA0,0xA1,0xA2,0xA3,0xA5,0xA6,0xA7,0xA8,0xAA,0xAB,0xAC,0xAD,0xAF,0xB0,0xB1,0xB2,0xB4,0xB5,0xB6,0xB7,0xB9,0xBA,0xBB,0xBC,0xBE,0xBF,0xC0,0xC1,0xC3,0xC4,0xC5,0xC6],[0xEE,0x99,0x32,0xB3,0x72,0x18,0x04,0xD5,0xA8,0x3E,0xF5,0x94,0x92,0x45,0xB6,0xF6],[0xF5,0xD6,0xFF,0x41,0x4F,0xD2,0xC6,0x18,0x14,0x94,0xD2,0x0C,0x37,0xF2,0xB8,0xC4]], - [[0xC8,0xC9,0xCA,0xCB,0xCD,0xCE,0xCF,0xD0,0xD2,0xD3,0xD4,0xD5,0xD7,0xD8,0xD9,0xDA,0xDC,0xDD,0xDE,0xDF,0xE1,0xE2,0xE3,0xE4,0xE6,0xE7,0xE8,0xE9,0xEB,0xEC,0xED,0xEE],[0xE6,0x24,0x8F,0x55,0xC5,0xFD,0xCB,0xCA,0x9C,0xBB,0xB0,0x1C,0x88,0xA2,0xEA,0x77],[0x85,0x39,0x9C,0x01,0xF5,0x9F,0xFF,0xB5,0x20,0x4F,0x19,0xF8,0x48,0x2F,0x00,0xB8]], - [[0xF0,0xF1,0xF2,0xF3,0xF5,0xF6,0xF7,0xF8,0xFA,0xFB,0xFC,0xFD,0xFE,0x01,0x00,0x02,0x04,0x05,0x06,0x07,0x09,0x0A,0x0B,0x0C,0x0E,0x0F,0x10,0x11,0x13,0x14,0x15,0x16],[0xB8,0x35,0x8E,0x41,0xB9,0xDF,0xF6,0x5F,0xD4,0x61,0xD5,0x5A,0x99,0x26,0x62,0x47],[0x92,0x09,0x7B,0x4C,0x88,0xA0,0x41,0xDD,0xF9,0x81,0x44,0xBC,0x8D,0x22,0xE8,0xE7]], - [[0x18,0x19,0x1A,0x1B,0x1D,0x1E,0x1F,0x20,0x22,0x23,0x24,0x25,0x27,0x28,0x29,0x2A,0x2C,0x2D,0x2E,0x2F,0x31,0x32,0x33,0x34,0x36,0x37,0x38,0x39,0x3B,0x3C,0x3D,0x3E],[0xF0,0xE2,0xD7,0x22,0x60,0xAF,0x58,0xE2,0x1E,0x01,0x5A,0xB3,0xA4,0xC0,0xD9,0x06],[0x89,0xBD,0x5B,0x73,0xB3,0x56,0xAB,0x41,0x2A,0xEF,0x9F,0x76,0xCE,0xA2,0xD6,0x5C]], - [[0x40,0x41,0x42,0x43,0x45,0x46,0x47,0x48,0x4A,0x4B,0x4C,0x4D,0x4F,0x50,0x51,0x52,0x54,0x55,0x56,0x57,0x59,0x5A,0x5B,0x5C,0x5E,0x5F,0x60,0x61,0x63,0x64,0x65,0x66],[0x47,0x5B,0x8B,0x82,0x3C,0xE8,0x89,0x3D,0xB3,0xC4,0x4A,0x9F,0x2A,0x37,0x9F,0xF7],[0x25,0x36,0x96,0x90,0x93,0xC5,0x5F,0xF9,0x45,0x46,0x92,0xF2,0xFA,0xC2,0xF5,0x30]], - [[0x68,0x69,0x6A,0x6B,0x6D,0x6E,0x6F,0x70,0x72,0x73,0x74,0x75,0x77,0x78,0x79,0x7A,0x7C,0x7D,0x7E,0x7F,0x81,0x82,0x83,0x84,0x86,0x87,0x88,0x89,0x8B,0x8C,0x8D,0x8E],[0x68,0x8F,0x52,0x81,0x94,0x58,0x12,0x86,0x2F,0x5F,0x30,0x76,0xCF,0x80,0x41,0x2F],[0x07,0xFC,0x76,0xA8,0x72,0x84,0x3F,0x3F,0x6E,0x00,0x81,0xEE,0x93,0x96,0xD6,0x37]], - [[0x90,0x91,0x92,0x93,0x95,0x96,0x97,0x98,0x9A,0x9B,0x9C,0x9D,0x9F,0xA0,0xA1,0xA2,0xA4,0xA5,0xA6,0xA7,0xA9,0xAA,0xAB,0xAC,0xAE,0xAF,0xB0,0xB1,0xB3,0xB4,0xB5,0xB6],[0x08,0xD1,0xD2,0xBC,0x75,0x0A,0xF5,0x53,0x36,0x5D,0x35,0xE7,0x5A,0xFA,0xCE,0xAA],[0xE3,0x8B,0xA8,0xEC,0x2A,0xA7,0x41,0x35,0x8D,0xCC,0x93,0xE8,0xF1,0x41,0xC4,0x91]], - [[0xB8,0xB9,0xBA,0xBB,0xBD,0xBE,0xBF,0xC0,0xC2,0xC3,0xC4,0xC5,0xC7,0xC8,0xC9,0xCA,0xCC,0xCD,0xCE,0xCF,0xD1,0xD2,0xD3,0xD4,0xD6,0xD7,0xD8,0xD9,0xDB,0xDC,0xDD,0xDE],[0x87,0x07,0x12,0x1F,0x47,0xCC,0x3E,0xFC,0xEC,0xA5,0xF9,0xA8,0x47,0x49,0x50,0xA1],[0xD0,0x28,0xEE,0x23,0xE4,0xA8,0x90,0x75,0xD0,0xB0,0x3E,0x86,0x8D,0x7D,0x3A,0x42]], - [[0xE0,0xE1,0xE2,0xE3,0xE5,0xE6,0xE7,0xE8,0xEA,0xEB,0xEC,0xED,0xEF,0xF0,0xF1,0xF2,0xF4,0xF5,0xF6,0xF7,0xF9,0xFA,0xFB,0xFC,0xFE,0xFE,0x01,0x01,0x03,0x04,0x05,0x06],[0xE5,0x1A,0xA0,0xB1,0x35,0xDB,0xA5,0x66,0x93,0x9C,0x3B,0x63,0x59,0xA9,0x80,0xC5],[0x8C,0xD9,0x42,0x3D,0xFC,0x45,0x9E,0x54,0x71,0x55,0xC5,0xD1,0xD5,0x22,0xE5,0x40]], - [[0x08,0x09,0x0A,0x0B,0x0D,0x0E,0x0F,0x10,0x12,0x13,0x14,0x15,0x17,0x18,0x19,0x1A,0x1C,0x1D,0x1E,0x1F,0x21,0x22,0x23,0x24,0x26,0x27,0x28,0x29,0x2B,0x2C,0x2D,0x2E],[0x06,0x9A,0x00,0x7F,0xC7,0x6A,0x45,0x9F,0x98,0xBA,0xF9,0x17,0xFE,0xDF,0x95,0x21],[0x08,0x0E,0x95,0x17,0xEB,0x16,0x77,0x71,0x9A,0xCF,0x72,0x80,0x86,0x04,0x0A,0xE3]], - [[0x30,0x31,0x32,0x33,0x35,0x36,0x37,0x38,0x3A,0x3B,0x3C,0x3D,0x3F,0x40,0x41,0x42,0x44,0x45,0x46,0x47,0x49,0x4A,0x4B,0x4C,0x4E,0x4F,0x50,0x51,0x53,0x54,0x55,0x56],[0x72,0x61,0x65,0xC1,0x72,0x3F,0xBC,0xF6,0xC0,0x26,0xD7,0xD0,0x0B,0x09,0x10,0x27],[0x7C,0x17,0x00,0x21,0x1A,0x39,0x91,0xFC,0x0E,0xCD,0xED,0x0A,0xB3,0xE5,0x76,0xB0]]]; - - it('128 bit key', function (done) { - for (let i = 0; i < testvectors128.length; i++) { - test_aes(testvectors128[i][1],testvectors128[i][0],testvectors128[i][2]); - } - done(); - }); - - it('192 bit key', function (done) { - for (let i = 0; i < testvectors192.length; i++) { - test_aes(testvectors192[i][1],testvectors192[i][0],testvectors192[i][2]); - } - done(); - }); - - it('256 bit key', function (done) { - for (let i = 0; i < testvectors256.length; i++) { - test_aes(testvectors256[i][1],testvectors256[i][0],testvectors256[i][2]); - } - done(); - }); -}); diff --git a/test/crypto/cipher/blowfish.js b/test/crypto/cipher/blowfish.js index fc31fe89..f1e7477f 100644 --- a/test/crypto/cipher/blowfish.js +++ b/test/crypto/cipher/blowfish.js @@ -1,9 +1,9 @@ -const { expect } = require('chai'); +import { expect } from 'chai'; -const BF = require('../../../src/crypto/cipher/blowfish'); -const util = require('../../../src/util'); +import BF from '../../../src/crypto/cipher/blowfish'; +import util from '../../../src/util.js'; -module.exports = () => it('Blowfish cipher test with test vectors from https://www.schneier.com/code/vectors.txt', function(done) { +export default () => it('Blowfish cipher test with test vectors from https://www.schneier.com/code/vectors.txt', function(done) { function test_bf(input, key, output) { const blowfish = new BF(util.uint8ArrayToString(key)); const result = blowfish.encrypt(input); diff --git a/test/crypto/cipher/cast5.js b/test/crypto/cipher/cast5.js index c68ab094..cde718c3 100644 --- a/test/crypto/cipher/cast5.js +++ b/test/crypto/cipher/cast5.js @@ -1,9 +1,9 @@ -const { expect } = require('chai'); +import { expect } from 'chai'; -const CAST5 = require('../../../src/crypto/cipher/cast5'); -const util = require('../../../src/util'); +import CAST5 from '../../../src/crypto/cipher/cast5.js'; +import util from '../../../src/util.js'; -module.exports = () => it('CAST-128 cipher test with test vectors from RFC2144', function (done) { +export default () => it('CAST-128 cipher test with test vectors from RFC2144', function (done) { function test_cast(input, key, output) { const cast5 = new CAST5(key); const result = cast5.encrypt(input); diff --git a/test/crypto/cipher/des.js b/test/crypto/cipher/des.js index 72dbd594..8cfc03b5 100644 --- a/test/crypto/cipher/des.js +++ b/test/crypto/cipher/des.js @@ -1,9 +1,9 @@ -const { expect } = require('chai'); +import { expect } from 'chai'; -const { DES, TripleDES } = require('../../../src/crypto/cipher/des'); -const util = require('../../../src/util'); +import { DES, TripleDES } from '../../../src/crypto/cipher/des.js'; +import util from '../../../src/util.js'; -module.exports = () => describe('TripleDES (EDE) cipher test with test vectors from NIST SP 800-20', function() { +export default () => describe('TripleDES (EDE) cipher test with test vectors from NIST SP 800-20', function() { // see https://csrc.nist.gov/publications/nistpubs/800-20/800-20.pdf const key = new Uint8Array([1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]); const testvectors = [[[0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00],[0x95,0xF8,0xA5,0xE5,0xDD,0x31,0xD9,0x00]], @@ -80,7 +80,7 @@ module.exports = () => describe('TripleDES (EDE) cipher test with test vectors f expect(encr, 'vector with block ' + util.uint8ArrayToHex(testvectors[i][0]) + ' and key ' + util.uint8ArrayToHex(key) + ' should be ' + util.uint8ArrayToHex(testvectors[i][1]) + - ' != ' + util.uint8ArrayToHex(encr)).to.be.equal(util.uint8ArrayToString(testvectors[i][1])); + ' != ' + encr).to.be.equal(util.uint8ArrayToString(testvectors[i][1])); } done(); }); diff --git a/test/crypto/cipher/index.js b/test/crypto/cipher/index.js index 35770ed8..5ecefe31 100644 --- a/test/crypto/cipher/index.js +++ b/test/crypto/cipher/index.js @@ -1,7 +1,11 @@ -module.exports = () => describe('Cipher', function () { - require('./aes')(); - require('./blowfish')(); - require('./cast5')(); - require('./des')(); - require('./twofish')(); +import testBlowfish from './blowfish'; +import testCAST5 from './cast5'; +import testDES from './des'; +import testTwofish from './twofish'; + +export default () => describe('Cipher', function () { + testBlowfish(); + testCAST5(); + testDES(); + testTwofish(); }); diff --git a/test/crypto/cipher/twofish.js b/test/crypto/cipher/twofish.js index 6a0e1445..f66d105a 100644 --- a/test/crypto/cipher/twofish.js +++ b/test/crypto/cipher/twofish.js @@ -1,9 +1,9 @@ -const { expect } = require('chai'); +import { expect } from 'chai'; -const TF = require('../../../src/crypto/cipher/twofish'); -const util = require('../../../src/util'); +import TF from '../../../src/crypto/cipher/twofish.js'; +import util from '../../../src/util.js'; -module.exports = () => it('Twofish with test vectors from https://www.schneier.com/code/ecb_ival.txt', function(done) { +export default () => it('Twofish with test vectors from https://www.schneier.com/code/ecb_ival.txt', function(done) { function tfencrypt(block, key) { const tf = new TF(util.stringToUint8Array(key)); diff --git a/test/crypto/crypto.js b/test/crypto/crypto.js index 8c5417bc..c6bd5828 100644 --- a/test/crypto/crypto.js +++ b/test/crypto/crypto.js @@ -1,12 +1,12 @@ -const { use: chaiUse, expect } = require('chai'); -chaiUse(require('chai-as-promised')); +import { use as chaiUse, expect } from 'chai'; +import chaiAsPromised from 'chai-as-promised'; // eslint-disable-line import/newline-after-import +chaiUse(chaiAsPromised); -const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../..'); -const sandbox = require('sinon/lib/sinon/sandbox'); -const crypto = require('../../src/crypto'); -const util = require('../../src/util'); +import openpgp from '../initOpenpgp.js'; +import crypto from '../../src/crypto'; +import util from '../../src/util.js'; -module.exports = () => describe('API functional testing', function() { +export default () => describe('API functional testing', function() { const RSAPublicKeyMaterial = util.concatUint8Array([ new Uint8Array([0x08,0x00,0xac,0x15,0xb3,0xd6,0xd2,0x0f,0xf0,0x7a,0xdd,0x21,0xb7, 0xbf,0x61,0xfa,0xca,0x93,0x86,0xc8,0x55,0x5a,0x4b,0xa6,0xa4,0x1a, @@ -229,6 +229,21 @@ module.exports = () => describe('API functional testing', function() { return expect(success).to.be.true; }); + + it('Ed448', async function () { + // key data from https://www.rfc-editor.org/rfc/rfc8032#section-7.4 + const seed = util.hexToUint8Array('d65df341ad13e008567688baedda8e9dcdc17dc024974ea5b4227b6530e339bff21f99e68ca6968f3cca6dfe0fb9f4fab4fa135d5542ea3f01'); + const A = util.hexToUint8Array('df9705f58edbab802c7f8363cfe5560ab1c6132c20a9f1dd163483a26f8ac53a39d6808bf4a1dfbd261b099bb03b3fb50906cb28bd8a081f00'); + const toSign = await crypto.hash.digest(openpgp.enums.hash.sha512, data); + const signedData = await crypto.signature.sign( + openpgp.enums.publicKey.ed448, openpgp.enums.hash.sha512, { A }, { seed }, data, toSign + ); + const success = await crypto.signature.verify( + openpgp.enums.publicKey.ed448, openpgp.enums.hash.sha512, signedData, { A }, data, toSign + ); + + return expect(success).to.be.true; + }); }); describe('Encrypt and decrypt', function () { @@ -239,7 +254,7 @@ module.exports = () => describe('API functional testing', function() { async function testCFB(plaintext, config = openpgp.config) { await Promise.all(symmAlgoNames.map(async function(algoName) { const algo = openpgp.enums.write(openpgp.enums.symmetric, algoName); - const { blockSize } = crypto.getCipher(algo); + const { blockSize } = crypto.getCipherParams(algo); const symmKey = crypto.generateSessionKey(algo); const IV = new Uint8Array(blockSize); const symmencData = await crypto.mode.cfb.encrypt(algo, symmKey, util.stringToUint8Array(plaintext), IV, config); @@ -252,19 +267,6 @@ module.exports = () => describe('API functional testing', function() { await testCFB('1234567'); await testCFB('foobarfoobar1234567890'); await testCFB('12345678901234567890123456789012345678901234567890'); - // test using webCrypto - const sinonSandbox = sandbox.create(); - const webCrypto = util.getWebCrypto(); - if (webCrypto && !util.getNodeCrypto()) { - const webCryptoSpy = sinonSandbox.spy(webCrypto, 'encrypt'); - try { - await testCFB('12345678901234567890123456789012345678901234567890', { ...openpgp.config, minBytesForWebCrypto: 0 }); - } finally { - expect(webCryptoSpy.called).to.be.true; - sinonSandbox.restore(); - } - } - }); it('Asymmetric using RSA with eme_pkcs1 padding', function () { diff --git a/test/crypto/eax.js b/test/crypto/eax.js index 2c7e7625..a18688a2 100644 --- a/test/crypto/eax.js +++ b/test/crypto/eax.js @@ -1,13 +1,14 @@ // Modified by ProtonTech AG // Adapted from https://github.com/artjomb/cryptojs-extension/blob/8c61d159/test/eax.js -const sandbox = require('sinon/lib/sinon/sandbox'); -const { use: chaiUse, expect } = require('chai'); -chaiUse(require('chai-as-promised')); +import sinon from 'sinon'; +import { use as chaiUse, expect } from 'chai'; +import chaiAsPromised from 'chai-as-promised'; // eslint-disable-line import/newline-after-import +chaiUse(chaiAsPromised); -const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../..'); -const EAX = require('../../src/crypto/mode/eax'); -const util = require('../../src/util'); +import openpgp from '../initOpenpgp.js'; +import EAX from '../../src/crypto/mode/eax.js'; +import util from '../../src/util.js'; function testAESEAX() { it('Passes all test vectors', async function() { @@ -124,7 +125,7 @@ function testAESEAX() { } /* eslint-disable no-invalid-this */ -module.exports = () => describe('Symmetric AES-EAX', function() { +export default () => describe('Symmetric AES-EAX', function() { let sinonSandbox; let getWebCryptoStub; let getNodeCryptoStub; @@ -147,7 +148,7 @@ module.exports = () => describe('Symmetric AES-EAX', function() { }); beforeEach(function () { - sinonSandbox = sandbox.create(); + sinonSandbox = sinon.createSandbox(); enableNative(); }); @@ -158,9 +159,9 @@ module.exports = () => describe('Symmetric AES-EAX', function() { testAESEAX(); }); - describe('Symmetric AES-EAX (asm.js fallback)', function() { + describe('Symmetric AES-EAX (non-native fallback)', function() { beforeEach(function () { - sinonSandbox = sandbox.create(); + sinonSandbox = sinon.createSandbox(); disableNative(); }); diff --git a/test/crypto/ecdh.js b/test/crypto/ecdh.js index d9e3d824..72a8892b 100644 --- a/test/crypto/ecdh.js +++ b/test/crypto/ecdh.js @@ -1,18 +1,20 @@ -const sandbox = require('sinon/lib/sinon/sandbox'); -const { use: chaiUse, expect } = require('chai'); -chaiUse(require('chai-as-promised')); +import x25519 from '@openpgp/tweetnacl'; +import sinon from 'sinon'; +import { use as chaiUse, expect } from 'chai'; +import chaiAsPromised from 'chai-as-promised'; // eslint-disable-line import/newline-after-import +chaiUse(chaiAsPromised); -const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../..'); -const OID = require('../../src/type/oid'); -const KDFParams = require('../../src/type/kdf_params'); -const elliptic_curves = require('../../src/crypto/public_key/elliptic'); -const util = require('../../src/util'); -const elliptic_data = require('./elliptic_data'); -const random = require('../../src/crypto/random'); +import openpgp from '../initOpenpgp.js'; +import OID from '../../src/type/oid.js'; +import KDFParams from '../../src/type/kdf_params.js'; +import * as elliptic_curves from '../../src/crypto/public_key/elliptic'; +import util from '../../src/util.js'; +import elliptic_data from './elliptic_data.js'; +import * as random from '../../src/crypto/random.js'; const key_data = elliptic_data.key_data; /* eslint-disable no-invalid-this */ -module.exports = () => describe('ECDH key exchange @lightweight', function () { +export default () => describe('ECDH key exchange @lightweight', function () { const decrypt_message = function (oid, hash, cipher, priv, pub, ephemeral, data, fingerprint) { if (util.isString(data)) { data = util.stringToUint8Array(data); @@ -61,38 +63,41 @@ module.exports = () => describe('ECDH key exchange @lightweight', function () { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]); const secp256k1_data = new Uint8Array([ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]); + it('Generated legacy x25519 secret scalar is stored clamped', async function () { + const curve = new elliptic_curves.CurveWithOID(openpgp.enums.curve.curve25519Legacy); + const { privateKey, publicKey } = await curve.genKeyPair(); + const clampedKey = privateKey.slice(); + clampedKey[0] = (clampedKey[0] & 127) | 64; + clampedKey[31] &= 248; + expect(privateKey).to.deep.equal(clampedKey); + const { publicKey: expectedPublicKey } = x25519.box.keyPair.fromSecretKey(privateKey.slice().reverse()); + expect(publicKey.subarray(1)).to.deep.equal(expectedPublicKey); + }); it('Invalid curve oid', function (done) { expect(decrypt_message( '', 2, 7, [], [], [], [], [] )).to.be.rejectedWith(Error, /Unknown curve/).notify(done); }); - it('Invalid ephemeral key', function (done) { - if (!openpgp.config.useIndutnyElliptic && !util.getNodeCrypto()) { - this.skip(); - } - expect(decrypt_message( - 'secp256k1', 2, 7, [], [], [], [], [] - )).to.be.rejectedWith(Error, /Private key is not valid for specified curve|Unknown point format/).notify(done); - }); it('Invalid elliptic public key', function (done) { - if (!openpgp.config.useIndutnyElliptic && !util.getNodeCrypto()) { + if (!openpgp.config.useEllipticFallback && !util.getNodeCrypto()) { this.skip(); } expect(decrypt_message( 'secp256k1', 2, 7, secp256k1_value, secp256k1_point, secp256k1_invalid_point, secp256k1_data, [] - )).to.be.rejectedWith(Error, /Public key is not valid for specified curve|Failed to translate Buffer to a EC_POINT|Invalid elliptic public key/).notify(done); + )).to.be.rejectedWith(/Public key is not valid for specified curve|Failed to translate Buffer to a EC_POINT|bad point/).notify(done); }); it('Invalid key data integrity', function (done) { - if (!openpgp.config.useIndutnyElliptic && !util.getNodeCrypto()) { + if (!openpgp.config.useEllipticFallback && !util.getNodeCrypto()) { this.skip(); } expect(decrypt_message( 'secp256k1', 2, 7, secp256k1_value, secp256k1_point, secp256k1_point, secp256k1_data, [] - )).to.be.rejectedWith(Error, /Key Data Integrity failed/).notify(done); + )).to.be.rejectedWith(/Key Data Integrity failed/).notify(done); }); const Q1 = new Uint8Array([ @@ -135,23 +140,23 @@ module.exports = () => describe('ECDH key exchange @lightweight', function () { const ecdh = elliptic_curves.ecdh; it('Invalid curve', async function () { - if (!openpgp.config.useIndutnyElliptic && !util.getNodeCrypto()) { + if (!openpgp.config.useEllipticFallback && !util.getNodeCrypto()) { this.skip(); } const curve = new elliptic_curves.CurveWithOID('secp256k1'); const oid = new OID(curve.oid); const kdfParams = new KDFParams({ hash: curve.hash, cipher: curve.cipher }); - const data = util.stringToUint8Array('test'); - expect( + const data = random.getRandomBytes(16); + await expect( ecdh.encrypt(oid, kdfParams, data, Q1, fingerprint1) - ).to.be.rejectedWith(Error, /Public key is not valid for specified curve|Failed to translate Buffer to a EC_POINT|Unknown point format/); + ).to.be.rejectedWith(/Invalid point encoding/); }); it('Different keys', async function () { - const curve = new elliptic_curves.CurveWithOID('curve25519'); + const curve = new elliptic_curves.CurveWithOID(openpgp.enums.curve.curve25519Legacy); const oid = new OID(curve.oid); const kdfParams = new KDFParams({ hash: curve.hash, cipher: curve.cipher }); - const data = util.stringToUint8Array('test'); + const data = random.getRandomBytes(16); const { publicKey: V, wrappedKey: C } = await ecdh.encrypt(oid, kdfParams, data, Q1, fingerprint1); await expect( ecdh.decrypt(oid, kdfParams, V, C, Q2, d2, fingerprint1) @@ -159,10 +164,10 @@ module.exports = () => describe('ECDH key exchange @lightweight', function () { }); it('Invalid fingerprint', async function () { - const curve = new elliptic_curves.CurveWithOID('curve25519'); + const curve = new elliptic_curves.CurveWithOID(openpgp.enums.curve.curve25519Legacy); const oid = new OID(curve.oid); const kdfParams = new KDFParams({ hash: curve.hash, cipher: curve.cipher }); - const data = util.stringToUint8Array('test'); + const data = random.getRandomBytes(16); const { publicKey: V, wrappedKey: C } = await ecdh.encrypt(oid, kdfParams, data, Q2, fingerprint1); await expect( ecdh.decrypt(oid, kdfParams, V, C, Q2, d2, fingerprint2) @@ -170,10 +175,10 @@ module.exports = () => describe('ECDH key exchange @lightweight', function () { }); it('Successful exchange x25519 (legacy)', async function () { - const curve = new elliptic_curves.CurveWithOID('curve25519'); + const curve = new elliptic_curves.CurveWithOID(openpgp.enums.curve.curve25519Legacy); const oid = new OID(curve.oid); const kdfParams = new KDFParams({ hash: curve.hash, cipher: curve.cipher }); - const data = util.stringToUint8Array('test'); + const data = random.getRandomBytes(16); const { publicKey: V, wrappedKey: C } = await ecdh.encrypt(oid, kdfParams, data, Q1, fingerprint1); expect(await ecdh.decrypt(oid, kdfParams, V, C, Q1, d1, fingerprint1)).to.deep.equal(data); }); @@ -188,17 +193,118 @@ module.exports = () => describe('ECDH key exchange @lightweight', function () { expect(await ecdhX.decrypt(openpgp.enums.publicKey.x25519, ephemeralPublicKey, wrappedKey, K_B, b)).to.deep.equal(data); }); - ['p256', 'p384', 'p521'].forEach(curveName => { - it(`NIST ${curveName} - Successful exchange`, async function () { + it('Successful exchange x448', async function () { + const { ecdhX } = elliptic_curves; + const data = random.getRandomBytes(16); + // Bob's keys from https://www.rfc-editor.org/rfc/rfc7748#section-6.2 + const b = util.hexToUint8Array('1c306a7ac2a0e2e0990b294470cba339e6453772b075811d8fad0d1d6927c120bb5ee8972b0d3e21374c9c921b09d1b0366f10b65173992d'); + const K_B = util.hexToUint8Array('3eb7a829b0cd20f5bcfc0b599b6feccf6da4627107bdb0d4f345b43027d8b972fc3e34fb4232a13ca706dcb57aec3dae07bdc1c67bf33609'); + const { ephemeralPublicKey, wrappedKey } = await ecdhX.encrypt(openpgp.enums.publicKey.x448, data, K_B); + expect(await ecdhX.decrypt(openpgp.enums.publicKey.x448, ephemeralPublicKey, wrappedKey, K_B, b)).to.deep.equal(data); + }); + + it('Detect small order points in x25519', async () => { + const vectors = [ + { + 'order': '0', + 'vector': '0000000000000000000000000000000000000000000000000000000000000000' + }, + { + 'order': '1', + 'vector': '0100000000000000000000000000000000000000000000000000000000000000' + }, + { + 'order': '8', + 'vector': 'e0eb7a7c3b41b8ae1656e3faf19fc46ada098deb9c32b1fd866205165f49b800' + }, + { + 'order': 'p-1 (order 2)', + 'vector': 'ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f' + }, + { + 'order': 'p (=0, order 4)', + 'vector': 'edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f' + }, + { + 'order': 'p+1 (=1, order 1)', + 'vector': 'eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f' + } + ]; + const data = random.getRandomBytes(16); + for (const { vector } of vectors) { + const lowOrderPoint = util.hexToUint8Array(vector); + const { A: K_A, k: a } = await elliptic_curves.ecdhX.generate(openpgp.enums.publicKey.x25519); + await expect(elliptic_curves.ecdhX.encrypt(openpgp.enums.publicKey.x25519, data, lowOrderPoint)).to.be.rejectedWith(/low order point/); + const dummyWrappedKey = new Uint8Array(32); // expected to be unused + await expect(elliptic_curves.ecdhX.decrypt(openpgp.enums.publicKey.x25519, lowOrderPoint, dummyWrappedKey, K_A, a)).to.be.rejectedWith(/low order point/); + } + }); + + it('Detect small order points in x448', async () => { + const vectors = [ + { + 'order': '0', + 'vector': '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + }, + { + 'order': '1', + 'vector': '0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + }, + { + 'order': 'p-1 (order 2)', + 'vector': 'fefffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff' + }, + { + 'order': 'p (=0, order 4)', + 'vector': 'fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff' + }, + { + 'order': 'p+1 (=1, order 1)', + 'vector': '00000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff' + } + ]; + const data = random.getRandomBytes(16); + for (const { vector } of vectors) { + const lowOrderPoint = util.hexToUint8Array(vector); + const { A: K_A, k: a } = await elliptic_curves.ecdhX.generate(openpgp.enums.publicKey.x448); + await expect(elliptic_curves.ecdhX.encrypt(openpgp.enums.publicKey.x448, data, lowOrderPoint)).to.be.rejectedWith(/Invalid private or public key received|expected valid u|low order point/); + const dummyWrappedKey = new Uint8Array(32); // expected to be unused + await expect(elliptic_curves.ecdhX.decrypt(openpgp.enums.publicKey.x448, lowOrderPoint, dummyWrappedKey, K_A, a)).to.be.rejectedWith(/Invalid private or public key received|expected valid u|low order point/); + } + }); + + const allCurves = ['secp256k1', 'nistP256', 'nistP384', 'nistP521', 'brainpoolP256r1', 'brainpoolP384r1', 'brainpoolP512r1']; + allCurves.forEach(curveName => { + it(`${curveName} - Successful exchange`, async function () { const curve = new elliptic_curves.CurveWithOID(curveName); const oid = new OID(curve.oid); const kdfParams = new KDFParams({ hash: curve.hash, cipher: curve.cipher }); - const data = util.stringToUint8Array('test'); + const data = random.getRandomBytes(16); const Q = key_data[curveName].pub; const d = key_data[curveName].priv; const { publicKey: V, wrappedKey: C } = await ecdh.encrypt(oid, kdfParams, data, Q, fingerprint1); expect(await ecdh.decrypt(oid, kdfParams, V, C, Q, d, fingerprint1)).to.deep.equal(data); }); + + it(`${curveName} - Detect invalid PKESK public point encoding on decryption`, async function () { + const curve = new elliptic_curves.CurveWithOID(curveName); + const oid = new OID(curve.oid); + const kdfParams = new KDFParams({ hash: curve.hash, cipher: curve.cipher }); + const data = random.getRandomBytes(16); + const Q = key_data[curveName].pub; + const d = key_data[curveName].priv; + const { publicKey: V, wrappedKey: C } = await ecdh.encrypt(oid, kdfParams, data, Q, fingerprint1); + + const publicPointWithoutPrefixByte = V.subarray(1); + const publicPointWithUnexpectedPrefixByte = new Uint8Array([0x1, ...publicPointWithoutPrefixByte]); + const publicPointWithUnexpectedSize = V.subarray(0, V.length - 1); + + const expectedError = /Invalid point encoding/; + await expect(ecdh.decrypt(oid, kdfParams, publicPointWithoutPrefixByte, C, Q, d, fingerprint1)).to.be.rejectedWith(expectedError); + await expect(ecdh.decrypt(oid, kdfParams, publicPointWithUnexpectedPrefixByte, C, Q, d, fingerprint1)).to.be.rejectedWith(expectedError); + await expect(ecdh.decrypt(oid, kdfParams, publicPointWithUnexpectedSize, C, Q, d, fingerprint1)).to.be.rejectedWith(expectedError); + + }); }); describe('Comparing decrypting with and without native crypto', () => { @@ -207,7 +313,7 @@ module.exports = () => describe('ECDH key exchange @lightweight', function () { let getNodeCryptoStub; beforeEach(function () { - sinonSandbox = sandbox.create(); + sinonSandbox = sinon.createSandbox(); }); afterEach(function () { @@ -225,27 +331,31 @@ module.exports = () => describe('ECDH key exchange @lightweight', function () { getNodeCryptoStub && getNodeCryptoStub.restore(); }; - ['p256', 'p384', 'p521'].forEach(curveName => { - it(`NIST ${curveName}`, async function () { + allCurves.forEach(curveName => { + it(`${curveName}`, async function () { const nodeCrypto = util.getNodeCrypto(); const webCrypto = util.getWebCrypto(); if (!nodeCrypto && !webCrypto) { this.skip(); } + const expectNativeWeb = new Set(['nistP256', 'nistP384']); // older versions of safari do not implement nistP521 + const curve = new elliptic_curves.CurveWithOID(curveName); const oid = new OID(curve.oid); const kdfParams = new KDFParams({ hash: curve.hash, cipher: curve.cipher }); - const data = util.stringToUint8Array('test'); + const data = random.getRandomBytes(16); const Q = key_data[curveName].pub; const d = key_data[curveName].priv; const { publicKey: V, wrappedKey: C } = await ecdh.encrypt(oid, kdfParams, data, Q, fingerprint1); const nativeDecryptSpy = webCrypto ? sinonSandbox.spy(webCrypto, 'deriveBits') : sinonSandbox.spy(nodeCrypto, 'createECDH'); expect(await ecdh.decrypt(oid, kdfParams, V, C, Q, d, fingerprint1)).to.deep.equal(data); + const expectedNativeCallCount = nativeDecryptSpy.callCount; disableNative(); expect(await ecdh.decrypt(oid, kdfParams, V, C, Q, d, fingerprint1)).to.deep.equal(data); - if (curveName !== 'p521') { // safari does not implement p521 in webcrypto + expect(nativeDecryptSpy.callCount).to.equal(expectedNativeCallCount); // assert that fallback implementation was called + if (expectNativeWeb.has(curveName)) { expect(nativeDecryptSpy.calledOnce).to.be.true; } }); diff --git a/test/crypto/elliptic.js b/test/crypto/elliptic.js index f1a52a86..80059cea 100644 --- a/test/crypto/elliptic.js +++ b/test/crypto/elliptic.js @@ -1,18 +1,20 @@ -const sandbox = require('sinon/lib/sinon/sandbox'); -const { use: chaiUse, expect } = require('chai'); -chaiUse(require('chai-as-promised')); +import sinon from 'sinon'; +import { use as chaiUse, expect } from 'chai'; +import chaiAsPromised from 'chai-as-promised'; // eslint-disable-line import/newline-after-import +chaiUse(chaiAsPromised); -const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../..'); -const elliptic_curves = require('../../src/crypto/public_key/elliptic'); -const hashMod = require('../../src/crypto/hash'); -const config = require('../../src/config'); -const util = require('../../src/util'); +import openpgp from '../initOpenpgp.js'; +import * as elliptic_curves from '../../src/crypto/public_key/elliptic'; +import hashMod from '../../src/crypto/hash'; +import config from '../../src/config'; +import util from '../../src/util.js'; -const elliptic_data = require('./elliptic_data'); +import elliptic_data from './elliptic_data'; +import OID from '../../src/type/oid.js'; const key_data = elliptic_data.key_data; /* eslint-disable no-invalid-this */ -module.exports = () => describe('Elliptic Curve Cryptography @lightweight', function () { +export default () => describe('Elliptic Curve Cryptography @lightweight', function () { const signature_data = { priv: new Uint8Array([ 0x14, 0x2B, 0xE2, 0xB7, 0x4D, 0xBD, 0x1B, 0x22, @@ -67,11 +69,11 @@ module.exports = () => describe('Elliptic Curve Cryptography @lightweight', func done(); }); it('Creating KeyPair', function () { - if (!config.useIndutnyElliptic && !util.getNodeCrypto()) { + if (!config.useEllipticFallback && !util.getNodeCrypto()) { this.skip(); } - const names = config.useIndutnyElliptic ? ['p256', 'p384', 'p521', 'secp256k1', 'curve25519', 'brainpoolP256r1', 'brainpoolP384r1', 'brainpoolP512r1'] : - ['p256', 'p384', 'p521', 'curve25519']; + const names = config.useEllipticFallback ? ['nistP256', 'nistP384', 'nistP521', 'secp256k1', 'curve25519Legacy', 'brainpoolP256r1', 'brainpoolP384r1', 'brainpoolP512r1'] : + ['nistP256', 'nistP384', 'nistP521', 'curve25519Legacy']; return Promise.all(names.map(function (name) { const curve = new elliptic_curves.CurveWithOID(name); return curve.genKeyPair().then(keyPair => { @@ -79,22 +81,25 @@ module.exports = () => describe('Elliptic Curve Cryptography @lightweight', func }); })); }); - it('Signature verification', function (done) { - expect( - elliptic_curves.ecdsa.verify('p256', 8, signature_data.signature, signature_data.message, signature_data.pub, signature_data.hashed) - ).to.eventually.be.true.notify(done); + it('Signature verification', async function () { + const curve = new elliptic_curves.CurveWithOID('nistP256'); + await expect( + elliptic_curves.ecdsa.verify(new OID(curve.oid), 8, signature_data.signature, signature_data.message, signature_data.pub, signature_data.hashed) + ).to.eventually.be.true; }); - it('Invalid signature', function (done) { - expect( - elliptic_curves.ecdsa.verify('p256', 8, signature_data.signature, signature_data.message, key_data.p256.pub, signature_data.hashed) - ).to.eventually.be.false.notify(done); + it('Invalid signature', async function () { + const curve = new elliptic_curves.CurveWithOID('nistP256'); + await expect( + elliptic_curves.ecdsa.verify(new OID(curve.oid), 8, signature_data.signature, signature_data.message, key_data.nistP256.pub, signature_data.hashed) + ).to.eventually.be.false; }); - it('Signature generation', function () { - return elliptic_curves.ecdsa.sign('p256', 8, signature_data.message, key_data.p256.pub, key_data.p256.priv, signature_data.hashed).then(async signature => { - await expect( - elliptic_curves.ecdsa.verify('p256', 8, signature, signature_data.message, key_data.p256.pub, signature_data.hashed) - ).to.eventually.be.true; - }); + it('Signature generation', async function () { + const curve = new elliptic_curves.CurveWithOID('nistP256'); + const oid = new OID(curve.oid); + const signature = await elliptic_curves.ecdsa.sign(oid, 8, signature_data.message, key_data.nistP256.pub, key_data.nistP256.priv, signature_data.hashed); + await expect( + elliptic_curves.ecdsa.verify(oid, 8, signature, signature_data.message, key_data.nistP256.pub, signature_data.hashed) + ).to.eventually.be.true; }); }); describe('ECDSA signature', function () { @@ -103,7 +108,7 @@ module.exports = () => describe('Elliptic Curve Cryptography @lightweight', func let getNodeCryptoStub; beforeEach(function () { - sinonSandbox = sandbox.create(); + sinonSandbox = sinon.createSandbox(); }); afterEach(function () { @@ -121,13 +126,30 @@ module.exports = () => describe('Elliptic Curve Cryptography @lightweight', func getNodeCryptoStub && getNodeCryptoStub.restore(); }; - const verify_signature = async function (oid, hash, r, s, message, pub) { + const testNativeAndFallback = async fn => { + const webCrypto = util.getWebCrypto(); + const nodeCrypto = util.getNodeCrypto(); + const nativeSpy = webCrypto ? sinonSandbox.spy(webCrypto, 'importKey') : sinonSandbox.spy(nodeCrypto, 'createVerify'); // spy on function used on verification, since that's used by all tests calling `testNativeAndFallback` + + // if native not available, fallback will be tested twice (not possible to automatically check native algo availability) + enableNative(); + await fn(); + const expectedNativeCallCount = nativeSpy.callCount; + disableNative(); + await fn(); + expect(nativeSpy.callCount).to.equal(expectedNativeCallCount); + enableNative(); + }; + + const verify_signature = async function (curveName, hash, r, s, message, pub) { if (util.isString(message)) { message = util.stringToUint8Array(message); } else if (!util.isUint8Array(message)) { message = new Uint8Array(message); } const ecdsa = elliptic_curves.ecdsa; + const curve = new elliptic_curves.CurveWithOID(curveName); + const oid = new OID(curve.oid); return ecdsa.verify( oid, hash, { r: new Uint8Array(r), s: new Uint8Array(s) }, message, new Uint8Array(pub), await hashMod.digest(hash, message) ); @@ -161,99 +183,77 @@ module.exports = () => describe('Elliptic Curve Cryptography @lightweight', func 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]); - it('Invalid curve oid', function () { - return Promise.all([ - expect(verify_signature( - 'invalid oid', 8, [], [], [], [] - )).to.be.rejectedWith(Error, /Unknown curve/), - expect(verify_signature( - '\x00', 8, [], [], [], [] - )).to.be.rejectedWith(Error, /Unknown curve/) - ]); + it('Invalid curve oid', async function () { + await expect(verify_signature( + 'invalid oid', 8, [], [], [], [] + )).to.be.rejectedWith(Error, /Unknown curve/); + await expect(verify_signature( + '\x00', 8, [], [], [], [] + )).to.be.rejectedWith(Error, /Unknown curve/); }); - it('Invalid public key', async function () { - if (!config.useIndutnyElliptic && !util.getNodeCrypto()) { - this.skip(); // webcrypto does not implement secp256k1 - } - if (util.getNodeCrypto()) { - await expect(verify_signature( - 'secp256k1', 8, [], [], [], [] - )).to.eventually.be.false; - await expect(verify_signature( - 'secp256k1', 8, [], [], [], secp256k1_invalid_point_format - )).to.eventually.be.false; - } - if (config.useIndutnyElliptic) { - disableNative(); - await expect(verify_signature( - 'secp256k1', 8, [], [], [], [] - )).to.be.rejectedWith(Error, /Unknown point format/); - await expect(verify_signature( - 'secp256k1', 8, [], [], [], secp256k1_invalid_point_format - )).to.be.rejectedWith(Error, /Unknown point format/); + it('secp256k1 - Invalid public key', async function () { + if (!config.useEllipticFallback && !util.getNodeCrypto()) { + this.skip(); // webcrypto does not implement secp256k1: JS fallback tested instead } + await expect(verify_signature( + 'secp256k1', 8, [], [], [], [] + )).to.be.rejectedWith(/Invalid point encoding/); + await expect(verify_signature( + 'secp256k1', 8, [], [], [], secp256k1_invalid_point_format + )).to.be.rejectedWith(/Invalid point encoding/); + await expect(verify_signature( + 'secp256k1', 8, [], [], [], secp256k1_invalid_point + )).to.eventually.be.false; }); - it('Invalid point', async function () { - if (!config.useIndutnyElliptic && !util.getNodeCrypto()) { - this.skip(); - } - if (util.getNodeCrypto()) { - await expect(verify_signature( - 'secp256k1', 8, [], [], [], secp256k1_invalid_point - )).to.eventually.be.false; - } - if (config.useIndutnyElliptic) { - disableNative(); - await expect(verify_signature( - 'secp256k1', 8, [], [], [], secp256k1_invalid_point - )).to.be.rejectedWith(Error, /Invalid elliptic public key/); - } - }); - it('Invalid signature', function (done) { - if (!config.useIndutnyElliptic && !util.getNodeCrypto()) { - this.skip(); + it('secp256k1 - Invalid signature', function (done) { + if (!config.useEllipticFallback && !util.getNodeCrypto()) { + this.skip(); // webcrypto does not implement secp256k1: JS fallback tested instead } expect(verify_signature( 'secp256k1', 8, [], [], [], secp256k1_point )).to.eventually.be.false.notify(done); }); - const p384_message = new Uint8Array([ - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, - 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF - ]); - const p384_r = new Uint8Array([ - 0x9D, 0x07, 0xCA, 0xA5, 0x9F, 0xBE, 0xB8, 0x76, - 0xA9, 0xB9, 0x66, 0x0F, 0xA0, 0x64, 0x70, 0x5D, - 0xE6, 0x37, 0x40, 0x43, 0xD0, 0x8E, 0x40, 0xA8, - 0x8B, 0x37, 0x83, 0xE7, 0xBC, 0x1C, 0x4C, 0x86, - 0xCB, 0x3C, 0xD5, 0x9B, 0x68, 0xF0, 0x65, 0xEB, - 0x3A, 0xB6, 0xD6, 0xA6, 0xCF, 0x85, 0x3D, 0xA9 - ]); - const p384_s = new Uint8Array([ - 0x32, 0x85, 0x78, 0xCC, 0xEA, 0xC5, 0x22, 0x83, - 0x10, 0x73, 0x1C, 0xCF, 0x10, 0x8A, 0x52, 0x11, - 0x8E, 0x49, 0x9E, 0xCF, 0x7E, 0x17, 0x18, 0xC3, - 0x11, 0x11, 0xBC, 0x0F, 0x6D, 0x98, 0xE2, 0x16, - 0x68, 0x58, 0x23, 0x1D, 0x11, 0xEF, 0x3D, 0x21, - 0x30, 0x75, 0x24, 0x39, 0x48, 0x89, 0x03, 0xDC - ]); - it('Valid signature', function (done) { - expect(verify_signature('p384', 8, p384_r, p384_s, p384_message, key_data.p384.pub)) - .to.eventually.be.true.notify(done); + it('P-384 - Valid signature', async function () { + const p384_r = new Uint8Array([ + 0x9D, 0x07, 0xCA, 0xA5, 0x9F, 0xBE, 0xB8, 0x76, + 0xA9, 0xB9, 0x66, 0x0F, 0xA0, 0x64, 0x70, 0x5D, + 0xE6, 0x37, 0x40, 0x43, 0xD0, 0x8E, 0x40, 0xA8, + 0x8B, 0x37, 0x83, 0xE7, 0xBC, 0x1C, 0x4C, 0x86, + 0xCB, 0x3C, 0xD5, 0x9B, 0x68, 0xF0, 0x65, 0xEB, + 0x3A, 0xB6, 0xD6, 0xA6, 0xCF, 0x85, 0x3D, 0xA9 + ]); + const p384_s = new Uint8Array([ + 0x32, 0x85, 0x78, 0xCC, 0xEA, 0xC5, 0x22, 0x83, + 0x10, 0x73, 0x1C, 0xCF, 0x10, 0x8A, 0x52, 0x11, + 0x8E, 0x49, 0x9E, 0xCF, 0x7E, 0x17, 0x18, 0xC3, + 0x11, 0x11, 0xBC, 0x0F, 0x6D, 0x98, 0xE2, 0x16, + 0x68, 0x58, 0x23, 0x1D, 0x11, 0xEF, 0x3D, 0x21, + 0x30, 0x75, 0x24, 0x39, 0x48, 0x89, 0x03, 0xDC + ]); + const p384_message = new Uint8Array([ + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF + ]); + + await testNativeAndFallback( + () => expect(verify_signature('nistP384', 8, p384_r, p384_s, p384_message, key_data.nistP384.pub)).to.eventually.be.true + ); }); - it('Sign and verify message', function () { - const curve = new elliptic_curves.CurveWithOID('p521'); - return curve.genKeyPair().then(async keyPair => { - const keyPublic = new Uint8Array(keyPair.publicKey); - const keyPrivate = new Uint8Array(keyPair.privateKey); - const oid = curve.oid; - const message = p384_message; - return elliptic_curves.ecdsa.sign(oid, 10, message, keyPublic, keyPrivate, await hashMod.digest(10, message)).then(async signature => { - await expect(elliptic_curves.ecdsa.verify(oid, 10, signature, message, keyPublic, await hashMod.digest(10, message))) - .to.eventually.be.true; - }); + const curves = ['secp256k1' , 'nistP256', 'nistP384', 'nistP521', 'brainpoolP256r1', 'brainpoolP384r1', 'brainpoolP512r1']; + curves.forEach(curveName => it(`${curveName} - Sign and verify message`, async function () { + const curve = new elliptic_curves.CurveWithOID(curveName); + const oid = new OID(curve.oid); + const { Q: keyPublic, secret: keyPrivate } = await elliptic_curves.generate(curveName); + const message = new Uint8Array([ + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF + ]); + const messageDigest = await hashMod.digest(openpgp.enums.hash.sha512, message); + await testNativeAndFallback(async () => { + const signature = await elliptic_curves.ecdsa.sign(oid, openpgp.enums.hash.sha512, message, keyPublic, keyPrivate, messageDigest); + await expect(elliptic_curves.ecdsa.verify(oid, openpgp.enums.hash.sha512, signature, message, keyPublic, messageDigest)).to.eventually.be.true; }); - }); + })); }); }); diff --git a/test/crypto/elliptic_data.js b/test/crypto/elliptic_data.js index c6cd83c1..ce04b32f 100644 --- a/test/crypto/elliptic_data.js +++ b/test/crypto/elliptic_data.js @@ -1,6 +1,8 @@ +import util from '../../src/util.js'; + const elliptic_data = { key_data: { - p256: { + nistP256: { priv: new Uint8Array([ 0x2B, 0x48, 0x2B, 0xE9, 0x88, 0x74, 0xE9, 0x49, 0x1F, 0x89, 0xCC, 0xFF, 0x0A, 0x26, 0x05, 0xA2, @@ -19,7 +21,7 @@ const elliptic_data = { 0x67, 0xC2, 0x09, 0xF9, 0xEF, 0xE7, 0x9E, 0x56 ]) }, - p384: { + nistP384: { priv: new Uint8Array([ 0xB5, 0x38, 0xDA, 0xF3, 0x77, 0x58, 0x3F, 0x94, 0x5B, 0xC2, 0xCA, 0xC6, 0xA9, 0xFC, 0xAA, 0x3F, @@ -44,7 +46,7 @@ const elliptic_data = { 0xFA, 0x85, 0x8A, 0x4B, 0x58, 0x7C, 0x61, 0x39 ]) }, - p521: { + nistP521: { priv: new Uint8Array([ 0x00, 0xBB, 0x35, 0x27, 0xBC, 0xD6, 0x7E, 0x35, 0xD5, 0xC5, 0x99, 0xC9, 0xB4, 0x6C, 0xEE, 0xDE, @@ -95,8 +97,20 @@ const elliptic_data = { 0xB8, 0xFD, 0x0B, 0xDF, 0x76, 0xCE, 0xBC, 0x95, 0x4B, 0x92, 0x26, 0xFC, 0xAA, 0x7A, 0x7C, 0x3F ]) + }, + brainpoolP256r1: { + priv: util.hexToUint8Array('8b426897130e1e5e70a4d6320c4002bb1642a5e57ade066e060464137dfd5e05'), + pub: util.hexToUint8Array('042a43d8cc20e5a3fbd75d3a5a9b17d867bba80f11334d0665f0c641d13460a52aa3373a4ccfaa7d76765a689bd9fe15a4fd107ef1ec9ac980234c31647170c81a') + }, + brainpoolP384r1: { + priv: util.hexToUint8Array('7ccc97acdf4b775606c5c994a37a8b28086167046ac0d55664ede4097d8de79dec56e69dfff5776d53fcbd2147bbae9f'), + pub: util.hexToUint8Array('043809fa0c74ec9817cb73eba67db71e01663528fb9fbe6a123f8339346c37efc9ff7cd116074a80684448e44ee9204c795c88ad634ad272585c0b4e3093b11e6c99a6c0ca9c278f83ef57e2ed802502aee76f4529bcb873eef754bec894a5032f') + }, + brainpoolP512r1: { + priv: util.hexToUint8Array('0a32459d1ecf8815397a66f6cdb18692c6f79a3c6059b4c344d0162416c7603a82a9a938568edafb132c7433ffeeab4cf201d9542209eb28070bea56ab6b8938'), + pub: util.hexToUint8Array('040f64473d9b3597752e3a87095c0b219dd85f56a79c3b2dc8fb2b0c95b60f4be45c41a8a7ea31d60e15fea6275eb7db93856bc2eb30cc8876513335d43812bd2c4e195e05679ac667a2f7fb05c5842779d18fa411500e43e2f291ea8348f061db15382d4db1cfcf106a29f46e1c00e7d63e635c51293f69c0dd4f6a61da589b2a') } } }; -module.exports = elliptic_data; +export default elliptic_data; diff --git a/test/crypto/gcm.js b/test/crypto/gcm.js index d3b0f01b..cf4e6600 100644 --- a/test/crypto/gcm.js +++ b/test/crypto/gcm.js @@ -1,19 +1,20 @@ -const sandbox = require('sinon/lib/sinon/sandbox'); -const { use: chaiUse, expect } = require('chai'); -chaiUse(require('chai-as-promised')); +import sinon from 'sinon'; +import { use as chaiUse, expect } from 'chai'; +import chaiAsPromised from 'chai-as-promised'; // eslint-disable-line import/newline-after-import +chaiUse(chaiAsPromised); -const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../..'); -const crypto = require('../../src/crypto'); -const util = require('../../src/util'); +import openpgp from '../initOpenpgp.js'; +import crypto from '../../src/crypto'; +import util from '../../src/util.js'; -module.exports = () => describe('Symmetric AES-GCM (experimental)', function() { +export default () => describe('Symmetric AES-GCM (experimental)', function() { let sinonSandbox; let getWebCryptoStub; let getNodeCryptoStub; beforeEach(function () { - sinonSandbox = sandbox.create(); + sinonSandbox = sinon.createSandbox(); enableNative(); }); @@ -76,11 +77,11 @@ module.exports = () => describe('Symmetric AES-GCM (experimental)', function() { testAESGCM('12345678901234567890123456789012345678901234567890', true, true); }); - describe('Symmetric AES-GCM (asm.js fallback)', function() { + describe('Symmetric AES-GCM (non-native)', function() { testAESGCM('12345678901234567890123456789012345678901234567890', false, false); }); - describe('Symmetric AES-GCM (native encrypt, asm.js decrypt)', function() { + describe('Symmetric AES-GCM (native encrypt, non-native decrypt)', function() { testAESGCM('12345678901234567890123456789012345678901234567890', true, false); }); }); diff --git a/test/crypto/hash/index.js b/test/crypto/hash/index.js index c0b01f8c..53366177 100644 --- a/test/crypto/hash/index.js +++ b/test/crypto/hash/index.js @@ -1,5 +1,9 @@ -module.exports = () => describe('Hash', function () { - require('./md5')(); - require('./ripemd')(); - require('./sha')(); +import testMD5 from './md5'; +import testRipeMD from './ripemd'; +import testSHA from './sha'; + +export default () => describe('Hash', function () { + testMD5(); + testRipeMD(); + testSHA(); }); diff --git a/test/crypto/hash/md5.js b/test/crypto/hash/md5.js index 078f6c1b..0825e421 100644 --- a/test/crypto/hash/md5.js +++ b/test/crypto/hash/md5.js @@ -1,9 +1,9 @@ -const { expect } = require('chai'); +import { expect } from 'chai'; -const md5 = require('../../../src/crypto/hash/md5'); -const util = require('../../../src/util'); +import md5 from '../../../src/crypto/hash/md5.js'; +import util from '../../../src/util.js'; -module.exports = () => it('MD5 with test vectors from RFC 1321', async function() { +export default () => it('MD5 with test vectors from RFC 1321', async function() { expect(util.uint8ArrayToHex(await md5(util.stringToUint8Array('')), 'MD5("") = d41d8cd98f00b204e9800998ecf8427e')).to.equal('d41d8cd98f00b204e9800998ecf8427e'); expect(util.uint8ArrayToHex(await md5(util.stringToUint8Array('abc')), 'MD5("a") = 0cc175b9c0f1b6a831c399e269772661')).to.equal('900150983cd24fb0d6963f7d28e17f72'); expect(util.uint8ArrayToHex(await md5(util.stringToUint8Array('message digest')), 'MD5("message digest") = f96b697d7cb7938d525a2f31aaf161d0')).to.equal('f96b697d7cb7938d525a2f31aaf161d0'); diff --git a/test/crypto/hash/ripemd.js b/test/crypto/hash/ripemd.js index e9d93d62..a1116c31 100644 --- a/test/crypto/hash/ripemd.js +++ b/test/crypto/hash/ripemd.js @@ -1,11 +1,11 @@ -const { expect } = require('chai'); +import { expect } from 'chai'; -const { ripemd: rmdString } = require('../../../src/crypto/hash'); -const util = require('../../../src/util'); +import hash from '../../../src/crypto/hash'; +import util from '../../../src/util.js'; -module.exports = () => it('RIPE-MD 160 bits with test vectors from https://homes.esat.kuleuven.be/~bosselae/ripemd160.html', async function() { - expect(util.uint8ArrayToHex(await rmdString(util.stringToUint8Array('')), 'RMDstring("") = 9c1185a5c5e9fc54612808977ee8f548b2258d31')).to.equal('9c1185a5c5e9fc54612808977ee8f548b2258d31'); - expect(util.uint8ArrayToHex(await rmdString(util.stringToUint8Array('a')), 'RMDstring("a") = 0bdc9d2d256b3ee9daae347be6f4dc835a467ffe')).to.equal('0bdc9d2d256b3ee9daae347be6f4dc835a467ffe'); - expect(util.uint8ArrayToHex(await rmdString(util.stringToUint8Array('abc')), 'RMDstring("abc") = 8eb208f7e05d987a9b044a8e98c6b087f15a0bfc')).to.equal('8eb208f7e05d987a9b044a8e98c6b087f15a0bfc'); - expect(util.uint8ArrayToHex(await rmdString(util.stringToUint8Array('message digest')), 'RMDstring("message digest") = 5d0689ef49d2fae572b881b123a85ffa21595f36')).to.equal('5d0689ef49d2fae572b881b123a85ffa21595f36'); +export default () => it('RIPE-MD 160 bits with test vectors from https://homes.esat.kuleuven.be/~bosselae/ripemd160.html', async function() { + expect(util.uint8ArrayToHex(await hash.ripemd(util.stringToUint8Array('')), 'RMDstring("") = 9c1185a5c5e9fc54612808977ee8f548b2258d31')).to.equal('9c1185a5c5e9fc54612808977ee8f548b2258d31'); + expect(util.uint8ArrayToHex(await hash.ripemd(util.stringToUint8Array('a')), 'RMDstring("a") = 0bdc9d2d256b3ee9daae347be6f4dc835a467ffe')).to.equal('0bdc9d2d256b3ee9daae347be6f4dc835a467ffe'); + expect(util.uint8ArrayToHex(await hash.ripemd(util.stringToUint8Array('abc')), 'RMDstring("abc") = 8eb208f7e05d987a9b044a8e98c6b087f15a0bfc')).to.equal('8eb208f7e05d987a9b044a8e98c6b087f15a0bfc'); + expect(util.uint8ArrayToHex(await hash.ripemd(util.stringToUint8Array('message digest')), 'RMDstring("message digest") = 5d0689ef49d2fae572b881b123a85ffa21595f36')).to.equal('5d0689ef49d2fae572b881b123a85ffa21595f36'); }); diff --git a/test/crypto/hash/sha.js b/test/crypto/hash/sha.js index fc81b29f..5297aca5 100644 --- a/test/crypto/hash/sha.js +++ b/test/crypto/hash/sha.js @@ -1,9 +1,9 @@ -const { expect } = require('chai'); +import { expect } from 'chai'; -const hash = require('../../../src/crypto/hash'); -const util = require('../../../src/util'); +import hash from '../../../src/crypto/hash'; +import util from '../../../src/util.js'; -module.exports = () => it('SHA* with test vectors from NIST FIPS 180-2', async function() { +export default () => it('SHA* with test vectors from NIST FIPS 180-2', async function() { expect(util.uint8ArrayToHex(await hash.sha1(util.stringToUint8Array('abc')), 'hash.sha1("abc") = a9993e364706816aba3e25717850c26c9cd0d89d')).to.equal('a9993e364706816aba3e25717850c26c9cd0d89d'); expect(util.uint8ArrayToHex(await hash.sha1(util.stringToUint8Array('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')), 'hash.sha1("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 84983e441c3bd26ebaae4aa1f95129e5e54670f1')).to.equal('84983e441c3bd26ebaae4aa1f95129e5e54670f1'); expect(util.uint8ArrayToHex(await hash.sha224(util.stringToUint8Array('abc')), 'hash.sha224("abc") = 23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7')).to.equal('23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7'); @@ -14,4 +14,8 @@ module.exports = () => it('SHA* with test vectors from NIST FIPS 180-2', async f expect(util.uint8ArrayToHex(await hash.sha384(util.stringToUint8Array('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')), 'hash.sha384("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b')).to.equal('3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b'); expect(util.uint8ArrayToHex(await hash.sha512(util.stringToUint8Array('abc')), 'hash.sha512("abc") = ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f')).to.equal('ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f'); expect(util.uint8ArrayToHex(await hash.sha512(util.stringToUint8Array('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')), 'hash.sha512("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445')).to.equal('204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445'); + expect(util.uint8ArrayToHex(await hash.sha3_256(util.stringToUint8Array('abc')), 'hash.sha3_256("abc") = 3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532')).to.equal('3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532'); + expect(util.uint8ArrayToHex(await hash.sha3_256(util.stringToUint8Array('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')), 'hash.sha3_256("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 41c0dba2a9d6240849100376a8235e2c82e1b9998a999e21db32dd97496d3376')).to.equal('41c0dba2a9d6240849100376a8235e2c82e1b9998a999e21db32dd97496d3376'); + expect(util.uint8ArrayToHex(await hash.sha3_512(util.stringToUint8Array('abc')), 'hash.sha3_512("abc") = b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0')).to.equal('b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0'); + expect(util.uint8ArrayToHex(await hash.sha3_512(util.stringToUint8Array('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')), 'hash.sha3_512("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 04a371e84ecfb5b8b77cb48610fca8182dd457ce6f326a0fd3d7ec2f1e91636dee691fbe0c985302ba1b0d8dc78c086346b533b49c030d99a27daf1139d6e75e')).to.equal('04a371e84ecfb5b8b77cb48610fca8182dd457ce6f326a0fd3d7ec2f1e91636dee691fbe0c985302ba1b0d8dc78c086346b533b49c030d99a27daf1139d6e75e'); }); diff --git a/test/crypto/hkdf.js b/test/crypto/hkdf.js index b2b88023..de048e1a 100644 --- a/test/crypto/hkdf.js +++ b/test/crypto/hkdf.js @@ -1,13 +1,10 @@ -const { expect } = require('chai'); +import { expect } from 'chai'; -const computeHKDF = require('../../src/crypto/hkdf'); -const enums = require('../../src/enums'); -const util = require('../../src/util'); +import computeHKDF from '../../src/crypto/hkdf'; +import enums from '../../src/enums'; +import util from '../../src/util'; -// WebCrypto implements HKDF natively, no need to test it -const maybeDescribe = util.getNodeCrypto() ? describe : describe; - -module.exports = () => maybeDescribe('HKDF test vectors', function() { +export default () => describe('HKDF test vectors', function() { // Vectors from https://www.rfc-editor.org/rfc/rfc5869#appendix-A it('Test Case 1', async function() { const inputKey = util.hexToUint8Array('0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b'); diff --git a/test/crypto/index.js b/test/crypto/index.js index 932dd1d8..a4c73ae5 100644 --- a/test/crypto/index.js +++ b/test/crypto/index.js @@ -1,15 +1,33 @@ -module.exports = () => describe('Crypto', function () { - require('./cipher')(); - require('./hash')(); - require('./crypto')(); - require('./elliptic')(); - require('./ecdh')(); - require('./pkcs5')(); - require('./aes_kw')(); - require('./hkdf')(); - require('./gcm')(); - require('./eax')(); - require('./ocb')(); - require('./rsa')(); - require('./validate')(); +import testBigInteger from './biginteger'; +import testCipher from './cipher'; +import testHash from './hash'; +import testCrypto from './crypto'; +import testElliptic from './elliptic'; +import testBrainpoolRFC7027 from './brainpool_rfc7027'; +import testECDH from './ecdh'; +import testPKCS5 from './pkcs5'; +import testAESKW from './aes_kw'; +import testHKDF from './hkdf'; +import testGCM from './gcm'; +import testEAX from './eax'; +import testOCB from './ocb'; +import testRSA from './rsa'; +import testValidate from './validate'; + +export default () => describe('Crypto', function () { + testBigInteger(); + testCipher(); + testHash(); + testCrypto(); + testElliptic(); + testBrainpoolRFC7027(); + testECDH(); + testPKCS5(); + testAESKW(); + testHKDF(); + testGCM(); + testEAX(); + testOCB(); + testRSA(); + testValidate(); }); diff --git a/test/crypto/ocb.js b/test/crypto/ocb.js index c97beb27..ef80a96b 100644 --- a/test/crypto/ocb.js +++ b/test/crypto/ocb.js @@ -1,14 +1,15 @@ // Modified by ProtonTech AG // Adapted from https://github.com/artjomb/cryptojs-extension/blob/8c61d159/test/eax.js -const { use: chaiUse, expect } = require('chai'); -chaiUse(require('chai-as-promised')); +import { use as chaiUse, expect } from 'chai'; +import chaiAsPromised from 'chai-as-promised'; // eslint-disable-line import/newline-after-import +chaiUse(chaiAsPromised); -const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../..'); -const OCB = require('../../src/crypto/mode/ocb'); -const util = require('../../src/util'); +import openpgp from '../initOpenpgp.js'; +import OCB from '../../src/crypto/mode/ocb.js'; +import util from '../../src/util.js'; -module.exports = () => describe('Symmetric AES-OCB', function() { +export default () => describe('Symmetric AES-OCB', function() { it('Passes all test vectors', async function() { const K = '000102030405060708090A0B0C0D0E0F'; const keyBytes = util.hexToUint8Array(K); diff --git a/test/crypto/pkcs5.js b/test/crypto/pkcs5.js index c051206e..30d666a6 100644 --- a/test/crypto/pkcs5.js +++ b/test/crypto/pkcs5.js @@ -1,8 +1,8 @@ -const { expect } = require('chai'); +import { expect } from 'chai'; -const pkcs5 = require('../../src/crypto/pkcs5'); +import * as pkcs5 from '../../src/crypto/pkcs5.js'; -module.exports = () => describe('PKCS5 padding', function() { +export default () => describe('PKCS5 padding', function() { it('Add and remove padding', function () { const m = new Uint8Array([0,1,2,3,4,5,6,7,8]); const padded = pkcs5.encode(m); diff --git a/test/crypto/rsa.js b/test/crypto/rsa.js index 37f7e1e5..61d00cf7 100644 --- a/test/crypto/rsa.js +++ b/test/crypto/rsa.js @@ -1,20 +1,21 @@ -const sandbox = require('sinon/lib/sinon/sandbox'); -const { use: chaiUse, expect } = require('chai'); -chaiUse(require('chai-as-promised')); +import sinon from 'sinon'; +import { use as chaiUse, expect } from 'chai'; +import chaiAsPromised from 'chai-as-promised'; // eslint-disable-line import/newline-after-import +chaiUse(chaiAsPromised); -const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../..'); -const crypto = require('../../src/crypto'); -const random = require('../../src/crypto/random'); -const util = require('../../src/util'); +import openpgp from '../initOpenpgp.js'; +import crypto from '../../src/crypto'; +import * as random from '../../src/crypto/random.js'; +import util from '../../src/util.js'; /* eslint-disable no-invalid-this */ -module.exports = () => describe('basic RSA cryptography', function () { +export default () => describe('basic RSA cryptography', function () { let sinonSandbox; let getWebCryptoStub; let getNodeCryptoStub; beforeEach(function () { - sinonSandbox = sandbox.create(); + sinonSandbox = sinon.createSandbox(); enableNative(); }); @@ -120,6 +121,22 @@ module.exports = () => describe('basic RSA cryptography', function () { expect(util.uint8ArrayToHex(signatureNative)).to.be.equal(util.uint8ArrayToHex(signatureBN)); }); + it('compare native crypto and bnSign: throw on key size shorter than digest size', async function() { + if (!detectNative()) { this.skip(); } + + const bits = 512; + const hashName = 'sha512'; // digest too long for a 512-bit key + const { publicParams, privateParams } = await crypto.generateParams(openpgp.enums.publicKey.rsaSign, bits); + const { n, e, d, p, q, u } = { ...publicParams, ...privateParams }; + const message = random.getRandomBytes(64); + const hashAlgo = openpgp.enums.write(openpgp.enums.hash, hashName); + const hashed = await crypto.hash.digest(hashAlgo, message); + enableNative(); + await expect(crypto.publicKey.rsa.sign(hashAlgo, message, n, e, d, p, q, u, hashed)).to.be.rejectedWith(/Digest size cannot exceed key modulus size/); + disableNative(); + await expect(crypto.publicKey.rsa.sign(hashAlgo, message, n, e, d, p, q, u, hashed)).to.be.rejectedWith(/Digest size cannot exceed key modulus size/); + }); + it('compare native crypto and bnVerify', async function() { if (!detectNative()) { this.skip(); } diff --git a/test/crypto/validate.js b/test/crypto/validate.js index 5e311532..8ed63089 100644 --- a/test/crypto/validate.js +++ b/test/crypto/validate.js @@ -1,8 +1,9 @@ -const BN = require('bn.js'); -const { use: chaiUse, expect } = require('chai'); -chaiUse(require('chai-as-promised')); +import { use as chaiUse, expect } from 'chai'; +import chaiAsPromised from 'chai-as-promised'; // eslint-disable-line import/newline-after-import +chaiUse(chaiAsPromised); -const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../..'); +import openpgp from '../initOpenpgp.js'; +import { bigIntToUint8Array, modExp, uint8ArrayToBigInt } from '../../src/crypto/biginteger.ts'; const armoredDSAKey = `-----BEGIN PGP PRIVATE KEY BLOCK----- @@ -86,11 +87,11 @@ async function generatePrivateKeyObject(options) { } /* eslint-disable no-invalid-this */ -module.exports = () => { +export default () => { describe('EdDSA parameter validation (legacy format)', function() { let eddsaKey; before(async () => { - eddsaKey = await generatePrivateKeyObject({ curve: 'ed25519' }); + eddsaKey = await generatePrivateKeyObject({ curve: 'ed25519Legacy' }); }); it('EdDSA params should be valid', async function() { @@ -114,9 +115,9 @@ module.exports = () => { let ecdhKey; let ecdsaKey; before(async () => { - eddsaKey = await generatePrivateKeyObject({ curve: 'ed25519' }); + eddsaKey = await generatePrivateKeyObject({ curve: 'ed25519Legacy' }); ecdhKey = eddsaKey.subkeys[0]; - ecdsaKey = await generatePrivateKeyObject({ curve: 'p256' }); + ecdsaKey = await generatePrivateKeyObject({ curve: 'nistP256' }); }); it('EdDSA params are not valid for ECDH', async function() { @@ -192,17 +193,17 @@ module.exports = () => { }); }); - const curves = ['curve25519', 'p256', 'p384', 'p521', 'secp256k1', 'brainpoolP256r1', 'brainpoolP384r1', 'brainpoolP512r1']; + const curves = ['curve25519Legacy', 'nistP256', 'nistP384', 'nistP521', 'secp256k1', 'brainpoolP256r1', 'brainpoolP384r1', 'brainpoolP512r1']; curves.forEach(curve => { describe(`ECC ${curve} parameter validation`, () => { let ecdsaKey; let ecdhKey; before(async () => { - if (curve !== 'curve25519') { + if (curve !== 'curve25519Legacy') { ecdsaKey = await generatePrivateKeyObject({ curve }); ecdhKey = ecdsaKey.subkeys[0]; } else { - const eddsaKey = await generatePrivateKeyObject({ curve: 'ed25519' }); + const eddsaKey = await generatePrivateKeyObject({ curve: 'ed25519Legacy' }); ecdhKey = eddsaKey.subkeys[0]; } }); @@ -246,6 +247,48 @@ module.exports = () => { }); }); + // new EdDSA/XECDH algos + ['25519', '448'].forEach(curveID => { + describe(`Ed${curveID}/X${curveID} parameter validation`, function() { + let eddsaKey; + let ecdhXKey; + before(async () => { + eddsaKey = await generatePrivateKeyObject({ type: `curve${curveID}` }); + ecdhXKey = eddsaKey.subkeys[0]; + }); + + it(`Ed${curveID} params should be valid`, async function() { + await expect(eddsaKey.keyPacket.validate()).to.not.be.rejected; + }); + + it(`detect invalid Ed${curveID} public point`, async function() { + const eddsaKeyPacket = await cloneKeyPacket(eddsaKey); + const A = eddsaKeyPacket.publicParams.A; + A[0]++; + await expect(eddsaKeyPacket.validate()).to.be.rejectedWith('Key is invalid'); + + const infA = new Uint8Array(A.length); + eddsaKeyPacket.publicParams.A = infA; + await expect(eddsaKeyPacket.validate()).to.be.rejectedWith('Key is invalid'); + }); + + it(`X${curveID} params should be valid`, async function() { + await expect(ecdhXKey.keyPacket.validate()).to.not.be.rejected; + }); + + it(`detect invalid X${curveID} public point`, async function() { + const ecdhXKeyPacket = await cloneKeyPacket(ecdhXKey); + const A = ecdhXKeyPacket.publicParams.A; + A[0]++; + await expect(ecdhXKeyPacket.validate()).to.be.rejectedWith('Key is invalid'); + + const infA = new Uint8Array(A.length); + ecdhXKeyPacket.publicParams.A = infA; + await expect(ecdhXKeyPacket.validate()).to.be.rejectedWith('Key is invalid'); + }); + }); + }); + describe('RSA parameter validation', function() { let rsaKey; before(async () => { @@ -345,14 +388,14 @@ module.exports = () => { it('detect g with small order', async function() { const keyPacket = await cloneKeyPacket(egKey); - const p = keyPacket.publicParams.p; - const g = keyPacket.publicParams.g; + const { p, g } = keyPacket.publicParams; - const pBN = new BN(p); - const gModP = new BN(g).toRed(new BN.red(pBN)); + const _1n = BigInt(1); + const pBN = uint8ArrayToBigInt(p); + const gBN = uint8ArrayToBigInt(g); // g**(p-1)/2 has order 2 - const gOrd2 = gModP.redPow(pBN.subn(1).shrn(1)); - keyPacket.publicParams.g = gOrd2.toArrayLike(Uint8Array, 'be'); + const gOrd2 = modExp(gBN, (pBN - _1n) >> _1n, pBN); + keyPacket.publicParams.g = bigIntToUint8Array(gOrd2); await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid'); }); }); diff --git a/test/general/armor.js b/test/general/armor.js index 3ec400a3..01df7c05 100644 --- a/test/general/armor.js +++ b/test/general/armor.js @@ -1,9 +1,8 @@ -const { expect } = require('chai'); +import { expect } from 'chai'; -const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../..'); +import openpgp from '../initOpenpgp.js'; - -module.exports = () => describe('ASCII armor', function() { +export default () => describe('ASCII armor', function() { function getArmor(headers, signatureHeaders) { return ['-----BEGIN PGP SIGNED MESSAGE-----'] @@ -37,12 +36,6 @@ module.exports = () => describe('ASCII armor', function() { await expect(msg).to.be.rejectedWith(Error, /Hash algorithm mismatch in armor header and signature/); }); - it('Exception if no header and non-MD5 signature', async function () { - let msg = getArmor(null); - msg = openpgp.readCleartextMessage({ cleartextMessage: msg }); - await expect(msg).to.be.rejectedWith(Error, /If no "Hash" header in cleartext signed message, then only MD5 signatures allowed/); - }); - it('Exception if unknown hash algorithm', async function () { let msg = getArmor(['Hash: LAV750']); msg = openpgp.readCleartextMessage({ cleartextMessage: msg }); @@ -67,18 +60,6 @@ module.exports = () => describe('ASCII armor', function() { await expect(msg).to.be.rejectedWith(Error, /Only "Hash" header allowed in cleartext signed message/); }); - it('Multiple wrong hash values', async function () { - let msg = getArmor(['Hash: SHA512, SHA256']); - msg = openpgp.readCleartextMessage({ cleartextMessage: msg }); - await expect(msg).to.be.rejectedWith(Error, /Hash algorithm mismatch in armor header and signature/); - }); - - it('Multiple wrong hash values', async function () { - let msg = getArmor(['Hash: SHA512, SHA256']); - msg = openpgp.readCleartextMessage({ cleartextMessage: msg }); - await expect(msg).to.be.rejectedWith(Error, /Hash algorithm mismatch in armor header and signature/); - }); - it('Filter whitespace in blank line', async function () { let msg = [ '-----BEGIN PGP SIGNED MESSAGE-----', @@ -100,11 +81,6 @@ module.exports = () => describe('ASCII armor', function() { expect(msg).to.be.an.instanceof(openpgp.CleartextMessage); }); - it('Exception if header is not Hash in cleartext signed message', async function () { - const msg = openpgp.readCleartextMessage({ cleartextMessage: getArmor(['Ha sh: SHA256']) }); - await expect(msg).to.be.rejectedWith(Error, /Only "Hash" header allowed in cleartext signed message/); - }); - it('Ignore improperly formatted armor header', async function () { await Promise.all(['Space : trailing', 'Space :switched', ': empty', 'none', 'Space:missing'].map(async function (invalidHeader) { expect(await openpgp.readCleartextMessage({ cleartextMessage: getArmor(['Hash: SHA1'], [invalidHeader]) })).to.be.an.instanceof(openpgp.CleartextMessage); @@ -172,15 +148,7 @@ module.exports = () => describe('ASCII armor', function() { '-----END PGP PRIVATE KEY BLOCK-----' ].join('\n'); - // try with default config - await expect(openpgp.readKey({ armoredKey: privKey })).to.be.rejectedWith(/Ascii armor integrity check failed/); - - // try opposite config - openpgp.config.checksumRequired = !openpgp.config.checksumRequired; - await expect(openpgp.readKey({ armoredKey: privKey })).to.be.rejectedWith(/Ascii armor integrity check failed/); - - // back to default - openpgp.config.checksumRequired = !openpgp.config.checksumRequired; + await openpgp.readKey({ armoredKey: privKey }); }); it('Armor checksum validation - valid', async function () { @@ -204,15 +172,7 @@ module.exports = () => describe('ASCII armor', function() { '=wJNM', '-----END PGP PRIVATE KEY BLOCK-----'].join('\n'); - // try with default config await openpgp.readKey({ armoredKey: privKey }); - - // try opposite config - openpgp.config.checksumRequired = !openpgp.config.checksumRequired; - await openpgp.readKey({ armoredKey: privKey }); - - // back to default - openpgp.config.checksumRequired = !openpgp.config.checksumRequired; }); it('Armor checksum validation - missing', async function () { @@ -235,23 +195,7 @@ module.exports = () => describe('ASCII armor', function() { '71vlXMJNXvoCeuejiRw=', '-----END PGP PRIVATE KEY BLOCK-----'].join('\n'); - // try with default config - if (openpgp.config.checksumRequired) { - await expect(openpgp.readKey({ armoredKey: privKeyNoCheckSum })).to.be.rejectedWith(/Ascii armor integrity check failed/); - } else { - await openpgp.readKey({ armoredKey: privKeyNoCheckSum }); - } - - // try opposite config - openpgp.config.checksumRequired = !openpgp.config.checksumRequired; - if (openpgp.config.checksumRequired) { - await expect(openpgp.readKey({ armoredKey: privKeyNoCheckSum })).to.be.rejectedWith(/Ascii armor integrity check failed/); - } else { - await openpgp.readKey({ armoredKey: privKeyNoCheckSum }); - } - - // back to default - openpgp.config.checksumRequired = !openpgp.config.checksumRequired; + await openpgp.readKey({ armoredKey: privKeyNoCheckSum }); }); it('Armor checksum validation - missing - trailing newline', async function () { @@ -275,23 +219,7 @@ module.exports = () => describe('ASCII armor', function() { '-----END PGP PRIVATE KEY BLOCK-----', ''].join('\n'); - // try with default config - if (openpgp.config.checksumRequired) { - await expect(openpgp.readKey({ armoredKey: privKeyNoCheckSumWithTrailingNewline })).to.be.rejectedWith(/Ascii armor integrity check failed/); - } else { - await openpgp.readKey({ armoredKey: privKeyNoCheckSumWithTrailingNewline }); - } - - // try opposite config - openpgp.config.checksumRequired = !openpgp.config.checksumRequired; - if (openpgp.config.checksumRequired) { - await expect(openpgp.readKey({ armoredKey: privKeyNoCheckSumWithTrailingNewline })).to.be.rejectedWith(/Ascii armor integrity check failed/); - } else { - await openpgp.readKey({ armoredKey: privKeyNoCheckSumWithTrailingNewline }); - } - - // back to default - openpgp.config.checksumRequired = !openpgp.config.checksumRequired; + await openpgp.readKey({ armoredKey: privKeyNoCheckSumWithTrailingNewline }); }); it('Accept header with trailing whitespace', async function () { @@ -327,6 +255,40 @@ module.exports = () => describe('ASCII armor', function() { expect(msg.text).to.equal('\r\nsign this'); }); + it('Selectively output CRC checksum', async function () { + const includesArmorChecksum = armoredData => { + const lines = armoredData.split('\n'); + const lastDataLine = lines[lines.length - 3]; + return (lastDataLine[0] === '=' && lastDataLine.length === 5); + }; + + // unless explicitly forbidden by the spec, we include the checksum to work around a GnuPG bug (https://dev.gnupg.org/T7071) + const { privateKey: v4Key } = await openpgp.generateKey({ userIDs: { email: 'v4@armor.test' }, format: 'object' }); + expect(includesArmorChecksum(v4Key.armor())).to.be.true; + const { privateKey: v6Key } = await openpgp.generateKey({ userIDs: { email: 'v6@armor.test' }, config: { v6Keys: true, aeadProtect: true }, format: 'object' }); + expect(includesArmorChecksum(v6Key.armor())).to.be.false; + + const messageWithSEIPDv1 = await openpgp.encrypt({ message: await openpgp.createMessage({ text: 'test' }), encryptionKeys: v4Key }); + expect(includesArmorChecksum(messageWithSEIPDv1)).to.be.true; + const messageWithSEIPDv2 = await openpgp.encrypt({ message: await openpgp.createMessage({ text: 'test' }), encryptionKeys: v6Key }); + expect(includesArmorChecksum(messageWithSEIPDv2)).to.be.false; + + const signatureV4V6 = await openpgp.sign({ message: await openpgp.createMessage({ text: 'test' }), signingKeys: [v4Key, v6Key] }); + expect(includesArmorChecksum(signatureV4V6)).to.be.true; + const signatureV6 = await openpgp.sign({ message: await openpgp.createMessage({ text: 'test' }), signingKeys: v6Key }); + expect(includesArmorChecksum(signatureV6)).to.be.false; + + const detachedSignatureV4V6 = await openpgp.sign({ message: await openpgp.createMessage({ text: 'test' }), signingKeys: [v4Key, v6Key], detached: true }); + expect(includesArmorChecksum(detachedSignatureV4V6)).to.be.true; + const detachedSignatureV6 = await openpgp.sign({ message: await openpgp.createMessage({ text: 'test' }), signingKeys: v6Key, detached: true }); + expect(includesArmorChecksum(detachedSignatureV6)).to.be.false; + + const cleartextSignatureV4V6 = await openpgp.sign({ message: await openpgp.createCleartextMessage({ text: 'test' }), signingKeys: [v4Key, v6Key] }); + expect(includesArmorChecksum(cleartextSignatureV4V6)).to.be.true; + const cleartextSignatureV6 = await openpgp.sign({ message: await openpgp.createCleartextMessage({ text: 'test' }), signingKeys: v6Key }); + expect(includesArmorChecksum(cleartextSignatureV6)).to.be.false; + }); + it('Do not add extraneous blank line when base64 ends on line break', async function () { const pubKey = `-----BEGIN PGP PUBLIC KEY BLOCK----- @@ -380,7 +342,6 @@ VWx8AtKEInT8YvN19cS2Jpr81jCN819IqgDr+YQezYMwZMzWISmA3w5Z3UCU lO771jlg4fHlWOZ2nJqselFlNc3X/VoZ8swmMkI6KVDV+rKaeyTWe61Up0Jj NJCB6+LWtabSoVIjNVgKwyKqyTLaESNwC2ogZwkdE8qPGiDFEHo4Gg9zuRof -=trqv -----END PGP PUBLIC KEY BLOCK----- `; @@ -391,7 +352,7 @@ NJCB6+LWtabSoVIjNVgKwyKqyTLaESNwC2ogZwkdE8qPGiDFEHo4Gg9zuRof .replace(/^(Version|Comment): .*$\n/mg, '') ).to.equal( pubKey - .replace('\n=', '=') + .replace('\n-', '-') .replace(/\n\r/g, '\n') ); }); diff --git a/test/general/biginteger.js b/test/general/biginteger.js deleted file mode 100644 index 10dd8446..00000000 --- a/test/general/biginteger.js +++ /dev/null @@ -1,160 +0,0 @@ -const { use: chaiUse, expect } = require('chai'); -chaiUse(require('chai-as-promised')); - -const BN = require('bn.js'); -const random = require('../../src/crypto/random'); -const util = require('../../src/util'); - -let BigInteger; - -async function getRandomBN(min, max) { - if (max.cmp(min) <= 0) { - throw new Error('Illegal parameter value: max <= min'); - } - - const modulus = max.sub(min); - const bytes = modulus.byteLength(); - const r = new BN(random.getRandomBytes(bytes + 8)); - return r.mod(modulus).add(min); -} - -module.exports = () => describe('BigInteger interface', function() { - before(async () => { - BigInteger = await util.getBigInteger(); - }); - - it('constructor throws on undefined input', function() { - expect(() => new BigInteger()).to.throw('Invalid BigInteger input'); - }); - - - it('constructor supports strings', function() { - const input = '417653931840771530406225971293556769925351769207235721650257629558293828796031115397206059067934284452829611906818956352854418342467914729341523414945427019410284762464062112274326172407819051167058569790660930309496043254270888417520676082271432948852231332576271876251597199882908964994070268531832274431027'; - const got = new BigInteger(input); - const expected = new BN(input); - expect(got.toString()).to.equal(expected.toString()); - }); - - it('constructor supports Uint8Arrays', function() { - const expected = new BN('417653931840771530406225971293556769925351769207235721650257629558293828796031115397206059067934284452829611906818956352854418342467914729341523414945427019410284762464062112274326172407819051167058569790660930309496043254270888417520676082271432948852231332576271876251597199882908964994070268531832274431027'); - const input = expected.toArrayLike(Uint8Array); - const got = new BigInteger(input); - expect(got.toString()).to.equal(expected.toString()); - }); - - it('conditional operators are correct', function() { - const a = new BigInteger(12); - const b = new BigInteger(34); - - expect(a.equal(a)).to.be.true; - expect(a.equal(b)).to.be.false; - expect(a.gt(a) === a.lt(a)).to.be.true; - expect(a.gt(b) === a.lt(b)).to.be.false; - expect(a.gte(a) === a.lte(a)).to.be.true; - - const zero = new BigInteger(0); - const one = new BigInteger(1); - expect(zero.isZero()).to.be.true; - expect(one.isZero()).to.be.false; - - expect(one.isOne()).to.be.true; - expect(zero.isOne()).to.be.false; - - expect(zero.isEven()).to.be.true; - expect(one.isEven()).to.be.false; - - expect(zero.isNegative()).to.be.false; - expect(zero.dec().isNegative()).to.be.true; - }); - - it('bitLength is correct', function() { - const n = new BigInteger(127); - let expected = 7; - expect(n.bitLength() === expected).to.be.true; - expect(n.inc().bitLength() === (++expected)).to.be.true; - }); - - it('byteLength is correct', function() { - const n = new BigInteger(65535); - let expected = 2; - expect(n.byteLength() === expected).to.be.true; - expect(n.inc().byteLength() === (++expected)).to.be.true; - }); - - it('toUint8Array is correct', function() { - const nString = '417653931840771530406225971293556769925351769207235721650257629558293828796031115397206059067934284452829611906818956352854418342467914729341523414945427019410284762464062112274326172407819051167058569790660930309496043254270888417520676082271432948852231332576271876251597199882908964994070268531832274431027'; - const n = new BigInteger(nString); - const paddedSize = Number(n.byteLength()) + 1; - // big endian, unpadded - let expected = new BN(nString).toArrayLike(Uint8Array); - expect(n.toUint8Array()).to.deep.equal(expected); - // big endian, padded - expected = new BN(nString).toArrayLike(Uint8Array, 'be', paddedSize); - expect(n.toUint8Array('be', paddedSize)).to.deep.equal(expected); - // little endian, unpadded - expected = new BN(nString).toArrayLike(Uint8Array, 'le'); - expect(n.toUint8Array('le')).to.deep.equal(expected); - //little endian, padded - expected = new BN(nString).toArrayLike(Uint8Array, 'le', paddedSize); - expect(n.toUint8Array('le', paddedSize)).to.deep.equal(expected); - }); - - it('binary operators are consistent', function() { - const a = new BigInteger(12); - const b = new BigInteger(34); - const ops = ['add', 'sub', 'mul', 'mod', 'leftShift', 'rightShift']; - ops.forEach(op => { - const iop = `i${op}`; - expect(a[op](b).equal(a[iop](b))).to.be.true; - }); - }); - - it('unary operators are consistent', function() { - const a = new BigInteger(12); - const one = new BigInteger(1); - expect(a.sub(one).equal(a.dec())).to.be.true; - expect(a.add(one).equal(a.inc())).to.be.true; - }); - - it('modExp is correct (large values)', function() { - const stringX = '417653931840771530406225971293556769925351769207235721650257629558293828796031115397206059067934284452829611906818956352854418342467914729341523414945427019410284762464062112274326172407819051167058569790660930309496043254270888417520676082271432948852231332576271876251597199882908964994070268531832274431027'; - const stringE = '21139356010872569239159922781526379521587348169074209285187910481667533072168468011617194695181255483288792585413365359733692097084373249198758148704369207793873998901870577262254971784191473102265830193058813215898765238784670469696574407580179153118937858890572095234316482449291777882525949871374961971753'; - const stringN = '129189808515414783602892982235788912674846062846614219472827821758734760420002631653235573915244294540972376140705505703576175711417114803419704967903726436285518767606681184247119430411311152556442947708732584954518890222684529678365388350886907287414896703685680210648760841628375425909680236584021041565183'; - const x = new BigInteger(stringX); - const e = new BigInteger(stringE); - const n = new BigInteger(stringN); - - const got = x.modExp(e, n); - const expected = new BN(stringX).toRed(BN.red(new BN(stringN))).redPow(new BN(stringE)); - // different formats, it's easier to compare strings - expect(got.toString() === expected.toString()).to.be.true; - }); - - it('gcd is correct', async function() { - const aBN = await getRandomBN(new BN(2), new BN(200)); - const bBN = await getRandomBN(new BN(2), new BN(200)); - if (aBN.isEven()) aBN.iaddn(1); - const a = new BigInteger(aBN.toString()); - const b = new BigInteger(bBN.toString()); - const expected = aBN.gcd(bBN); - expect(a.gcd(b).toString()).to.equal(expected.toString()); - }); - - it('modular inversion is correct', async function() { - const moduloBN = new BN(229); // this is a prime - const baseBN = await getRandomBN(new BN(2), moduloBN); - const a = new BigInteger(baseBN.toString()); - const n = new BigInteger(moduloBN.toString()); - const expected = baseBN.invm(moduloBN); - expect(a.modInv(n).toString()).to.equal(expected.toString()); - expect(() => a.mul(n).modInv(n)).to.throw('Inverse does not exist'); - }); - - it('getBit is correct', async function() { - const i = 5; - const nBN = await getRandomBN(new BN(2), new BN(200)); - const n = new BigInteger(nBN.toString()); - const expected = nBN.testn(5) ? 1 : 0; - expect(n.getBit(i) === expected).to.be.true; - }); -}); diff --git a/test/general/brainpool.js b/test/general/brainpool.js index c432b766..c50987c0 100644 --- a/test/general/brainpool.js +++ b/test/general/brainpool.js @@ -1,18 +1,19 @@ -/* globals tryTests: true */ -const { use: chaiUse, expect } = require('chai'); -chaiUse(require('chai-as-promised')); +/* globals tryTests */ +import { use as chaiUse, expect } from 'chai'; +import chaiAsPromised from 'chai-as-promised'; // eslint-disable-line import/newline-after-import +chaiUse(chaiAsPromised); -const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../..'); -const util = require('../../src/util'); +import openpgp from '../initOpenpgp.js'; +import util from '../../src/util.js'; -const input = require('./testInputs'); +import * as input from './testInputs.js'; -module.exports = () => (openpgp.config.ci ? describe.skip : describe)('Brainpool Cryptography @lightweight', function () { +export default () => (openpgp.config.ci ? describe.skip : describe)('Brainpool Cryptography @lightweight', function () { let rejectCurvesVal; before(function() { //only x25519 crypto is fully functional in lightbuild - if (!openpgp.config.useIndutnyElliptic && !util.getNodeCrypto()) { + if (!openpgp.config.useEllipticFallback && !util.getNodeCrypto()) { this.skip(); // eslint-disable-line no-invalid-this } }); @@ -255,10 +256,6 @@ EJ4QcD/oQ6x1M/8X/iKQCtxZP8RnlrbH7ExkNON5s5g= expect(await result.signatures[0].verified).to.be.true; }); it('Decrypt and verify message with leading zero in hash signed with old elliptic algorithm', async function () { - //this test would not work with nodeCrypto, since message is signed with leading zero stripped from the hash - if (util.getNodeCrypto()) { - this.skip(); // eslint-disable-line no-invalid-this - } const juliet = await load_priv_key('juliet'); const romeo = await load_pub_key('romeo'); const msg = await openpgp.readMessage({ armoredMessage: data.romeo.message_encrypted_with_leading_zero_in_hash_signed_by_elliptic_with_old_implementation }); @@ -286,7 +283,7 @@ EJ4QcD/oQ6x1M/8X/iKQCtxZP8RnlrbH7ExkNON5s5g= }); tryTests('Brainpool Omnibus Tests @lightweight', omnibus, { - if: openpgp.config.useIndutnyElliptic || util.getNodeCrypto() + if: openpgp.config.useEllipticFallback || util.getNodeCrypto() }); }); @@ -321,14 +318,13 @@ function omnibus() { signingKeys: [hi] }); // Decrypting and verifying - return openpgp.decrypt({ + const output = await openpgp.decrypt({ message: await openpgp.readMessage({ armoredMessage: encrypted }), decryptionKeys: bye, verificationKeys: [pubHi] - }).then(async output => { - expect(output.data).to.equal(testData2); - await expect(output.signatures[0].verified).to.eventually.be.true; }); + expect(output.data).to.equal(testData2); + await expect(output.signatures[0].verified).to.eventually.be.true; } finally { openpgp.config.rejectCurves = rejectCurves; } diff --git a/test/general/config.js b/test/general/config.js index 10ba476b..c9080ea0 100644 --- a/test/general/config.js +++ b/test/general/config.js @@ -1,8 +1,8 @@ -const { expect } = require('chai'); +import { expect } from 'chai'; -const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../..'); +import openpgp from '../initOpenpgp.js'; -module.exports = () => describe('Custom configuration', function() { +export default () => describe('Custom configuration', function() { it('openpgp.readMessage', async function() { const armoredMessage = await openpgp.encrypt({ message: await openpgp.createMessage({ text:'hello world' }), passwords: 'password' }); const message = await openpgp.readMessage({ armoredMessage }); @@ -116,10 +116,10 @@ n9/quqtmyOtYOA6gXNCw0Fal3iANKBmsPmYI }); it('openpgp.generateKey', async function() { - const v5KeysVal = openpgp.config.v5Keys; + const v6KeysVal = openpgp.config.v6Keys; const preferredHashAlgorithmVal = openpgp.config.preferredHashAlgorithm; const showCommentVal = openpgp.config.showComment; - openpgp.config.v5Keys = false; + openpgp.config.v6Keys = false; openpgp.config.preferredHashAlgorithm = openpgp.enums.hash.sha256; openpgp.config.showComment = false; @@ -134,7 +134,7 @@ n9/quqtmyOtYOA6gXNCw0Fal3iANKBmsPmYI expect(key.users[0].selfCertifications[0].preferredHashAlgorithms[0]).to.equal(openpgp.config.preferredHashAlgorithm); const config = { - v5Keys: true, + v6Keys: true, showComment: true, preferredHashAlgorithm: openpgp.enums.hash.sha512 }; @@ -144,11 +144,11 @@ n9/quqtmyOtYOA6gXNCw0Fal3iANKBmsPmYI }; const { privateKey: privateKeyArmored2 } = await openpgp.generateKey(opt2); const key2 = await openpgp.readKey({ armoredKey: privateKeyArmored2 }); - expect(key2.keyPacket.version).to.equal(5); + expect(key2.keyPacket.version).to.equal(6); expect(privateKeyArmored2.indexOf(openpgp.config.commentString) > 0).to.be.true; - expect(key2.users[0].selfCertifications[0].preferredHashAlgorithms[0]).to.equal(config.preferredHashAlgorithm); + expect(key2.directSignatures[0].preferredHashAlgorithms[0]).to.equal(config.preferredHashAlgorithm); } finally { - openpgp.config.v5Keys = v5KeysVal; + openpgp.config.v6Keys = v6KeysVal; openpgp.config.preferredHashAlgorithm = preferredHashAlgorithmVal; openpgp.config.showComment = showCommentVal; } @@ -272,19 +272,20 @@ n9/quqtmyOtYOA6gXNCw0Fal3iANKBmsPmYI const { packets: [skesk, encData] } = encrypted; expect(skesk.version).to.equal(4); // cfb expect(encData.constructor.tag).to.equal(openpgp.enums.packet.symEncryptedIntegrityProtectedData); + expect(encData.version).to.equal(1); const { packets: [literal] } = await encrypted.decrypt(null, passwords, null, encrypted.fromStream, openpgp.config); expect(literal.constructor.tag).to.equal(openpgp.enums.packet.literalData); const config = { aeadProtect: true, - preferredCompressionAlgorithm: openpgp.enums.compression.zip, - deflateLevel: 1 + preferredCompressionAlgorithm: openpgp.enums.compression.zip }; const armored2 = await openpgp.encrypt({ message, passwords, config }); const encrypted2 = await openpgp.readMessage({ armoredMessage: armored2 }); const { packets: [skesk2, encData2] } = encrypted2; - expect(skesk2.version).to.equal(5); - expect(encData2.constructor.tag).to.equal(openpgp.enums.packet.aeadEncryptedData); + expect(skesk2.version).to.equal(6); + expect(encData2.constructor.tag).to.equal(openpgp.enums.packet.symEncryptedIntegrityProtectedData); + expect(encData2.version).to.equal(2); const { packets: [compressed] } = await encrypted2.decrypt(null, passwords, null, encrypted2.fromStream, openpgp.config); expect(compressed.constructor.tag).to.equal(openpgp.enums.packet.compressedData); expect(compressed.algorithm).to.equal(openpgp.enums.compression.zip); @@ -297,12 +298,27 @@ n9/quqtmyOtYOA6gXNCw0Fal3iANKBmsPmYI await expect(openpgp.encrypt({ message, encryptionKeys: [key], config: { rejectCurves: new Set([openpgp.enums.curve.curve25519Legacy]) } - })).to.be.eventually.rejectedWith(/Support for ecdh keys using curve curve25519 is disabled/); + })).to.be.eventually.rejectedWith(/Support for ecdh keys using curve curve25519Legacy is disabled/); - const echdEncrypted = await openpgp.encrypt({ + await expect(openpgp.encrypt({ message, encryptionKeys: [key], config: { rejectCurves: new Set([openpgp.enums.curve.ed25519Legacy]) } - }); - expect(echdEncrypted).to.match(/---BEGIN PGP MESSAGE---/); + })).to.be.eventually.rejectedWith(/Could not verify primary key: Support for eddsaLegacy keys using curve ed25519Legacy is disabled/); + + // RSA 512 bits primary key, ECC subkey + const weakPrimaryKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PUBLIC KEY BLOCK----- + +xk0EZbuUkQECAOVRTj4yGjMTk94lHaJGJZpwAnPJzSLr0lsqRzbsaeL+JeUr +HtKQyv8wEnqN0o7j39DXdFBI8f2/T0DkC4gkbQsAEQEAAc0OPHRlc3RAdGVz +dC5pdD7CiwQQAQgAPwWCZbuUkQMLCQcJkL/AJzZtSewrBRUICgwOBBYAAgEC +GQECmwMCHgEWIQSUuxkscrvIncEIj8K/wCc2bUnsKwAABpYCAMsq3UDscj6W +IVz8+VubCuJma95dgMXjqDGd2XGLUthYzKQ+k0USut3nwrt5aJOiQGse7W9O +Mjr/KnRCNGrJdm7OOARlu5SREgorBgEEAZdVAQUBAQdAkDQHPjXorB969PXZ +p09HqVCOqcOAzKi4KLL7I3QosmsDAQgHwnYEGAEIACoFgmW7lJEJkL/AJzZt +SewrApsMFiEElLsZLHK7yJ3BCI/Cv8AnNm1J7CsAAJ6VAf9uBYUWIM2LFx1L +c1HGHD56KA0Mu4eQksKNEugotEyBuWiZCVO+LBrDUFztC1IwXaNPL3bCjYaD +5f5A+c8qOY1f +-----END PGP PUBLIC KEY BLOCK-----` }); + await expect(openpgp.encrypt({ message, encryptionKeys: weakPrimaryKey, config: { minRSABits: 2048 } })).to.be.rejectedWith(/Could not verify primary key: RSA keys shorter than 2048 bits are considered too weak./); } finally { openpgp.config.aeadProtect = aeadProtectVal; openpgp.config.preferredCompressionAlgorithm = preferredCompressionAlgorithmVal; @@ -367,10 +383,10 @@ n9/quqtmyOtYOA6gXNCw0Fal3iANKBmsPmYI await expect(openpgp.sign({ message, signingKeys: [key], config: { rejectPublicKeyAlgorithms: new Set([openpgp.enums.publicKey.eddsaLegacy]) } - })).to.be.eventually.rejectedWith(/eddsa keys are considered too weak/); + })).to.be.eventually.rejectedWith(/eddsaLegacy keys are considered too weak/); await expect(openpgp.sign({ message, signingKeys: [key], config: { rejectCurves: new Set([openpgp.enums.curve.ed25519Legacy]) } - })).to.be.eventually.rejectedWith(/Support for eddsa keys using curve ed25519 is disabled/); + })).to.be.eventually.rejectedWith(/Support for eddsaLegacy keys using curve ed25519Legacy is disabled/); }); it('openpgp.verify', async function() { @@ -414,7 +430,7 @@ n9/quqtmyOtYOA6gXNCw0Fal3iANKBmsPmYI config: { rejectPublicKeyAlgorithms: new Set([openpgp.enums.publicKey.eddsaLegacy]) } }; const { signatures: [sig4] } = await openpgp.verify(opt4); - await expect(sig4.verified).to.be.rejectedWith(/eddsa keys are considered too weak/); + await expect(sig4.verified).to.be.rejectedWith(/eddsaLegacy keys are considered too weak/); const opt5 = { message: await openpgp.readMessage({ armoredMessage: signed }), @@ -422,7 +438,7 @@ n9/quqtmyOtYOA6gXNCw0Fal3iANKBmsPmYI config: { rejectCurves: new Set([openpgp.enums.curve.ed25519Legacy]) } }; const { signatures: [sig5] } = await openpgp.verify(opt5); - await expect(sig5.verified).to.be.eventually.rejectedWith(/Support for eddsa keys using curve ed25519 is disabled/); + await expect(sig5.verified).to.be.eventually.rejectedWith(/Support for eddsaLegacy keys using curve ed25519Legacy is disabled/); }); describe('detects unknown config property', async function() { diff --git a/test/general/decompression.js b/test/general/decompression.js index d6ebbc6a..7680a73d 100644 --- a/test/general/decompression.js +++ b/test/general/decompression.js @@ -1,7 +1,8 @@ -const { use: chaiUse, expect } = require('chai'); -chaiUse(require('chai-as-promised')); +import { use as chaiUse, expect } from 'chai'; +import chaiAsPromised from 'chai-as-promised'; // eslint-disable-line import/newline-after-import +chaiUse(chaiAsPromised); -const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../..'); +import openpgp from '../initOpenpgp.js'; const password = 'I am a password'; @@ -38,7 +39,7 @@ Xg== } }; -module.exports = () => describe('Decrypt and decompress message tests', function () { +export default () => describe('Decrypt and decompress message tests', function () { function runTest(key, test) { it(`Decrypts message compressed with ${key}`, async function () { diff --git a/test/general/ecc_nist.js b/test/general/ecc_nist.js index 06287a68..5e661248 100644 --- a/test/general/ecc_nist.js +++ b/test/general/ecc_nist.js @@ -1,20 +1,21 @@ -const { use: chaiUse, expect } = require('chai'); -chaiUse(require('chai-as-promised')); +import { use as chaiUse, expect } from 'chai'; +import chaiAsPromised from 'chai-as-promised'; // eslint-disable-line import/newline-after-import +chaiUse(chaiAsPromised); -const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../..'); +import openpgp from '../initOpenpgp.js'; -const util = require('../../src/util'); +import util from '../../src/util.js'; -const input = require('./testInputs'); +import * as input from './testInputs.js'; -module.exports = () => describe('Elliptic Curve Cryptography for NIST P-256,P-384,P-521 curves @lightweight', function () { +export default () => describe('Elliptic Curve Cryptography for NIST P-256,P-384,P-521 curves @lightweight', function () { function omnibus() { it('Omnibus NIST P-256 Test', async function () { const testData = input.createSomeMessage(); const testData2 = input.createSomeMessage(); - const { privateKey: hi, publicKey: pubHi } = await openpgp.generateKey({ userIDs: { name: 'Hi', email: 'hi@hel.lo' }, curve: 'p256', format: 'object' }); - const { privateKey: bye, publicKey: pubBye } = await openpgp.generateKey({ userIDs: { name: 'Bye', email: 'bye@good.bye' }, curve: 'p256', format: 'object' }); + const { privateKey: hi, publicKey: pubHi } = await openpgp.generateKey({ userIDs: { name: 'Hi', email: 'hi@hel.lo' }, curve: 'nistP256', format: 'object' }); + const { privateKey: bye, publicKey: pubBye } = await openpgp.generateKey({ userIDs: { name: 'Bye', email: 'bye@good.bye' }, curve: 'nistP256', format: 'object' }); const cleartextMessage = await openpgp.sign({ message: await openpgp.createCleartextMessage({ text: testData }), signingKeys: hi }); await openpgp.verify({ @@ -50,7 +51,7 @@ module.exports = () => describe('Elliptic Curve Cryptography for NIST P-256,P-38 it('Sign message', async function () { const testData = input.createSomeMessage(); - const options = { userIDs: { name: 'Hi', email: 'hi@hel.lo' }, curve: 'p256', format: 'object' }; + const options = { userIDs: { name: 'Hi', email: 'hi@hel.lo' }, curve: 'nistP256', format: 'object' }; const { privateKey, publicKey } = await openpgp.generateKey(options); const signature = await openpgp.sign({ message: await openpgp.createCleartextMessage({ text: testData }), signingKeys: privateKey }); const msg = await openpgp.readCleartextMessage({ cleartextMessage: signature }); @@ -60,9 +61,9 @@ module.exports = () => describe('Elliptic Curve Cryptography for NIST P-256,P-38 it('Encrypt and sign message', async function () { const testData = input.createSomeMessage(); - let options = { userIDs: { name: 'Hi', email: 'hi@hel.lo' }, curve: 'p256', format: 'object' }; + let options = { userIDs: { name: 'Hi', email: 'hi@hel.lo' }, curve: 'nistP256', format: 'object' }; const firstKey = await openpgp.generateKey(options); - options = { userIDs: { name: 'Bye', email: 'bye@good.bye' }, curve: 'p256', format: 'object' }; + options = { userIDs: { name: 'Bye', email: 'bye@good.bye' }, curve: 'nistP256', format: 'object' }; const secondKey = await openpgp.generateKey(options); const encrypted = await openpgp.encrypt({ message: await openpgp.createMessage({ text: testData }), @@ -74,5 +75,44 @@ module.exports = () => describe('Elliptic Curve Cryptography for NIST P-256,P-38 expect(await result.signatures[0].verified).to.be.true; }); + it('should decrypt a message using the correct fingerprint size in the KDF (v6 key)', async function() { + // this test is to ensure the KDF function uses the correct fingerprint size (the fingerprint should not be truncated) + const key = await openpgp.readKey({ + armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xXkGZoVjGhMAAABMCCqGSM49AwEHAgMEUqR9vqdSZv8I+DGuSOYUSf4cNVlE +H16loiqRcAsDY9SHSTVHQkEWbc63HyEvV3jGSbSk2dNF64faN3nbhlZ0PgAB +APcoOjqcdJ9/LHRgxWvSbrKAmKNm0yJE9U9DY9hwshqhwqEGHxMIAAAAPgWC +ZoVjGgMLCQcFFQgKDA4EFgACAQKbAwIeASKhBk+e6Xq0rbnjKzVy/3Qitc2h +eW/w/IuxgPXjJW3nfTRxAAAAABQOEKxf0tyJS3Pbs1xApVxWKP4BAM8Bkygn +ddtiBifou11xgxOjT0y0CsbjIKyOnPTvIh/4AQCfyLJIAmQUN36mSInEepvy +NVk8jmweVYOCT8RluvFtG80OPHRlc3RAdGVzdC5pdD7CjwYTEwgAAAAsBYJm +hWMaAhkBIqEGT57perStueMrNXL/dCK1zaF5b/D8i7GA9eMlbed9NHEAAAAA +g9UQJqaRsvniF1WYuuRLpqMpOAEAvAhGhNpom/L2iIZLCpeyFCfGe5VDUBQB +1cjGpTbnrJoBAIjy1tgUH1gjixchymNf5LfUqwdXwEiLfv2f/Iq+KEX/x30G +ZoVjGhIAAABQCCqGSM49AwEHAgMESZrMsc0UrXB5/C8FHXAepykqAyueem7p +cjVvWFP9V59w/O/VXVyJBrZqleN0w/KexznRyzvQjH36HRlwVFwJ5QMBCAcA +AQDiiISRsjcPcaGXSAEYmvd80nH1oP8CJ/TQsi8od5nhqMKPBhgTCAAAACwF +gmaFYxoCmwwioQZPnul6tK254ys1cv90IrXNoXlv8PyLsYD14yVt5300cQAA +AAC2GhBn4S5eLyGPjccfUkFRKKWmAP4iHESir/KDsmsfhE5m/RwQcy7feCl7 +2bny7QRNGY8dFQD8CwmHJ0EvMDQcvVWPrj8WdgPblJEEgWd9AUItEFcDee0= +-----END PGP PRIVATE KEY BLOCK-----` + }); + const message = await openpgp.readMessage({ + armoredMessage: `-----BEGIN PGP MESSAGE----- + +wX4DYKEfntV7jkcSAgMEXplJPwjsvhh7xNeBeZtgepG1f0hUaW4eoeFCDpYH +IOr2RZFgRd6KbtmNsI1saqDwDg7EjFk+AWOe7av2xcFStTDfz+9mus03A6tk +7mPFWGsDUrxP2b+tyO6ofr9I4gyj5tI2X7R94AfRWgQxy+O2PvLSNAFXcx4o +SsrtSQmZUKpxuBROy+bZNheNgmN966vqnFBiM1vXikv5OVyprUV0EzzQ3Hnt +69s= +=0Agg +-----END PGP MESSAGE-----` + }); + const decrypted = await openpgp.decrypt({ message, decryptionKeys: key }); + expect(decrypted.data).to.equal('abc'); + }); + + // TODO find test vectors }); diff --git a/test/general/ecc_secp256k1.js b/test/general/ecc_secp256k1.js index a8d63741..8b4674d8 100644 --- a/test/general/ecc_secp256k1.js +++ b/test/general/ecc_secp256k1.js @@ -1,11 +1,12 @@ -const { use: chaiUse, expect } = require('chai'); -chaiUse(require('chai-as-promised')); +import { use as chaiUse, expect } from 'chai'; +import chaiAsPromised from 'chai-as-promised'; // eslint-disable-line import/newline-after-import +chaiUse(chaiAsPromised); -const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../..'); -const util = require('../../src/util'); +import openpgp from '../initOpenpgp.js'; +import util from '../../src/util.js'; -module.exports = () => describe('Elliptic Curve Cryptography for secp256k1 curve @lightweight', function () { - if (!openpgp.config.useIndutnyElliptic && !util.getNodeCrypto()) { +export default () => describe('Elliptic Curve Cryptography for secp256k1 curve @lightweight', function () { + if (!openpgp.config.useEllipticFallback && !util.getNodeCrypto()) { before(function() { this.skip(); // eslint-disable-line no-invalid-this }); diff --git a/test/general/index.js b/test/general/index.js index 01a81a86..478a0c5a 100644 --- a/test/general/index.js +++ b/test/general/index.js @@ -1,18 +1,31 @@ -module.exports = () => describe('General', function () { - require('./util')(); - require('./biginteger')(); - require('./armor')(); - require('./packet')(); - require('./signature')(); - require('./key')(); - require('./openpgp')(); - require('./config')(); - require('./oid')(); - require('./ecc_nist')(); - require('./ecc_secp256k1')(); - require('./x25519')(); - require('./brainpool')(); - require('./decompression')(); - require('./streaming')(); -}); +import testX25519 from './x25519.js'; +import testUtil from './util.js'; +import testArmor from './armor.js'; +import testPacket from './packet.js'; +import testSignature from './signature.js'; +import testKey from './key.js'; +import testOpenPGP from './openpgp.js'; +import testConfig from './config.js'; +import testOID from './oid.js'; +import testNistECC from './ecc_nist.js'; +import testSecp256k1 from './ecc_secp256k1.js'; +import testBrainpool from './brainpool.js'; +import testDecompression from './decompression.js'; +import testStreaming from './streaming.js'; +export default () => describe('General', function () { + testX25519(); + testUtil(); + testArmor(); + testPacket(); + testSignature(); + testKey(); + testOpenPGP(); + testConfig(); + testOID(); + testNistECC(); + testSecp256k1(); + testBrainpool(); + testDecompression(); + testStreaming(); +}); diff --git a/test/general/key.js b/test/general/key.js index c75aaa3e..ff94ecab 100644 --- a/test/general/key.js +++ b/test/general/key.js @@ -1,13 +1,14 @@ /* eslint-disable max-lines */ -/* globals tryTests: true */ -const { use: chaiUse, expect } = require('chai'); -chaiUse(require('chai-as-promised')); - -const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../..'); -const util = require('../../src/util'); -const { isAEADSupported, getPreferredAlgo } = require('../../src/key'); -const KeyID = require('../../src/type/keyid'); +/* globals tryTests */ +import { use as chaiUse, expect } from 'chai'; +import chaiAsPromised from 'chai-as-promised'; // eslint-disable-line import/newline-after-import +chaiUse(chaiAsPromised); +import sinon from 'sinon'; +import openpgp from '../initOpenpgp.js'; +import util from '../../src/util.js'; +import { getPreferredCipherSuite, getPreferredHashAlgo } from '../../src/key'; +import KeyID from '../../src/type/keyid.js'; const priv_key_arm2 = ['-----BEGIN PGP PRIVATE KEY BLOCK-----', @@ -2239,32 +2240,36 @@ VFBLG8uc9IiaKann/DYBAJcZNZHRSfpDoV2pUA5EAEi2MdjxkRysFQnYPRAu function versionSpecificTests() { it('Preferences of generated key', function() { const testPref = function(key) { + const selfSignature = openpgp.config.v6Keys ? key.directSignatures[0] : key.users[0].selfCertifications[0]; // key flags const keyFlags = openpgp.enums.keyFlags; - expect(key.users[0].selfCertifications[0].keyFlags[0] & keyFlags.certifyKeys).to.equal(keyFlags.certifyKeys); - expect(key.users[0].selfCertifications[0].keyFlags[0] & keyFlags.signData).to.equal(keyFlags.signData); + expect(selfSignature.keyFlags[0] & keyFlags.certifyKeys).to.equal(keyFlags.certifyKeys); + expect(selfSignature.keyFlags[0] & keyFlags.signData).to.equal(keyFlags.signData); expect(key.subkeys[0].bindingSignatures[0].keyFlags[0] & keyFlags.encryptCommunication).to.equal(keyFlags.encryptCommunication); expect(key.subkeys[0].bindingSignatures[0].keyFlags[0] & keyFlags.encryptStorage).to.equal(keyFlags.encryptStorage); const sym = openpgp.enums.symmetric; - expect(key.users[0].selfCertifications[0].preferredSymmetricAlgorithms).to.eql([sym.aes256, sym.aes128, sym.aes192]); + expect(selfSignature.preferredSymmetricAlgorithms).to.eql([sym.aes256, sym.aes128]); if (openpgp.config.aeadProtect) { const aead = openpgp.enums.aead; - expect(key.users[0].selfCertifications[0].preferredAEADAlgorithms).to.eql([aead.eax, aead.ocb]); + expect(selfSignature.preferredCipherSuites).to.eql([ + [sym.aes256, aead.gcm], + [sym.aes128, aead.gcm], + [sym.aes256, aead.eax], + [sym.aes128, aead.eax], + [sym.aes256, aead.ocb], + [sym.aes128, aead.ocb] + ]); } const hash = openpgp.enums.hash; - expect(key.users[0].selfCertifications[0].preferredHashAlgorithms).to.eql([hash.sha256, hash.sha512]); + expect(selfSignature.preferredHashAlgorithms).to.eql([hash.sha512, hash.sha256, hash.sha3_256, hash.sha3_512]); const compr = openpgp.enums.compression; - expect(key.users[0].selfCertifications[0].preferredCompressionAlgorithms).to.eql([compr.uncompressed, compr.zlib, compr.zip]); + expect(selfSignature.preferredCompressionAlgorithms).to.eql([compr.uncompressed, compr.zlib, compr.zip]); - let expectedFeatures; - if (openpgp.config.v5Keys) { - expectedFeatures = [7]; // v5 + aead + mdc - } else if (openpgp.config.aeadProtect) { - expectedFeatures = [3]; // aead + mdc - } else { - expectedFeatures = [1]; // mdc + let expectedFeatures = 0x01; // SEIPDv1 + if (openpgp.config.aeadProtect) { + expectedFeatures |= 0x08; // SEIPDv2 } - expect(key.users[0].selfCertifications[0].features).to.eql(expectedFeatures); + expect(selfSignature.features).to.eql([expectedFeatures]); }; const opt = { userIDs: { name: 'test', email: 'a@b.com' }, passphrase: 'hello', format: 'object' }; return openpgp.generateKey(opt).then(async function({ privateKey, publicKey }) { @@ -2280,36 +2285,46 @@ function versionSpecificTests() { const preferredAEADAlgorithmVal = openpgp.config.preferredAEADAlgorithm; openpgp.config.preferredSymmetricAlgorithm = openpgp.enums.symmetric.aes192; openpgp.config.preferredHashAlgorithm = openpgp.enums.hash.sha224; - openpgp.config.preferredCompressionAlgorithm = openpgp.enums.compression.zip; + openpgp.config.preferredCompressionAlgorithm = openpgp.enums.compression.zlib; openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.experimentalGCM; const testPref = function(key) { + const selfSignature = openpgp.config.v6Keys ? key.directSignatures[0] : key.users[0].selfCertifications[0]; // key flags const keyFlags = openpgp.enums.keyFlags; - expect(key.users[0].selfCertifications[0].keyFlags[0] & keyFlags.certifyKeys).to.equal(keyFlags.certifyKeys); - expect(key.users[0].selfCertifications[0].keyFlags[0] & keyFlags.signData).to.equal(keyFlags.signData); + expect(selfSignature.keyFlags[0] & keyFlags.certifyKeys).to.equal(keyFlags.certifyKeys); + expect(selfSignature.keyFlags[0] & keyFlags.signData).to.equal(keyFlags.signData); expect(key.subkeys[0].bindingSignatures[0].keyFlags[0] & keyFlags.encryptCommunication).to.equal(keyFlags.encryptCommunication); expect(key.subkeys[0].bindingSignatures[0].keyFlags[0] & keyFlags.encryptStorage).to.equal(keyFlags.encryptStorage); const sym = openpgp.enums.symmetric; - expect(key.users[0].selfCertifications[0].preferredSymmetricAlgorithms).to.eql([sym.aes192, sym.aes256, sym.aes128]); + expect(selfSignature.preferredSymmetricAlgorithms).to.eql([sym.aes192, sym.aes256, sym.aes128]); if (openpgp.config.aeadProtect) { const aead = openpgp.enums.aead; - expect(key.users[0].selfCertifications[0].preferredAEADAlgorithms).to.eql([aead.experimentalGCM, aead.eax, aead.ocb]); + expect(selfSignature.preferredCipherSuites).to.eql([ + [sym.aes192, aead.experimentalGCM], + [sym.aes256, aead.experimentalGCM], + [sym.aes128, aead.experimentalGCM], + [sym.aes192, aead.gcm], + [sym.aes256, aead.gcm], + [sym.aes128, aead.gcm], + [sym.aes192, aead.eax], + [sym.aes256, aead.eax], + [sym.aes128, aead.eax], + [sym.aes192, aead.ocb], + [sym.aes256, aead.ocb], + [sym.aes128, aead.ocb] + ]); } const hash = openpgp.enums.hash; - expect(key.users[0].selfCertifications[0].preferredHashAlgorithms).to.eql([hash.sha224, hash.sha256, hash.sha512]); + expect(selfSignature.preferredHashAlgorithms).to.eql([hash.sha224, hash.sha256, hash.sha512, hash.sha3_256, hash.sha3_512]); const compr = openpgp.enums.compression; - expect(key.users[0].selfCertifications[0].preferredCompressionAlgorithms).to.eql([compr.zip, compr.zlib, compr.uncompressed]); + expect(selfSignature.preferredCompressionAlgorithms).to.eql([compr.zlib, compr.uncompressed, compr.zip]); - let expectedFeatures; - if (openpgp.config.v5Keys) { - expectedFeatures = [7]; // v5 + aead + mdc - } else if (openpgp.config.aeadProtect) { - expectedFeatures = [3]; // aead + mdc - } else { - expectedFeatures = [1]; // mdc + let expectedFeatures = 0x01; // SEIPDv1 + if (openpgp.config.aeadProtect) { + expectedFeatures |= 0x08; // SEIPDv2 } - expect(key.users[0].selfCertifications[0].features).to.eql(expectedFeatures); + expect(selfSignature.features).to.eql([expectedFeatures]); }; const opt = { userIDs: { name: 'test', email: 'a@b.com' }, passphrase: 'hello', format: 'object' }; try { @@ -2447,34 +2462,40 @@ function versionSpecificTests() { }); it('Generate key - default values', function() { + const v6Key = openpgp.config.v6Keys; + const userID = { name: 'test', email: 'a@b.com' }; const opt = { userIDs: [userID], format: 'object' }; return openpgp.generateKey(opt).then(function({ privateKey: key }) { + expect(key.keyPacket.version).to.equal(v6Key ? 6 : 4); expect(key.isDecrypted()).to.be.true; - expect(key.getAlgorithmInfo().algorithm).to.equal('eddsa'); + expect(key.getAlgorithmInfo().algorithm).to.equal(v6Key ? 'ed25519' : 'eddsaLegacy'); expect(key.users.length).to.equal(1); expect(key.users[0].userID.userID).to.equal('test '); expect(key.users[0].selfCertifications[0].isPrimaryUserID).to.be.true; expect(key.subkeys).to.have.length(1); - expect(key.subkeys[0].getAlgorithmInfo().algorithm).to.equal('ecdh'); + expect(key.subkeys[0].getAlgorithmInfo().algorithm).to.equal(v6Key ? 'x25519' : 'ecdh'); }); }); it('Generate key - two subkeys with default values', function() { + const v6Key = openpgp.config.v6Keys; + const userID = { name: 'test', email: 'a@b.com' }; const opt = { userIDs: [userID], passphrase: '123', format: 'object', subkeys:[{},{}] }; return openpgp.generateKey(opt).then(function({ privateKey: key }) { + expect(key.keyPacket.version).to.equal(v6Key ? 6 : 4); expect(key.users.length).to.equal(1); expect(key.users[0].userID.userID).to.equal('test '); expect(key.users[0].selfCertifications[0].isPrimaryUserID).to.be.true; expect(key.subkeys).to.have.length(2); - expect(key.subkeys[0].getAlgorithmInfo().algorithm).to.equal('ecdh'); - expect(key.subkeys[1].getAlgorithmInfo().algorithm).to.equal('ecdh'); + expect(key.subkeys[0].getAlgorithmInfo().algorithm).to.equal(v6Key ? 'x25519' : 'ecdh'); + expect(key.subkeys[1].getAlgorithmInfo().algorithm).to.equal(v6Key ? 'x25519' : 'ecdh'); }); }); it('Generate RSA key - two subkeys with default values', async function() { - const rsaBits = 512; + const rsaBits = 1024; const minRSABits = openpgp.config.minRSABits; openpgp.config.minRSABits = rsaBits; @@ -2493,46 +2514,99 @@ function versionSpecificTests() { } }); + it('Generate Ed25519 key (new format) - default subkey', async function() { + const userID = { name: 'test', email: 'a@b.com' }; + const opt = { type: 'curve25519', userIDs: [userID], passphrase: '123', format: 'object' }; + const { privateKey: key } = await openpgp.generateKey(opt); + expect(key.users.length).to.equal(1); + expect(key.users[0].userID.userID).to.equal('test '); + expect(key.users[0].selfCertifications[0].isPrimaryUserID).to.be.true; + expect(key.getAlgorithmInfo().algorithm).to.equal('ed25519'); + expect(key.subkeys).to.have.length(1); + expect(key.subkeys[0].getAlgorithmInfo().algorithm).to.equal('x25519'); + }); + + it('Generate Ed25519 key (new format) - one signing subkey', async function() { + const userID = { name: 'test', email: 'a@b.com' }; + const opt = { type: 'curve25519', userIDs: [userID], passphrase: '123', format: 'object', subkeys:[{ sign: true }] }; + const { privateKey: key } = await openpgp.generateKey(opt); + expect(key.users.length).to.equal(1); + expect(key.users[0].userID.userID).to.equal('test '); + expect(key.users[0].selfCertifications[0].isPrimaryUserID).to.be.true; + expect(key.getAlgorithmInfo().algorithm).to.equal('ed25519'); + expect(key.subkeys).to.have.length(1); + expect(key.subkeys[0].getAlgorithmInfo().algorithm).to.equal('ed25519'); + }); + + it('Generate Ed448 key - default subkey', async function() { + const userID = { name: 'test', email: 'a@b.com' }; + const opt = { type: 'curve448', userIDs: [userID], passphrase: '123', format: 'object' }; + const { privateKey: key } = await openpgp.generateKey(opt); + expect(key.users.length).to.equal(1); + expect(key.users[0].userID.userID).to.equal('test '); + expect(key.users[0].selfCertifications[0].isPrimaryUserID).to.be.true; + expect(key.getAlgorithmInfo().algorithm).to.equal('ed448'); + expect(key.subkeys).to.have.length(1); + expect(key.subkeys[0].getAlgorithmInfo().algorithm).to.equal('x448'); + }); + + it('Generate Ed448 key - one signing subkey', async function() { + const userID = { name: 'test', email: 'a@b.com' }; + const opt = { type: 'curve448', userIDs: [userID], passphrase: '123', format: 'object', subkeys:[{ sign: true }] }; + const { privateKey: key } = await openpgp.generateKey(opt); + expect(key.users.length).to.equal(1); + expect(key.users[0].userID.userID).to.equal('test '); + expect(key.users[0].selfCertifications[0].isPrimaryUserID).to.be.true; + expect(key.getAlgorithmInfo().algorithm).to.equal('ed448'); + expect(key.subkeys).to.have.length(1); + expect(key.subkeys[0].getAlgorithmInfo().algorithm).to.equal('ed448'); + }); + it('Generate key - one signing subkey', function() { + const v6Key = openpgp.config.v6Keys; const userID = { name: 'test', email: 'a@b.com' }; const opt = { userIDs: [userID], passphrase: '123', format: 'object', subkeys:[{}, { sign: true }] }; + return openpgp.generateKey(opt).then(async function({ privateKey: key }) { + expect(key.keyPacket.version).to.equal(v6Key ? 6 : 4); expect(key.users.length).to.equal(1); expect(key.users[0].userID.userID).to.equal('test '); expect(key.users[0].selfCertifications[0].isPrimaryUserID).to.be.true; expect(key.subkeys).to.have.length(2); - expect(key.subkeys[0].getAlgorithmInfo().algorithm).to.equal('ecdh'); + expect(key.subkeys[0].getAlgorithmInfo().algorithm).to.equal(v6Key ? 'x25519' : 'ecdh'); expect(await key.getEncryptionKey()).to.equal(key.subkeys[0]); - expect(key.subkeys[1].getAlgorithmInfo().algorithm).to.equal('eddsa'); + expect(key.subkeys[1].getAlgorithmInfo().algorithm).to.equal(v6Key ? 'ed25519' : 'eddsaLegacy'); expect(await key.getSigningKey()).to.equal(key.subkeys[1]); }); }); it('Reformat key - one signing subkey', async function() { + const v6Key = openpgp.config.v6Keys; const userID = { name: 'test', email: 'a@b.com' }; const opt = { userIDs: [userID], format: 'object', subkeys:[{}, { sign: true }] }; const { privateKey } = await openpgp.generateKey(opt); return openpgp.reformatKey({ privateKey, userIDs: [userID] }).then(async function({ privateKey: armoredKey }) { const key = await openpgp.readKey({ armoredKey }); + expect(key.keyPacket.version).to.equal(v6Key ? 6 : 4); expect(key.users.length).to.equal(1); expect(key.users[0].userID.userID).to.equal('test '); expect(key.users[0].selfCertifications[0].isPrimaryUserID).to.be.true; expect(key.subkeys).to.have.length(2); - expect(key.subkeys[0].getAlgorithmInfo().algorithm).to.equal('ecdh'); + expect(key.subkeys[0].getAlgorithmInfo().algorithm).to.equal(v6Key ? 'x25519' : 'ecdh'); expect(await key.getEncryptionKey()).to.equal(key.subkeys[0]); - expect(key.subkeys[1].getAlgorithmInfo().algorithm).to.equal('eddsa'); + expect(key.subkeys[1].getAlgorithmInfo().algorithm).to.equal(v6Key ? 'ed25519' : 'eddsaLegacy'); expect(await key.getSigningKey()).to.equal(key.subkeys[1]); }); }); it('Generate key - override main RSA key options for subkey', async function() { - const rsaBits = 512; + const rsaBits = 1024; const minRSABits = openpgp.config.minRSABits; openpgp.config.minRSABits = rsaBits; const userID = { name: 'test', email: 'a@b.com' }; - const opt = { type: 'rsa', rsaBits, userIDs: [userID], passphrase: '123', format: 'object', subkeys:[{ type: 'ecc', curve: 'curve25519' }] }; + const opt = { type: 'rsa', rsaBits, userIDs: [userID], passphrase: '123', format: 'object', subkeys:[{ type: 'ecc', curve: 'nistP256' }] }; try { const { privateKey: key } = await openpgp.generateKey(opt); expect(key.users.length).to.equal(1); @@ -2877,46 +2951,33 @@ function versionSpecificTests() { await expect(privateKey.verifyPrimaryKey()).to.be.fulfilled; }); }); - - - it('Parses V5 sample key', async function() { - // sec ed25519 2019-03-20 [SC] - // 19347BC9872464025F99DF3EC2E0000ED9884892E1F7B3EA4C94009159569B54 - // uid emma.goldman@example.net - // ssb cv25519 2019-03-20 [E] - // E4557C2B02FFBF4B04F87401EC336AF7133D0F85BE7FD09BAEFD9CAEB8C93965 - const key = await openpgp.readKey({ armoredKey: v5_sample_key }); - expect(await key.keyPacket.getFingerprint()).to.equal('19347bc9872464025f99df3ec2e0000ed9884892e1f7b3ea4c94009159569b54'); - expect(await key.subkeys[0].getFingerprint()).to.equal('e4557c2b02ffbf4b04f87401ec336af7133d0f85be7fd09baefd9caeb8c93965'); - await key.verifyPrimaryKey(); - }); } -module.exports = () => describe('Key', function() { - let v5KeysVal; +export default () => describe('Key', function() { + let v6KeysVal; let aeadProtectVal; tryTests('V4', versionSpecificTests, { - if: !openpgp.config.ci, + if: true, beforeEach: function() { - v5KeysVal = openpgp.config.v5Keys; - openpgp.config.v5Keys = false; + v6KeysVal = openpgp.config.v6Keys; + openpgp.config.v6Keys = false; }, afterEach: function() { - openpgp.config.v5Keys = v5KeysVal; + openpgp.config.v6Keys = v6KeysVal; } }); - tryTests('V5', versionSpecificTests, { - if: !openpgp.config.ci, + tryTests('V6', versionSpecificTests, { + if: true, beforeEach: function() { - v5KeysVal = openpgp.config.v5Keys; + v6KeysVal = openpgp.config.v6Keys; aeadProtectVal = openpgp.config.aeadProtect; - openpgp.config.v5Keys = true; + openpgp.config.v6Keys = true; openpgp.config.aeadProtect = true; }, afterEach: function() { - openpgp.config.v5Keys = v5KeysVal; + openpgp.config.v6Keys = v6KeysVal; openpgp.config.aeadProtect = aeadProtectVal; } }); @@ -2958,6 +3019,18 @@ module.exports = () => describe('Key', function() { expect(key.write()).to.deep.equal(expectedSerializedKey.data); }); + it('Parses V5 sample key', async function() { + // sec ed25519 2019-03-20 [SC] + // 19347BC9872464025F99DF3EC2E0000ED9884892E1F7B3EA4C94009159569B54 + // uid emma.goldman@example.net + // ssb cv25519 2019-03-20 [E] + // E4557C2B02FFBF4B04F87401EC336AF7133D0F85BE7FD09BAEFD9CAEB8C93965 + const key = await openpgp.readKey({ armoredKey: v5_sample_key, config: { enableParsingV5Entities: true } }); + expect(await key.keyPacket.getFingerprint()).to.equal('19347bc9872464025f99df3ec2e0000ed9884892e1f7b3ea4c94009159569b54'); + expect(await key.subkeys[0].getFingerprint()).to.equal('e4557c2b02ffbf4b04f87401ec336af7133d0f85be7fd09baefd9caeb8c93965'); + await key.verifyPrimaryKey(); + }); + it('Parsing V5 public key packet', async function() { // Manually modified from https://gitlab.com/openpgp-wg/rfc4880bis/blob/00b2092/back.mkd#sample-eddsa-key const packetBytes = util.hexToUint8Array(` @@ -2972,6 +3045,311 @@ module.exports = () => describe('Key', function() { expect(key).to.exist; }); + it('Parsing, decrypting, encrypting and serializing V5 key (AEAD-encrypted)', async function() { + const armoredKey = `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xYwFZC7tvxYAAAAtCSsGAQQB2kcPAQEHQP/d1oBAqCKZYxb6k8foyX2Aa/VK +dHFymZPGvHRk1ncs/R0JAQMIrDnS3Bany9EAF6dwQSfPSdObc4ROYIMAnwAA +ADKV1OhGzwANnapimvODI6fK5F7/V0GxETY9WmnipnBzr4Fe9GZw4QD4Q4hd +IJMawjUBrs0MdjVAYWVhZC50ZXN0wpIFEBYKAEQFgmQu7b8ECwkHCAMVCAoE +FgACAQIZAQKbAwIeByKhBQ/Y89PNwfdXUdI/td5Q9rNrYP9mb7Dg6k/3nxTg +ugQ5AyIBAgAAf0kBAJv0OQvd4u8R0f3HAsmQeqMnwNA4or75BOn/ieApNZUt +AP9kQVmYEk4+MV57Us15l2kQEslLDr3qiH5+VCICdEprB8eRBWQu7b8SAAAA +MgorBgEEAZdVAQUBAQdA4IgEkfze3eNKRz6DgzGSJxw/CV/5Rp5u4Imn47h7 +pyADAQgH/R0JAQMIwayD3R4E0ugAyszSmOIpaLJ40YGBp5uU7wAAADKmSv4W +tio7GfZCVl8eJ7xX3J1b0iMvEm876tUeHANQlYYCWz+2ahmPVe79zzZA9OhN +FcJ6BRgWCAAsBYJkLu2/ApsMIqEFD9jz083B91dR0j+13lD2s2tg/2ZvsODq +T/efFOC6BDkAAHcjAPwIPNHnR9bKmkVop6cE05dCIpZ/W8zXDGnjKYrrC4Hb +4gEAmISD1GRkNOmCV8aHwN5svO6HuwXR4cR3o3l7HlYeag8= +=wpkQ +-----END PGP PRIVATE KEY BLOCK-----`; + const passphrase = 'password'; + const encryptedKey = await openpgp.readKey({ armoredKey, config: { enableParsingV5Entities: true } }); + const decryptedKey = await openpgp.decryptKey({ + privateKey: encryptedKey, + passphrase + }); + const reecryptedKey = await openpgp.encryptKey({ + privateKey: decryptedKey, + passphrase, + config: { aeadProtect: true } + }); + expect(reecryptedKey.keyPacket.s2kUsage).to.equal(253); + const redecryptedKey = await openpgp.decryptKey({ + privateKey: reecryptedKey, + passphrase + }); + expect(redecryptedKey.write()).to.deep.equal(decryptedKey.write()); + }); + + it('Parsing, decrypting, encrypting and serializing V4 key (AEAD-encrypted, deprecated/legacy format from RFC4880bis)', async function() { + // v4 key from OpenPGP.js v5, generated with config.aeadProtect flag (https://www.ietf.org/archive/id/draft-ietf-openpgp-rfc4880bis-10.html#section-5.5.3-3.5) + const armoredKey = `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xYMEZPXfehYJKwYBBAHaRw8BAQdAw/62MUaSzRSY5gR18DOlfeo/m8eKUkbr +ZSRqS4wtss39CQEDCGHd70SYYYPkAAALGg1YptTjEuIk4wiBreDE9U/urf3J +6Z8fP3oy+plzBRKs+8k+kzXY/643Ayesfy3qXA4zM6fqNrrlS6AqT8wDys0A +wpAEEBYKAEIFgmT133oECwkHCAmQB4Z/qOo0isgDFQgKBBYAAgECGQECmwMC +HgMWIQQm7YhFQvi0bx7ixrQHhn+o6jSKyAMiAQIAADQZAP9kECruRBva4izE +9ZET1iQ6i1HiisUKrO6miHfjsxDycgD9EXvtbpi1AORIrYO/pPpGUHpUt25n +JM+rgWhJwOHw1g3HiARk9d96EgorBgEEAZdVAQUBAQdAiVNiLZRC9wZG9/ef +V9eu8VKEoHqAFjci3Lu2N+8hQQoDAQgH/QkBAwh+kYDhbTGARwBZRY0lR39D +EriFZ1N5PKW1TAdxTMNecP3sOyUWRutHQgRrxuF0512fCnelgr2a3of5bQHC +0XWFfbac2d91VEP2mqrCeAQYFggAKgWCZPXfegmQB4Z/qOo0isgCmwwWIQQm +7YhFQvi0bx7ixrQHhn+o6jSKyAAAkN4A/31Hm3vy7FHFGJh+VYdqmeESo7mr +18jzxSbxd71FGTw7AQDqfERTB7zZzk1EqNSAqfrg3hbI7+4XXgHz6qnA3vFm +Cg== +=mTGB +-----END PGP PRIVATE KEY BLOCK-----`; + const binaryKey = (await openpgp.unarmor(armoredKey)).data; + const passphrase = 'passphrase'; + const encryptedKey = await openpgp.readKey({ armoredKey, config: { parseAEADEncryptedV4KeysAsLegacy: true } }); + expect(encryptedKey.keyPacket.isLegacyAEAD).to.be.true; + expect(encryptedKey.keyPacket.usedModernAEAD).to.be.false; // legacy AEAD does not guarantee integrity of public key material + expect(encryptedKey.write()).to.deep.equal(binaryKey); + + const decryptedKey = await openpgp.decryptKey({ + privateKey: encryptedKey, + passphrase + }); + const reecryptedKey = await openpgp.encryptKey({ + privateKey: decryptedKey, + passphrase, + config: { aeadProtect: true } + }); + expect(reecryptedKey.keyPacket.s2kUsage).to.equal(253); + expect(reecryptedKey.keyPacket.isLegacyAEAD).to.be.false; + const redecryptedKey = await openpgp.decryptKey({ + privateKey: reecryptedKey, + passphrase + }); + expect(redecryptedKey.write()).to.deep.equal(decryptedKey.write()); + }); + + it('Parsing, decrypting, encrypting and serializing V5 key (AEAD-encrypted, deprecated/legacy format from RFC4880bis)', async function() { + // v5 key from OpenPGP.js v5, generated with config.aeadProtect flag (https://www.ietf.org/archive/id/draft-ietf-openpgp-rfc4880bis-10.html#section-5.5.3-3.5) + const armoredKey = `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xYwFZoaoyxYAAAAtCSsGAQQB2kcPAQEHQECiSN62X9PnTUoE9cx6aRxqh2aR +piHEjy2dtbhOsC7X/R0JAQMI3R4vBY0cUVnglteNXGCjgMgSTO3VeB70tgAA +ADIUlEIeqChwz1NRWl0WafC7vLrhIwgzW4dfRLqWU/tcDxhQji8oOdMihMVH +gOT1M/58zs0EVGVzdMKSBRAWCgBEBYJmhqjLBAsJBwgDFQgKBBYAAgECGQEC +mwMCHgcioQVBp/l8xtMGAhUT99DQhSQ8spB7ILxEALjWUfi5ODEQZgMiAQIA +ABJ7AQDbdXScaIjOUmKjsX1pTeDPfIPEWJSBY5n4e9tKMoFLuAD+ISyssmch +WjtxzfvElCc4/QL7P4yv7VBCHgVMfdBIggPHkQVmhqjLEgAAADIKKwYBBAGX +VQEFAQEHQL5K5HBcf0/GTcajPc3xeNNQQhJfT0TsmcorbEWV73FZAwEIB/0d +CQEDCJ2Wqcffz5cT4LmtIq4KlZUR8vlQrKcWF5MAAAAyiqtRwe6bSZ94e8Yt +1O6D4oH37UnCkKEuDQJb3G4SvHw4lJdlehfRFxndhHTuTVNQW9zCegUYFgoA +LAWCZoaoywKbDCKhBUGn+XzG0wYCFRP30NCFJDyykHsgvEQAuNZR+Lk4MRBm +AADOyAEA0VMzgtpSnXOfPNvVjOOW3yW/DnHSnOWjLmUujTLYXf0A/0nHjVMI +yrHaO8+1bQew7SIS9kYr1sh/z7LKooqYHBwH +=Woga +-----END PGP PRIVATE KEY BLOCK-----`; + const binaryKey = (await openpgp.unarmor(armoredKey)).data; + const passphrase = 'passphrase'; + const encryptedKey = await openpgp.readKey({ armoredKey, config: { enableParsingV5Entities: true } }); + expect(encryptedKey.keyPacket.isLegacyAEAD).to.be.true; + expect(encryptedKey.keyPacket.usedModernAEAD).to.be.false; // legacy AEAD does not guarantee integrity of public key material + expect(encryptedKey.write()).to.deep.equal(binaryKey); + + const decryptedKey = await openpgp.decryptKey({ + privateKey: encryptedKey, + passphrase + }); + const reecryptedKey = await openpgp.encryptKey({ + privateKey: decryptedKey, + passphrase, + config: { aeadProtect: true } + }); + expect(reecryptedKey.keyPacket.s2kUsage).to.equal(253); + expect(reecryptedKey.keyPacket.isLegacyAEAD).to.be.true; + const redecryptedKey = await openpgp.decryptKey({ + privateKey: reecryptedKey, + passphrase + }); + expect(redecryptedKey.write()).to.deep.equal(decryptedKey.write()); + }); + + it('Parsing, decrypting, encrypting and serializing V4 key (AEAD-encrypted)', async function() { + // key from gopenpgp + const armoredKey = `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xcMIBGQuuRABCADm+/vQI1Memff31qyXxdB4Z14hnF+Bu7RirXpYNjM07CcGTDdT +vj1AmZNR0o3G1vvbAUp2jWxquWq+8C//NJn13Axrg3R599j3+1TL+9vlwmgSJJdT +SjQkSUjlkJpZQJPfkk0tngLBQGwlEJJLOlnWLfCc1eTh5x/cjO5E/jOOHwSHNgBp +mhpKO/k6bAdgyB6jAYOJKI6TZE0JNc2+ZGSjr5EdwEs8sGDT2nMgn3oectuZO4y8 +tmFzNvAY9oQD0T4wmqwZ8evzlgRkCMRrdKCCdHfYdluQuRJb2WOWoV03PRaPfRQS +k2SiNSKYhQF2tnybd1BgnAiPcwa4dJtn1IWrABEBAAH9BwME1X5xBykUBjyv9kd4 +gx/UewMEEHfi7UejYVJhtGf63vgzp98C00CiDkYbCJXh2Io4Pro1lP7J75hm7zyf +1VuNQbd8dx2IEcig8OpF+tlH14U+YexVrbm1rX/vBg0BZrqO87HU+ILiZFpGV/tF +9GeLPBLLCyIqvb/PzP0hiqEHP84xBkIIOEY+PZJoXGpfA7TUNsGpVS9ySpGWxCny +nsjv9Lnv2NVtfaaA8YoQl7GJbI/Qh9wx/wtiqE8sVuH9ddFdFGSvjhrLSu58jPIv +9SBdMjI/WHVlqVXkAXpEPBlmpn4xgffW3HDAx19YuwHEVjrsLUISBi1PodfAieT/ +cdtqejiFLQv8zQkTOz/J59yUy+OUZ3SKBM3vRPf0lxSUAoNNYrvg0gNd6qpCNChN +z7LjNkUjgDp0DorPtTLT4FS/O/kB25K69CxkUeOyk3i+p3fqr/9wz0gFpRW5pkLa +Hi3T5gjT4O1kTyGeoetGKwbdzfLisc981ynqKhlLdBw0R0hMpalak3NOf3QUjZEu +10TFHhGUuCJVNbluQwVSD9e5znu5IBxawo8yHcV8OEIcc8wS1TuJer/cWj9zf/3Y +C/l5Gngwa99YE8nrZdhKlra0viiAvpPqJs61pOzGj5NoKoEPDWB26TpbrPGFyKu6 +EY8Uz1SNo+Zn42w1g4KTA2x4LPdyblYlea5RRqodqot9hgRMVy758QwMBmoLzwn3 +sSOZeasCF5pw4a1Trr+Qupy0N+TyoCvt7hlP3qt12+8Y7ObB5hAk9YHlWB/mXeGK +APA0n6o2eTKBrXcjAk600nn30BH93GQ88LxwPsF2IKcwqf8sBlm3IPzmQUbGTtfr +lcm7PTipnN9NyGZrimbS9Eujp6IEAQGsPT9VqWBf2xM18kLnkYWO3Q+iQxhoyeHU +R+SpZ3rzZ7dqJKzNF2R1bW15IDxkdW1teUB0ZXN0LnRlc3Q+wsCLBBMBCAA/BQJk +LrkQCZD/Lbr4zUX6OxYhBEJ8H0lz9aZ8pbX+hP8tuvjNRfo7AhsDAh4JAhkBAgsH +AhUIAhYABScHAwcCAAA3RAgA2+RQ/U9FYhTghvU/2r/SDiL1BRA+TOOwDKyxLKKm +J9j/f/GSon74YqZmWSZTWLgDxXGXO0+I9Mz029qEs/tQTcFrulJcxY6V5B6ci+Wv +J9+7A4UDz7wk30jb0FKT6NDhw/w2UbI5tf9aUY+iKxqcvDI3zBL3AMkILPKK+kXw +dq5DvbRIh3oUcD3+xhEnlkBWbB9oUcQC0QdC9bHdPNTPGNJLQozo+cSq/VMYn5Bj +RPJQSoSA6BJa4omdNi1GkVoYNmnBVi8W+DnqgwwOxOhlbTRHyhoKC8pbGC/ty/qd +HLWrGbFXOcl1cVio85zT74q98v+tL6CEKDHTire1tbKhy8fDCARkLrkQAQgAwbzO +crec+eXvoxyL/woFffGBKoMICXFGYiZvd0mI7iMYDRy2oVBIZuT5fAorSfc8PUYS +lljlV7LP9WW1/IA9oPRSTj0bywqZrxRVaIzBqoXNtpujnyPpFHDzubxkNr+WcbmQ +KufphQMolp2p0LQ7C6c6ssAKS6ue8mNJ1KRvdvRXMUqop+fGaEKoec+PgRUwIKDq +sLVAzGtGkJTC2J0w9673ZzxlbejHj/g/eEHFSwTm92E2q2UbSoJLV7dtpAR4y1i3 +GTZSNsPm3Wngn4C8AQtuZyqcFJiTvcrMJDptRsQ9pwkyDquEd0fsJel5pY1WQiXr +g4UZDLQ4QmIIwFdbnwARAQAB/QcDBOMwz+uO8Knhstz1WDIJvPoDBBA+WqgPij7y +RQz9nTftmWIPUVvrOC49h66Smv3eDVikCq2ibFj6znpvDZgp7LWly0OAfHLHf/qg +4x7ld0miXTSe4ZeCTo2qsh8gKqqW/CLgSgnixSjdyyqHBLvCS3dPbwrjjeI+qPuS +EcuDzRqDhwfs6eUCei2lDwOYlm69WkT73Ll2EoJUZOVkxrqHkfY5hakQZbMBy6gs +VqCzaLOaqHaBrg6c2KqWEZ6WB2KasT/p5fuW+aoYqNbQibmic1H1ETGjnUlVWhwf +4aySRvbaTw3DzJXduZsJEQSq2Dv04JM/InxZEvh+FLXluccv6Os6MqZnKEST/e7f +zL6G4zphIFecrOqvvl/ej1UlOXCqUfOn3Srsy8AjLOvPJJ13VBPFo6Lz+P+5RcUX +VauY/vepsjecrcY2BaANct6BNdL0rgRkoT2HZ2g8snvWl+UVTZnwsjnwEZYYazrK +C6woDti14bn0Mc71kaeNTog0FU/nqfP1exMiV87H+EU04XcyGn8b0oSSI3DcEDau +SV9qwksQcqF28fDbQz5h9UsEdWjjSYQmNpF4Iow6t18buspqSRbEZXap8Vt5tLAr +9t1CV9vIKNMU7JIodZngUxITMZYZyVHHbTidu3rzv9ojsAMvFElr590yIk8FPsDD +m3vnKlNHT7B8/irI8gyhLGlF+mwGEROM1PSeNNq6ufV74DWh0C4RpdzLfgzd8AqL +bxX1kOOzC6kVjwa8lCowMRS4d9Kah8jRoOgx9Az/GSJ2ODBXYGGcOwF1ERDU8P7i +IsAVjFZ2OeC5E36MHSiP60rRe4i/NGJOgY6pY3mwTdCFtUdnRv6ASc6k4TOQGMup +xgGFJ0ph68AtRheZ0IdN/VXMQfseyzufb5bq0Yc3yb9spogH7sY4IplxvEtMwsB2 +BBgBCAAqBQJkLrkQCZD/Lbr4zUX6OxYhBEJ8H0lz9aZ8pbX+hP8tuvjNRfo7AhsM +AABaJgf/dTX0lJCphR9DlppTFNhcwOdtmvJf9CPP8+vHpPjyL5fiB4wDPCU1C7x1 +ku/QS00EKIpPP1EbDUsY0jIN7IV24x0eQcAswIV1F63Bzfft1rWZsA5iiZms1bgh +AEA3Kv2Xh7DUaiykaXvbtyfCI6pX+MgMZsLqVhFEH/5lq+dlYc8UyM7IE3LNWYj3 +Uluz+3GjCdLZ8FVJVTrRZz8wR8HDlcPdC60gqnnx6QQ4rmzYoivK0Rf/4LLjujOc +VjyzpPJS+t/gabeMRho7vChSge603d227AKpJtQnfUKN3mjN1i/XQ3iIFlVAGlGA +oZIvKIVq9Vqf8XJVjMDbRMNTmh3a5A== +-----END PGP PRIVATE KEY BLOCK-----`; + const passphrase = 'password'; + const encryptedKey = await openpgp.readKey({ armoredKey }); + const decryptedKey = await openpgp.decryptKey({ + privateKey: encryptedKey, + passphrase + }); + const reecryptedKey = await openpgp.encryptKey({ + privateKey: decryptedKey, + passphrase, + config: { aeadProtect: true } + }); + expect(reecryptedKey.keyPacket.s2kUsage).to.equal(253); + const redecryptedKey = await openpgp.decryptKey({ + privateKey: reecryptedKey, + passphrase + }); + expect(redecryptedKey.write()).to.deep.equal(decryptedKey.write()); + }); + + it('Parsing, decrypting, encrypting and serializing V6 key (AEAD-encrypted)', async function() { + // official test vector from https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#appendix-A.5 + const armoredKey = `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xYIGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laP9JgkC +FARdb9ccngltHraRe25uHuyuAQQVtKipJ0+r5jL4dacGWSAheCWPpITYiyfyIOPS +3gIDyg8f7strd1OB4+LZsUhcIjOMpVHgmiY/IutJkulneoBYwrEGHxsKAAAAQgWC +Y4d/4wMLCQcFFQoOCAwCFgACmwMCHgkiIQbLGGxPBgmml+TVLfpscisMHx4nwYpW +cI9lJewnutmsyQUnCQIHAgAAAACtKCAQPi19In7A5tfORHHbNr/JcIMlNpAnFJin +7wV2wH+q4UWFs7kDsBJ+xP2i8CMEWi7Ha8tPlXGpZR4UruETeh1mhELIj5UeM8T/ +0z+5oX1RHu11j8bZzFDLX9eTsgOdWATHggZjh3/jGQAAACCGkySDZ/nlAV25Ivj0 +gJXdp4SYfy1ZhbEvutFsr15ENf0mCQIUBA5hhGgp2oaavg6mFUXcFMwBBBUuE8qf +9Ock+xwusd+GAglBr5LVyr/lup3xxQvHXFSjjA2haXfoN6xUGRdDEHI6+uevKjVR +v5oAxgu7eJpaXNjCmwYYGwoAAAAsBYJjh3/jApsMIiEGyxhsTwYJppfk1S36bHIr +DB8eJ8GKVnCPZSXsJ7rZrMkAAAAABAEgpukYbZ1ZNfyP5WMUzbUnSGpaUSD5t2Ki +Nacp8DkBClZRa2c3AMQzSDXa9jGhYzxjzVb5scHDzTkjyRZWRdTq8U6L4da+/+Kt +ruh8m7Xo2ehSSFyWRSuTSZe5tm/KXgYG +-----END PGP PRIVATE KEY BLOCK-----`; + const passphrase = 'correct horse battery staple'; + const encryptedKey = await openpgp.readKey({ armoredKey }); + + // avoid argon2's expensive computation + const stubArgon2PrimaryKey = sinon.stub(encryptedKey.keyPacket.s2k, 'produceKey').returns( + util.hexToUint8Array('832bd2662a5c2b251ee3fc82aec349a766ca539015880133002e5a21960b3bcf')); + const stubArgon2Subkey = sinon.stub(encryptedKey.subkeys[0].keyPacket.s2k, 'produceKey').returns( + util.hexToUint8Array('f74a6ce873a089ef13a3da9ac059777bb22340d15eaa6c9dc0f8ef09035c67cd')); + + try { + const decryptedKey = await openpgp.decryptKey({ + privateKey: encryptedKey, + passphrase + }); + + const reecryptedKey = await openpgp.encryptKey({ + privateKey: decryptedKey, + passphrase, + config: { aeadProtect: true } + }); + expect(reecryptedKey.keyPacket.s2kUsage).to.equal(253); + const redecryptedKey = await openpgp.decryptKey({ + privateKey: reecryptedKey, + passphrase + }); + expect(redecryptedKey.write()).to.deep.equal(decryptedKey.write()); + } finally { + stubArgon2PrimaryKey.restore(); + stubArgon2Subkey.restore(); + } + }); + + it('Parsing EdDSALegacy key with unsupported OID (Curve448Legacy)', async function() { + // Key with unknown OID (corresponding to curve448) which is not supported for EdDSALegacy + const armoredEdDSALegacyCurve448Key = `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xYUEYV2UmRYDK2VxAc9AFyxgh5xnSbyt50TWl558mw9xdMN+/UBLr5+UMP8IsrvV +MdXuTIE8CyaUQKSotHtH2RkYEXj5nsMAAAHPQIbTMSzjIWug8UFECzAex5FHgAgH +gYF3RK+TS8D24wX8kOu2C/NoVxwGY+p+i0JHaB+7yljriSKAGxs6wsBEBB8WCgCD +BYJhXZSZBYkFpI+9AwsJBwkQppmYlfq6zlJHFAAAAAAAHgAgc2FsdEBub3RhdGlv +bnMuc2VxdW9pYS1wZ3Aub3Jn5wSpIutJ5HncJWk4ruUV8GzQF390rR5+qWEAnAoY +akcDFQoIApsBAh4BFiEEwdtl1YDXuSJyVEseppmYlfq6zlIAALzdA5dA/fsgYg/J +qaQriYKaPUkyHL7EB3BXhV2d1h/gk+qJLvXQuU2WEJ/XSs3GrsBRiiZwvPH4o+7b +mleAxjy5wpS523vqrrBR2YZ5FwIku7WS4litSdn4AtVam/TlLdMNIf41CtFeZKBe +c5R5VNdQy8y7qy8AAADNEUN1cnZlNDQ4IE9wdGlvbiA4wsBHBBMWCgCGBYJhXZSZ +BYkFpI+9AwsJBwkQppmYlfq6zlJHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2Vx +dW9pYS1wZ3Aub3JnD55UsYMzE6OACP+mgw5zvT+BBgol8/uFQjHg4krjUCMDFQoI +ApkBApsBAh4BFiEEwdtl1YDXuSJyVEseppmYlfq6zlIAAPQJA5dA0Xqwzn/0uwCq +RlsOVCB3f5NOj1exKnlBvRw0xT1VBee1yxvlUt5eIAoCxWoRlWBJob3TTkhm9AEA +8dyhwPmyGfWHzPw5NFG3xsXrZdNXNvit9WMVAPcmsyR7teXuDlJItxRAdJJc/qfJ +YVbBFoaNrhYAAADHhQRhXZSZFgMrZXEBz0BL7THZ9MnCLfSPJ1FMLim9eGkQ3Bfn +M3he5rOwO3t14QI1LjI96OjkeJipMgcFAmEP1Bq/ZHGO7oAAAc9AFnE8iNBaT3OU +EFtxkmWHXtdaYMmGGRdopw9JPXr/UxuunDln5o9dxPxf7q7z26zXrZen+qed/Isa +HsDCwSwEGBYKAWsFgmFdlJkFiQWkj70JEKaZmJX6us5SRxQAAAAAAB4AIHNhbHRA +bm90YXRpb25zLnNlcXVvaWEtcGdwLm9yZxREUizdTcepBzgSMOv2VWQCWbl++3CZ +EbgAWDryvSsyApsCwDGgBBkWCgBvBYJhXZSZCRBKo3SL4S5djkcUAAAAAAAeACBz +YWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmemoGTDjmNQiIzw6HOEddvS0OB7 +UZ/P07jM/EVmnYxTlBYhBAxsnkGpx1UCiH6gUUqjdIvhLl2OAAALYQOXQAMB1oKq +OWxSFmvmgCKNcbAAyA3piF5ERIqs4z07oJvqDYrOWt75UsEIH/04gU/vHc4EmfG2 +JDLJgOLlyTUPkL/08f0ydGZPofFQBhn8HkuFFjnNtJ5oz3GIP4cdWMQFaUw0uvjb +PM9Tm3ptENGd6Ts1AAAAFiEEwdtl1YDXuSJyVEseppmYlfq6zlIAAGpTA5dATR6i +U2GrpUcQgpG+JqfAsGmF4yAOhgFxc1UfidFk3nTup3fLgjipkYY170WLRNbyKkVO +Sodx93GAs58rizO1acDAWiLq3cyEPBFXbyFThbcNPcLl+/77Uk/mgkYrPQFAQWdK +1kSRm4SizDBK37K8ChAAAADHhwRhXZSZEgMrZW8Bx0DMhzvhQo+OsXeqQ6QVw4sF +CaexHh6rLohh7TzL3hQSjoJ27fV6JBkIWdn0LfrMlJIDbSv2SLdlgQMBCgkAAcdA +MO7Dc1myF6Co1fAH+EuP+OxhxP/7V6ljuSCZENDfA49tQkzTta+PniG+pOVB2LHb +huyaKBkqiaogo8LAOQQYFgoAeAWCYV2UmQWJBaSPvQkQppmYlfq6zlJHFAAAAAAA +HgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3JnEjBMQAmc/2u45u5FQGmB +QAytjSG2LM3JQN+PPVl5vEkCmwwWIQTB22XVgNe5InJUSx6mmZiV+rrOUgAASdYD +l0DXEHQ9ykNP2rZP35ET1dmiFagFtTj/hLQcWlg16LqvJNGqOgYXuqTerbiOOt02 +XLCBln+wdewpU4ChEffMUDRBfqfQco/YsMqWV7bHJHAO0eC/DMKCjyU90xdH7R/d +QgqsfguR1PqPuJxpXV4bSr6CGAAAAA== +=MSvh +-----END PGP PRIVATE KEY BLOCK----- +`; + await expect(openpgp.readKey({ armoredKey: armoredEdDSALegacyCurve448Key })).to.be.rejectedWith(/No key packet found/); + + await expect(openpgp.readKey({ + armoredKey: armoredEdDSALegacyCurve448Key, + config: { ignoreUnsupportedPackets: false } + })).to.be.rejectedWith(/Unknown curve OID/); + }); + it('Parsing ECDH key with unknown kdf param version', async function() { // subkey with unknown kdfParam version 255. Parsing should not fail, the subkey should simply dropped const key = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- @@ -3038,6 +3416,66 @@ aU71tdtNBQ== expect(encryptionKey.getAlgorithmInfo()).to.deep.equal({ algorithm: 'x25519' }); }); + it('Parsing V4 key using curve448 format', async function() { + const privateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xX0GZRqLYhwAAAA52IEq/TpKiPp6RofQaq4uhCruTtiG+qiVFnwsQgeh0ui34kHD +Y1E04mBai0pCoDiFVokwsKt3F5sAAC8lDYfVP/p3atbXJDTJB2W9WmZxIS7pUGhS +bjlWpZB/OVTBsoIfP/2J+Hi4ESwBRfDUDgwK4aJVKsLAIAYfHAoAAAA/BQJlGoti +IqEGobsxt8WKsMuJWANyTXpWMdC1QN/7EyJClfcs+nBgqdUCGwMCHgkCCwcDFQoI +AhYABScHAwcCAAAAAPiGIG2qmhCULQ/+H4rKV0XEM1x0uVY3l878Pa6ijZLouZU/ +VRd5PnbGyLPL++q3LDViUUdZ1uusRc01f677Q6wpUU90k8MH/oULwI0+KPtqe1N4 +6nr1NTERsAmAaPjUdf4ZUXX/GWiTd/AlsS5JqGnAQxKRJkzCJacOTOElRMjzGUX7 +CGaAnhSC86YRZ68ocTPfZysAzRdVc2VyQiA8VXNlckJAdGVzdC50ZXN0PsLADQYT +HAoAAAAsBQJlGotiIqEGobsxt8WKsMuJWANyTXpWMdC1QN/7EyJClfcs+nBgqdUC +GQEAAAAASKwgVzMoPb2Hbr3lbNI1CRWECokYLokL7F8MbYiMnlg+v6QXLdStvT13 +ZjxdrWQAx3MbihSOUSXbdAys90yMOAdtognj+x418J/TaYFMtIGBHwoHv8gQVnx9 +9ICv8ezx1T5VvGBYNuKZ5Ww0WPEpYMf1VA+Y9JxpohdcRenNBdSug4tLWla2y8NH +aO28Fltpb4AuGQDHewZlGotiGgAAADjdabr1ohAOnbSUUkVhtUM/LVdnYgDLhmaj +YZ1N7TWY0fqEpMk2LLo2165HOmhddRPeTB1TWbuwBwB8lKc3czFUzYcAgvZ08T5S +UUHjfIhjeJeY4yd0OZDfzPw1vbegCc7t94bT+XGoIQbC/Bl7HCyAiMLADQYYHAoA +AAAsBQJlGotiIqEGobsxt8WKsMuJWANyTXpWMdC1QN/7EyJClfcs+nBgqdUCGwwA +AAAAHh0gf2kdqLoXdFX+aNVORr5VCVgcm2gBw8l68lDJ1ftA71bMllFi6Q5sLPUr +6UCpJYYk3o3hYUYzIHmCIZGVYe1pxRgIUNqxydXEfzylJmN5NbiJNkjJizrI7oAR +1mIcEEb/hmRMOUs1V2mcGuoeALBI/r/SyqDE2GRjH6d6g1RS7ZARPPHlZlY4CTqC +4a7L+8odDwA= +=chx0 +-----END PGP PRIVATE KEY BLOCK----- + ` }); + // sanity checks + await expect(privateKey.validate()).to.be.fulfilled; + const signingKey = await privateKey.getSigningKey(); + expect(signingKey.keyPacket.algorithm).to.equal(openpgp.enums.publicKey.ed448); + expect(signingKey.getAlgorithmInfo()).to.deep.equal({ algorithm: 'ed448' }); + + const encryptionKey = await privateKey.getEncryptionKey(); + expect(encryptionKey.keyPacket.algorithm).to.equal(openpgp.enums.publicKey.x448); + expect(encryptionKey.getAlgorithmInfo()).to.deep.equal({ algorithm: 'x448' }); + }); + + it('Throw when parsing x448 key with unexpected secret param size', async function() { + // x448 subkey with secret seed of 57 bytes instead of 56 + const armoredKey = `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xXsEZRwKeRx0854v253kTyH54UIFSy3dLbMLTSIGh+UeC6IWXvxt2551rUee +wn9y5hJbJwm/f2eA3vkOUqhKbAAAy7hGpOu61AMTr6w9G9VLStDR9Int/vgi +cNSl1LTvF8f5lqBrpMFFPUHwi4igNMqb7I/c7J2Uc+4uInvNAMK3BBAcCgA7 +BYJlHAp5AwsJBwmQ+2DdQup2xx8DFQgKAhYAAhkBApsDAh4BFiEE5NkqBdRy +GYLB7cPm+2DdQup2xx8AAIuONFkN6wRtRJA9EJvwhj7DkzNRjFNw8OE/ENzj +3XcN/WtZYCnLZ+ih9HSar9+CZzI+4mHtvOunq7sAjuvPbGndbbdg46DSy0Ac +wIVxSeIMNpwpktMyUx/ugIZeu7VvcnW4SbQOEB5KPlja/qjapWwg4wIAx3oE +ZRwKeRogjMz3j2jL4X1Zhk+i/EK09BTU/2zuYuB+Pl9Y+RKDaxuOmZ4zzx+S +xa/RYWEVKkcIY9pBAxd4RgDZs0rJP9DRIe69vix1Wd/LxuSctG2SMfcjzyAl +5mmCsb+sgubDduEBotTv3qFnNTYMUUHEFojWC4EfjcKmBBgcCgAqBYJlHAp5 +CZD7YN1C6nbHHwKbDBYhBOTZKgXUchmCwe3D5vtg3ULqdscfAAD/uWh1fZy7 +hMeb7552mWqB0eGXpOJR9K/rLDj8woLkXJMyyhfYU5PTwmRpowsGwbm7TMku +gXxMryvfgBDKTN8tkgJ4BJUsDDwU7aJE1fzOZ5TP4iNHpPOY1qqpmaAtTh6Q +PzIEeL7UH3trraFmi+Gq8u4kAA== +-----END PGP PRIVATE KEY BLOCK-----`; + + await expect(openpgp.readKey({ armoredKey })).to.be.rejectedWith(/Error reading MPIs/); + }); + it('Testing key ID and fingerprint for V4 keys', async function() { const pubKeysV4 = await openpgp.readKeys({ armoredKeys: twoKeys }); expect(pubKeysV4).to.exist; @@ -3101,7 +3539,7 @@ aU71tdtNBQ== expect(selfCertification.valid).to.be.true; const certifyingKey = await openpgp.readKey({ armoredKey: certifying_key }); - const certifyingSigningKey = await certifyingKey.getSigningKey(); + const certifyingSigningKey = await certifyingKey.getSigningKey(undefined, undefined, undefined, { ...openpgp.config, allowMissingKeyFlags: true }); const signatures = await pubKey.verifyPrimaryUser([certifyingKey]); expect(signatures.length).to.equal(2); expect(signatures[0].keyID.toHex()).to.equal(publicSigningKey.getKeyID().toHex()); @@ -3110,7 +3548,9 @@ aU71tdtNBQ== expect(signatures[1].valid).to.be.false; const { user } = await pubKey.getPrimaryUser(); - await expect(user.verifyCertificate(user.otherCertifications[0], [certifyingKey], undefined, openpgp.config)).to.be.rejectedWith('User certificate is revoked'); + await expect( + user.verifyCertificate(user.otherCertifications[0], [certifyingKey], undefined, { ...openpgp.config, allowMissingKeyFlags: true }) + ).to.be.rejectedWith('User certificate is revoked'); } finally { openpgp.config.rejectPublicKeyAlgorithms = rejectPublicKeyAlgorithms; } @@ -3139,7 +3579,7 @@ aU71tdtNBQ== }); it('should pad an ECDSA P-521 key with shorter secret key', async function() { - const key = await openpgp.readKey({ armoredKey: shortP521Key }); + const key = await openpgp.readKey({ armoredKey: shortP521Key, config: { enableParsingV5Entities: true } }); // secret key should be padded expect(key.keyPacket.privateParams.d.length === 66); // sanity check @@ -3152,7 +3592,7 @@ aU71tdtNBQ== await expect(openpgp.decrypt({ message: await openpgp.readMessage({ armoredMessage: encryptedRsaSignOnly }), decryptionKeys: key - })).to.be.rejectedWith(/Session key decryption failed/); + })).to.be.rejectedWith(/No decryption key packets found/); await expect(openpgp.decrypt({ message: await openpgp.readMessage({ armoredMessage: encryptedRsaSignOnly }), @@ -3161,6 +3601,11 @@ aU71tdtNBQ== })).to.be.fulfilled; }); + it('PrivateKey.getDecryptionKeys() - should throw for sign-only key', async function() { + const key = await openpgp.readKey({ armoredKey: rsaSignOnly }); + await expect(key.getDecryptionKeys()).to.be.rejectedWith(/No decryption key packets found/); + }); + it('Key.getExpirationTime()', async function() { const [, pubKey] = await openpgp.readKeys({ armoredKeys: twoKeys }); expect(pubKey).to.exist; @@ -3209,7 +3654,7 @@ aU71tdtNBQ== }); it("validate() - don't throw if key parameters correspond", async function() { - const { privateKey: key } = await openpgp.generateKey({ userIDs: {}, curve: 'ed25519', format: 'object' }); + const { privateKey: key } = await openpgp.generateKey({ userIDs: {}, curve: 'ed25519Legacy', format: 'object' }); await expect(key.validate()).to.not.be.rejected; }); @@ -3233,9 +3678,72 @@ aU71tdtNBQ== await expect(key.validate()).to.be.rejectedWith('Key is invalid'); }); + it('validate() - should skip for AEAD-encrypted key (non-legacy)', async function() { + const v4KeyWithAEAD = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xYMEZaAFFxYJKwYBBAHaRw8BAQdA/C3ybUC91HiWAGd/KtPtjmYwWk7VmB+X +GXhQY9yyZkf9CQEDCBpqDPXSr+aPAGoAsXz/V2uY7EE0Xlutx3MVMEbqWz6m +fRRUn3/mtGr+PCdj9bbl0rVK+fR62kTbwATUpNdL4rrt1cNgOTlDtq1ZCc0P +PGFlYWRAdGVzdC5jb20+wpsEEBYKAE0FgmWgBRcDCwkHCZB2ZM+malVbdgUV +CAoMDgQWAAIBAhkBApsDAh4JFiEE8e8z3IJRZ+bZze8ldmTPpmpVW3YNJwkD +BwMJAQcBCQIHAgAAplYBALLKSm4Q0dPoX4mBgEuOMgtAEewfyUhp8MJdJvCa +9KIMAP9f3Qxf4ykXQwgL/e1pn1nNQgiQ7x33LXQ2vHynPkOyDceIBGWgBRcS +CisGAQQBl1UBBQEBB0Bfk/JMj2ONL/1hi31q3OePnDHLmo8IsafeG0RZY8wF +OgMBCAf9CQEDCHS8bQidEDvkAP6LovPpHodzcF7F+zbKlJKTI3l6xK4t2Dj6 +BIgBQov9zJ4OK2xFbyraXSLqStQJOQV7XBfIYKHYdIBtxj6cfTYtBMJ4BBgW +CgAqBYJloAUXCZB2ZM+malVbdgKbDBYhBPHvM9yCUWfm2c3vJXZkz6ZqVVt2 +AAA+gQD9EpMMjlBFvvyACsKwQRmIqUNTfCy4uHL1Ee1fJ4ur9ZQBAP2CiOSN +CNa5yq6lyexhsn2Vs8DsX+SOSUyNJiy5FyIJ +-----END PGP PRIVATE KEY BLOCK-----` }); + const passphrase = 'passphrase'; + // sanity checks about key encryption mechanism + expect(v4KeyWithAEAD.keyPacket.aead).to.not.be.null; + expect(v4KeyWithAEAD.keyPacket.isLegacyAEAD).to.be.false; + expect(v4KeyWithAEAD.keyPacket.usedModernAEAD).to.be.true; + + const decryptedKey = await openpgp.decryptKey({ privateKey: v4KeyWithAEAD, passphrase }); + decryptedKey.keyPacket.privateParams.seed = new Uint8Array(1); // corrupt key to confirm that the actual validation is skipped + await expect(decryptedKey.validate()).to.be.fulfilled; + }); + + it('validate() - should be run if AEAD-encrypted key gets re-encrypted without AEAD', async function() { + const v4KeyWithAEAD = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xYMEZaAFFxYJKwYBBAHaRw8BAQdA/C3ybUC91HiWAGd/KtPtjmYwWk7VmB+X +GXhQY9yyZkf9CQEDCBpqDPXSr+aPAGoAsXz/V2uY7EE0Xlutx3MVMEbqWz6m +fRRUn3/mtGr+PCdj9bbl0rVK+fR62kTbwATUpNdL4rrt1cNgOTlDtq1ZCc0P +PGFlYWRAdGVzdC5jb20+wpsEEBYKAE0FgmWgBRcDCwkHCZB2ZM+malVbdgUV +CAoMDgQWAAIBAhkBApsDAh4JFiEE8e8z3IJRZ+bZze8ldmTPpmpVW3YNJwkD +BwMJAQcBCQIHAgAAplYBALLKSm4Q0dPoX4mBgEuOMgtAEewfyUhp8MJdJvCa +9KIMAP9f3Qxf4ykXQwgL/e1pn1nNQgiQ7x33LXQ2vHynPkOyDceIBGWgBRcS +CisGAQQBl1UBBQEBB0Bfk/JMj2ONL/1hi31q3OePnDHLmo8IsafeG0RZY8wF +OgMBCAf9CQEDCHS8bQidEDvkAP6LovPpHodzcF7F+zbKlJKTI3l6xK4t2Dj6 +BIgBQov9zJ4OK2xFbyraXSLqStQJOQV7XBfIYKHYdIBtxj6cfTYtBMJ4BBgW +CgAqBYJloAUXCZB2ZM+malVbdgKbDBYhBPHvM9yCUWfm2c3vJXZkz6ZqVVt2 +AAA+gQD9EpMMjlBFvvyACsKwQRmIqUNTfCy4uHL1Ee1fJ4ur9ZQBAP2CiOSN +CNa5yq6lyexhsn2Vs8DsX+SOSUyNJiy5FyIJ +-----END PGP PRIVATE KEY BLOCK-----` }); + const passphrase = 'passphrase'; + // sanity checks about key encryption mechanism + expect(v4KeyWithAEAD.keyPacket.aead).to.not.be.null; + expect(v4KeyWithAEAD.keyPacket.isLegacyAEAD).to.be.false; + expect(v4KeyWithAEAD.keyPacket.usedModernAEAD).to.be.true; + + const reEncryptedKey = await openpgp.encryptKey({ + privateKey: await openpgp.decryptKey({ privateKey: v4KeyWithAEAD, passphrase }), + passphrase, + config: { aeadProtect: false } + }); + + expect(reEncryptedKey.keyPacket.usedModernAEAD).to.be.false; + const reDecryptedKey = await openpgp.decryptKey({ privateKey: reEncryptedKey, passphrase }); + reDecryptedKey.keyPacket.privateParams.seed = new Uint8Array(1); // corrupt key to confirm that the actual validation is now run + await expect(reDecryptedKey.validate()).to.be.rejectedWith('Key is invalid'); + }); + it('isDecrypted() - should reflect whether all (sub)keys are encrypted', async function() { const passphrase = '12345678'; - const { privateKey: key } = await openpgp.generateKey({ userIDs: {}, curve: 'ed25519', passphrase, format: 'object' }); + const { privateKey: key } = await openpgp.generateKey({ userIDs: {}, curve: 'ed25519Legacy', passphrase, format: 'object' }); expect(key.isDecrypted()).to.be.false; await key.subkeys[0].keyPacket.decrypt(passphrase); expect(key.isDecrypted()).to.be.true; @@ -3615,7 +4123,7 @@ aU71tdtNBQ== const input = await openpgp.unarmor(revocation_certificate_arm4); const packetlist = await openpgp.PacketList.fromBinary(input.data, util.constructAllowedPackets([openpgp.SignaturePacket]), openpgp.config); - const armored = openpgp.armor(openpgp.enums.armor.publicKey, packetlist.write()); + const armored = openpgp.armor(openpgp.enums.armor.publicKey, packetlist.write(), undefined, undefined, undefined, true); expect(revocationCertificate.replace(/^Comment: .*$\n/mg, '')).to.equal(armored.replace(/^Comment: .*$\n/mg, '')); }); @@ -3628,76 +4136,152 @@ aU71tdtNBQ== expect(revKey.armor()).not.to.match(/Comment: This is a revocation certificate/); }); - it("getPreferredAlgo('symmetric') - one key", async function() { - const [key1] = await openpgp.readKeys({ armoredKeys: twoKeys }); - const prefAlgo = await getPreferredAlgo('symmetric', [key1], undefined, undefined, { - ...openpgp.config, preferredSymmetricAlgorithm: openpgp.enums.symmetric.aes256 + describe('getPreferredCipherSuite()', () => { + it('getPreferredCipherSuite - one key', async function() { + const [key1] = await openpgp.readKeys({ armoredKeys: twoKeys }); + const { symmetricAlgo, aeadAlgo } = await getPreferredCipherSuite([key1], undefined, undefined, { + ...openpgp.config, preferredSymmetricAlgorithm: openpgp.enums.symmetric.aes256 + }); + expect(symmetricAlgo).to.equal(openpgp.enums.symmetric.aes256); + expect(aeadAlgo).to.equal(undefined); }); - expect(prefAlgo).to.equal(openpgp.enums.symmetric.aes256); - }); - it("getPreferredAlgo('symmetric') - two key", async function() { - const { aes128, aes192, cast5 } = openpgp.enums.symmetric; - const [key1, key2] = await openpgp.readKeys({ armoredKeys: twoKeys }); - const primaryUser = await key2.getPrimaryUser(); - primaryUser.selfCertification.preferredSymmetricAlgorithms = [6, aes192, cast5]; - const prefAlgo = await getPreferredAlgo('symmetric', [key1, key2], undefined, undefined, { - ...openpgp.config, preferredSymmetricAlgorithm: openpgp.enums.symmetric.aes192 + it('getPreferredCipherSuite - two keys', async function() { + const { aes128, aes192, cast5 } = openpgp.enums.symmetric; + const [key1, key2] = await openpgp.readKeys({ armoredKeys: twoKeys }); + const primaryUser = await key2.getPrimaryUser(); + primaryUser.selfCertification.preferredSymmetricAlgorithms = [6, aes192, cast5]; + const { symmetricAlgo, aeadAlgo } = await getPreferredCipherSuite([key1, key2], undefined, undefined, { + ...openpgp.config, preferredSymmetricAlgorithm: openpgp.enums.symmetric.aes192 + }); + expect(symmetricAlgo).to.equal(aes192); + expect(aeadAlgo).to.equal(undefined); + const { symmetricAlgo: symmetricAlgo2, aeadAlgo: aeadAlgo2 } = await getPreferredCipherSuite([key1, key2], undefined, undefined, { + ...openpgp.config, preferredSymmetricAlgorithm: openpgp.enums.symmetric.aes256 + }); + expect(symmetricAlgo2).to.equal(aes128); + expect(aeadAlgo2).to.equal(undefined); }); - expect(prefAlgo).to.equal(aes192); - const prefAlgo2 = await getPreferredAlgo('symmetric', [key1, key2], undefined, undefined, { - ...openpgp.config, preferredSymmetricAlgorithm: openpgp.enums.symmetric.aes256 + + it('getPreferredCipherSuite - two keys - one without pref', async function() { + const [key1, key2] = await openpgp.readKeys({ armoredKeys: twoKeys }); + const primaryUser = await key2.getPrimaryUser(); + primaryUser.selfCertification.preferredSymmetricAlgorithms = null; + const { symmetricAlgo, aeadAlgo } = await getPreferredCipherSuite([key1, key2]); + expect(symmetricAlgo).to.equal(openpgp.enums.symmetric.aes128); + expect(aeadAlgo).to.equal(undefined); }); - expect(prefAlgo2).to.equal(aes128); - }); - it("getPreferredAlgo('symmetric') - two key - one without pref", async function() { - const [key1, key2] = await openpgp.readKeys({ armoredKeys: twoKeys }); - const primaryUser = await key2.getPrimaryUser(); - primaryUser.selfCertification.preferredSymmetricAlgorithms = null; - const prefAlgo = await getPreferredAlgo('symmetric', [key1, key2]); - expect(prefAlgo).to.equal(openpgp.enums.symmetric.aes128); - }); - - it("getPreferredAlgo('aead') - one key - OCB", async function() { - const [key1] = await openpgp.readKeys({ armoredKeys: twoKeys }); - const primaryUser = await key1.getPrimaryUser(); - primaryUser.selfCertification.features = [7]; // Monkey-patch AEAD feature flag - primaryUser.selfCertification.preferredAEADAlgorithms = [2,1]; - const prefAlgo = await getPreferredAlgo('aead', [key1], undefined, undefined, { - ...openpgp.config, preferredAEADAlgorithm: openpgp.enums.aead.ocb + it('getPreferredCipherSuite with AEAD - one key - GCM', async function() { + const [key1] = await openpgp.readKeys({ armoredKeys: twoKeys }); + const primaryUser = await key1.getPrimaryUser(); + primaryUser.selfCertification.features = [9]; // Monkey-patch SEIPDv2 feature flag + primaryUser.selfCertification.preferredCipherSuites = [[9, 3], [9, 2]]; + const { symmetricAlgo, aeadAlgo } = await getPreferredCipherSuite([key1], undefined, undefined, { + ...openpgp.config, + aeadProtect: true, + preferredAEADAlgorithm: openpgp.enums.aead.gcm + }); + expect(symmetricAlgo).to.equal(openpgp.enums.symmetric.aes256); + expect(aeadAlgo).to.equal(openpgp.enums.aead.gcm); + }); + + it('getPreferredCipherSuite with AEAD - one key - AES256-OCB', async function() { + const [key1] = await openpgp.readKeys({ armoredKeys: twoKeys }); + const primaryUser = await key1.getPrimaryUser(); + primaryUser.selfCertification.features = [9]; // Monkey-patch SEIPDv2 feature flag + primaryUser.selfCertification.preferredCipherSuites = [[openpgp.enums.symmetric.aes256, openpgp.enums.aead.ocb]]; + const { symmetricAlgo, aeadAlgo } = await getPreferredCipherSuite([key1], undefined, undefined, { + ...openpgp.config, + aeadProtect: true, + preferredAEADAlgorithm: openpgp.enums.aead.gcm + }); + expect(symmetricAlgo).to.equal(openpgp.enums.symmetric.aes256); + expect(aeadAlgo).to.equal(openpgp.enums.aead.ocb); + }); + + it('getPreferredCipherSuite with AEAD - one key - AES128-GCM', async function() { + const [key1] = await openpgp.readKeys({ armoredKeys: twoKeys }); + const primaryUser = await key1.getPrimaryUser(); + primaryUser.selfCertification.features = [9]; // Monkey-patch SEIPDv2 feature flag + primaryUser.selfCertification.preferredCipherSuites = [[openpgp.enums.symmetric.aes128, openpgp.enums.aead.gcm]]; + const { symmetricAlgo, aeadAlgo } = await getPreferredCipherSuite([key1], undefined, undefined, { + ...openpgp.config, + aeadProtect: true, + preferredAEADAlgorithm: openpgp.enums.aead.gcm + }); + expect(symmetricAlgo).to.equal(openpgp.enums.symmetric.aes128); + expect(aeadAlgo).to.equal(openpgp.enums.aead.gcm); + }); + + it('getPreferredCipherSuite with AEAD - two keys - one without pref', async function() { + const keys = await openpgp.readKeys({ armoredKeys: twoKeys }); + const key1 = keys[0]; + const key2 = keys[1]; + const primaryUser = await key1.getPrimaryUser(); + primaryUser.selfCertification.features = [9]; // Monkey-patch SEIPDv2 feature flag + primaryUser.selfCertification.preferredCipherSuites = [[9, 3], [9, 2]]; + const primaryUser2 = await key2.getPrimaryUser(); + primaryUser2.selfCertification.features = [9]; // Monkey-patch SEIPDv2 feature flag + const { symmetricAlgo, aeadAlgo } = await getPreferredCipherSuite([key1, key2], undefined, undefined, { + ...openpgp.config, + aeadProtect: true + }); + expect(symmetricAlgo).to.equal(openpgp.enums.symmetric.aes128); + expect(aeadAlgo).to.equal(openpgp.enums.aead.ocb); + }); + + it('getPreferredCipherSuite with AEAD - two keys - one with no support', async function() { + const keys = await openpgp.readKeys({ armoredKeys: twoKeys }); + const key1 = keys[0]; + const key2 = keys[1]; + const primaryUser = await key1.getPrimaryUser(); + primaryUser.selfCertification.features = [9]; // Monkey-patch SEIPDv2 feature flag + primaryUser.selfCertification.preferredCipherSuites = [[9, 3], [9, 2]]; + const { symmetricAlgo, aeadAlgo } = await getPreferredCipherSuite([key1, key2], undefined, undefined, { + ...openpgp.config, + aeadProtect: true + }); + expect(symmetricAlgo).to.equal(openpgp.enums.symmetric.aes256); + expect(aeadAlgo).to.equal(undefined); }); - expect(prefAlgo).to.equal(openpgp.enums.aead.ocb); - const supported = await isAEADSupported([key1]); - expect(supported).to.be.true; }); - it("getPreferredAlgo('aead') - two key - one without pref", async function() { - const keys = await openpgp.readKeys({ armoredKeys: twoKeys }); - const key1 = keys[0]; - const key2 = keys[1]; - const primaryUser = await key1.getPrimaryUser(); - primaryUser.selfCertification.features = [7]; // Monkey-patch AEAD feature flag - primaryUser.selfCertification.preferredAEADAlgorithms = [2,1]; - const primaryUser2 = await key2.getPrimaryUser(); - primaryUser2.selfCertification.features = [7]; // Monkey-patch AEAD feature flag - const prefAlgo = await getPreferredAlgo('aead', [key1, key2]); - expect(prefAlgo).to.equal(openpgp.enums.aead.eax); - const supported = await isAEADSupported([key1, key2]); - expect(supported).to.be.true; - }); + describe('getPreferredHashAlgo()', () => { + it('getPreferredHashAlgo - it can handle unknown hash algorithms', async function() { + // Preferred hash algo: SHA256 and unknown algo with ID '99' + const signingKeyWithUnknownAlgoPref = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- - it("getPreferredAlgo('aead') - two key - one with no support", async function() { - const keys = await openpgp.readKeys({ armoredKeys: twoKeys }); - const key1 = keys[0]; - const key2 = keys[1]; - const primaryUser = await key1.getPrimaryUser(); - primaryUser.selfCertification.features = [7]; // Monkey-patch AEAD feature flag - primaryUser.selfCertification.preferredAEADAlgorithms = [2,1]; - const prefAlgo = await getPreferredAlgo('aead', [key1, key2]); - expect(prefAlgo).to.equal(openpgp.enums.aead.eax); - const supported = await isAEADSupported([key1, key2]); - expect(supported).to.be.false; +xVgEZyJrexYJKwYBBAHaRw8BAQdAJwddYhjAmI6OzqxkW9cAXVBfZdSFxsaZ +0v9YAJA50fQAAQCK5y2PWn5MEoWnMre7WDMCv3HPs92No9r7ZrmXED3ZohDT +zQ48dGVzdEB0ZXN0Lml0PsLAEQQTFgoAgwWCZyJrewMLCQcJkPu0BwaBSfbo +RRQAAAAAABwAIHNhbHRAbm90YXRpb25zLm9wZW5wZ3Bqcy5vcmdJHbHl9Kh1 +AmD2A1I0IgJEsWl12eWrRzU2C5MilKZDXQMVCGMEFgACAQIZAQKbAwIeARYh +BDc1TCI+j6hTVaLvtfu0BwaBSfboAAA3cwEAwA/JVtszZ1PgowLYG2/ok+WL ++AcEbvhPBBoJV6B2gLsA/2S/WIFiNLJd9xVPCsnlsh6GSqjNjEYXZIag0u14 +WoEKx10EZyJrexIKKwYBBAGXVQEFAQEHQEnAXen/dnz9PZ+oJ9BYrDV+N/6y +c5nTJbTmMj01obBBAwEIBwAA/0izDCturSN2513OhRlrHc55biP/GL2CR6LK +e3Zo4XCoEFDCvgQYFgoAcAWCZyJrewmQ+7QHBoFJ9uhFFAAAAAAAHAAgc2Fs +dEBub3RhdGlvbnMub3BlbnBncGpzLm9yZ6bxx8jT55ZC4ZuKBMyd1j0ULyQ4 +PPAbypPTzwI7bN7zApsMFiEENzVMIj6PqFNVou+1+7QHBoFJ9ugAAPRMAP9k +45AQSzIKF8JmS28I8hSUDrPCjSVh1A3Aw01F6sRYLgEA1wq81Sxnmvo6ztxK +EVdFOaJsHYaJ0A23hIaCWML5nAs= +=jJaL +-----END PGP PRIVATE KEY BLOCK----- +` }); + const config = { + ...openpgp.config, + preferredHashAlgorithm: openpgp.enums.hash.sha512 // SHA512 is not in the key prefs + }; + const hashAlgo = await getPreferredHashAlgo( + [signingKeyWithUnknownAlgoPref], + signingKeyWithUnknownAlgoPref, + undefined, + undefined, + config + ); + expect(hashAlgo).to.equal(openpgp.enums.hash.sha256); + }); }); it('User attribute packet read & write', async function() { @@ -3798,7 +4382,7 @@ VYGdb3eNlV8CfoEC privateKey.users[0].userID = openpgp.UserIDPacket.fromObject({ name: 'Test User', email: 'b@c.com' }); // Set second user to prefer aes128. We will select this user. privateKey.users[1].selfCertifications[0].preferredHashAlgorithms = [openpgp.enums.hash.sha512]; - const config = { minRSABits: 1024 }; + const config = { minRSABits: 1024, preferredHashAlgorithm: openpgp.enums.hash.sha512 }; const signed = await openpgp.sign({ message: await openpgp.createMessage({ text: 'hello' }), signingKeys: privateKey, signingUserIDs: { name: 'Test McTestington', email: 'test@example.com' }, format: 'binary', config }); @@ -3857,7 +4441,8 @@ VYGdb3eNlV8CfoEC const key = await openpgp.readKey({ armoredKey: pub_revoked_subkeys }); key.revocationSignatures = []; key.users[0].revocationSignatures = []; - return openpgp.encrypt({ encryptionKeys: [key], message: await openpgp.createMessage({ text: 'random data' }), date: new Date(1386842743000) }).then(() => { + const subkeyRevocationTime = key.subkeys[0].revocationSignatures[0].created; + return openpgp.encrypt({ encryptionKeys: [key], message: await openpgp.createMessage({ text: 'random data' }), date: subkeyRevocationTime }).then(() => { throw new Error('encryptSessionKey should not encrypt with revoked public key'); }).catch(error => { expect(error.message).to.equal('Error encrypting message: Could not find valid encryption key packet in key ' + key.getKeyID().toHex() + ': Subkey is revoked'); @@ -3866,11 +4451,9 @@ VYGdb3eNlV8CfoEC it('Reject encryption with key revoked with appended revocation cert', async function() { const key = await openpgp.readKey({ armoredKey: pub_revoked_with_cert }); - return openpgp.encrypt({ encryptionKeys: [key], message: await openpgp.createMessage({ text: 'random data' }) }).then(() => { - throw new Error('encryptSessionKey should not encrypt with revoked public key'); - }).catch(function(error) { - expect(error.message).to.equal('Error encrypting message: Primary key is revoked'); - }); + await expect( + openpgp.encrypt({ encryptionKeys: [key], message: await openpgp.createMessage({ text: 'random data' }) }) + ).to.be.rejectedWith(/Primary key is revoked/); }); it('Merge key with another key with non-ID user attributes', async function() { @@ -3942,7 +4525,29 @@ VYGdb3eNlV8CfoEC expect(key.subkeys).to.have.length(0); const newKey = await key.addSubkey(); expect(newKey.subkeys[0].getAlgorithmInfo().algorithm).to.equal('ecdh'); - expect(newKey.subkeys[0].getAlgorithmInfo().curve).to.equal('curve25519'); + expect(newKey.subkeys[0].getAlgorithmInfo().curve).to.equal('curve25519Legacy'); + }); + + it('Add a new default subkey to an Ed488 key', async function() { + const userID = { name: 'test', email: 'a@b.com' }; + const opt = { type: 'curve448', userIDs: [userID], format: 'object', subkeys: [] }; + const { privateKey: key } = await openpgp.generateKey(opt); + expect(key.subkeys).to.have.length(0); + const keyWithNewSubkey = await key.addSubkey(); + const parsedNewKey = await openpgp.readKey({ armoredKey: keyWithNewSubkey.armor() }); + expect(parsedNewKey.subkeys[0].getAlgorithmInfo().algorithm).to.equal('x448'); + expect(parsedNewKey.subkeys[0].getAlgorithmInfo().curve).to.be.undefined; + }); + + it('Add a new default subkey to a v6 key', async function() { + const userID = { name: 'test', email: 'a@b.com' }; + const opt = { type: 'curve25519', userIDs: [userID], format: 'object', subkeys: [], config: { v6Keys: true } }; + const { privateKey: key } = await openpgp.generateKey(opt); + expect(key.subkeys).to.have.length(0); + const newKey = await key.addSubkey(); + expect(newKey.subkeys[0].keyPacket.version).to.equal(6); + expect(newKey.subkeys[0].getAlgorithmInfo().algorithm).to.equal('x25519'); + expect(newKey.subkeys[0].getAlgorithmInfo().curve).to.be.undefined; }); it('Add a new default subkey to a dsa key', async function() { @@ -3982,6 +4587,26 @@ XvmoLueOOShu01X/kaylMqaT8w== expect(newKey.subkeys[0].getAlgorithmInfo().curve).to.equal('secp256k1'); }); + it('should throw when trying to add a curve25519Legacy key to a v6 key', async function() { + const v6Key = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMA +GXKBexK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJj +h3/jAwsJBwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifB +ilZwj2Ul7Ce62azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2 +kCcUmKfvBXbAf6rhRYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaE +QsiPlR4zxP/TP7mhfVEe7XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaT +JINn+eUBXbki+PSAld2nhJh/LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u +/tVY6a//1q0NWC1X+yui3O24wpsGGBsKAAAALAWCY4d/4wKbDCIhBssYbE8G +CaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce62azJAAAAAAQBIKbpGG2dWTX8j+Vj +FM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDEM0g12vYxoWM8Y81W+bHBw805 +I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg== +-----END PGP PRIVATE KEY BLOCK-----` }); + expect(v6Key.subkeys).to.have.length(1); + await expect(v6Key.addSubkey({ type: 'ecc' })).to.be.rejectedWith(/Cannot generate v6 keys of type 'ecc' with curve curve25519Legacy/); + expect(v6Key.subkeys).to.have.length(1); + }); + it('should throw when trying to encrypt a subkey separately from key', async function() { const privateKey = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key_rsa }), @@ -4017,10 +4642,10 @@ XvmoLueOOShu01X/kaylMqaT8w== it('create and add a new eddsa subkey to a eddsa key', async function() { const passphrase = '12345678'; const userID = { name: 'test', email: 'a@b.com' }; - const { privateKey } = await openpgp.generateKey({ curve: 'curve25519', userIDs: [userID], format: 'object', subkeys:[] }); + const { privateKey } = await openpgp.generateKey({ curve: 'curve25519Legacy', userIDs: [userID], format: 'object', subkeys:[] }); const total = privateKey.subkeys.length; - let newPrivateKey = await privateKey.addSubkey({ curve: 'curve25519', userIDs: [userID], sign: true }); + let newPrivateKey = await privateKey.addSubkey({ curve: 'curve25519Legacy', userIDs: [userID], sign: true }); const subkey1 = newPrivateKey.subkeys[total]; const encNewPrivateKey = await openpgp.encryptKey({ privateKey: newPrivateKey, passphrase }); newPrivateKey = await openpgp.decryptKey({ @@ -4035,22 +4660,22 @@ XvmoLueOOShu01X/kaylMqaT8w== const subkeyOid = subkey2.keyPacket.publicParams.oid; const pkOid = privateKey.keyPacket.publicParams.oid; expect(subkeyOid.getName()).to.be.equal(pkOid.getName()); - expect(subkey2.getAlgorithmInfo().algorithm).to.be.equal('eddsa'); + expect(subkey2.getAlgorithmInfo().algorithm).to.be.equal('eddsaLegacy'); await subkey2.verify(); }); it('create and add a new ecdsa subkey to a eddsa key', async function() { const userID = { name: 'test', email: 'a@b.com' }; - const { privateKey } = await openpgp.generateKey({ curve: 'ed25519', userIDs: [userID], format: 'object', subkeys:[] }); + const { privateKey } = await openpgp.generateKey({ curve: 'ed25519Legacy', userIDs: [userID], format: 'object', subkeys:[] }); const total = privateKey.subkeys.length; - let newPrivateKey = await privateKey.addSubkey({ curve: 'p256', sign: true }); + let newPrivateKey = await privateKey.addSubkey({ curve: 'nistP256', sign: true }); newPrivateKey = await openpgp.readKey({ armoredKey: newPrivateKey.armor() }); const subkey = newPrivateKey.subkeys[total]; expect(subkey).to.exist; expect(newPrivateKey.subkeys.length).to.be.equal(total + 1); - expect(newPrivateKey.getAlgorithmInfo().curve).to.be.equal('ed25519'); - expect(subkey.getAlgorithmInfo().curve).to.be.equal('p256'); - expect(newPrivateKey.getAlgorithmInfo().algorithm).to.be.equal('eddsa'); + expect(newPrivateKey.getAlgorithmInfo().curve).to.be.equal('ed25519Legacy'); + expect(subkey.getAlgorithmInfo().curve).to.be.equal('nistP256'); + expect(newPrivateKey.getAlgorithmInfo().algorithm).to.be.equal('eddsaLegacy'); expect(subkey.getAlgorithmInfo().algorithm).to.be.equal('ecdsa'); await subkey.verify(); @@ -4076,7 +4701,7 @@ XvmoLueOOShu01X/kaylMqaT8w== it('create and add a new rsa subkey to a ecc key', async function() { const userID = { name: 'test', email: 'a@b.com' }; - const opt = { curve: 'ed25519', userIDs: [userID], format: 'object', subkeys:[] }; + const opt = { curve: 'ed25519Legacy', userIDs: [userID], format: 'object', subkeys:[] }; const { privateKey } = await openpgp.generateKey(opt); const total = privateKey.subkeys.length; let newPrivateKey = await privateKey.addSubkey({ type: 'rsa' }); @@ -4118,7 +4743,7 @@ XvmoLueOOShu01X/kaylMqaT8w== const subkeyOid = subkey.keyPacket.publicParams.oid; const pkOid = newPrivateKey.keyPacket.publicParams.oid; expect(subkeyOid.getName()).to.be.equal(pkOid.getName()); - expect(subkey.getAlgorithmInfo().algorithm).to.be.equal('eddsa'); + expect(subkey.getAlgorithmInfo().algorithm).to.be.equal('eddsaLegacy'); await subkey.verify(); expect(await newPrivateKey.getSigningKey()).to.be.equal(subkey); const signed = await openpgp.sign({ message: await openpgp.createMessage({ text: 'the data to signed' }), signingKeys: newPrivateKey, format: 'binary' }); @@ -4155,6 +4780,33 @@ XvmoLueOOShu01X/kaylMqaT8w== expect(decrypted.data).to.be.equal(vData); }); + ['curve25519', 'curve448'].forEach(keyType => ( + it(`encrypt/decrypt data with the new subkey correctly using ${keyType}`, async function() { + const userID = { name: 'test', email: 'a@b.com' }; + const vData = 'the data to encrypted!'; + const opt = { type: keyType, userIDs: [userID], format: 'object', subkeys:[] }; + const { privateKey } = await openpgp.generateKey(opt); + const total = privateKey.subkeys.length; + let newPrivateKey = await privateKey.addSubkey(); + const armoredKey = newPrivateKey.armor(); + newPrivateKey = await openpgp.readKey({ armoredKey: armoredKey }); + const subkey = newPrivateKey.subkeys[total]; + const publicKey = newPrivateKey.toPublic(); + await subkey.verify(); + expect(await newPrivateKey.getEncryptionKey()).to.be.equal(subkey); + const encrypted = await openpgp.encrypt({ message: await openpgp.createMessage({ text: vData }), encryptionKeys: publicKey, format: 'binary' }); + expect(encrypted).to.be.exist; + const message = await openpgp.readMessage({ binaryMessage: encrypted }); + const pkSessionKeys = message.packets.filterByTag(openpgp.enums.packet.publicKeyEncryptedSessionKey); + expect(pkSessionKeys).to.exist; + expect(pkSessionKeys.length).to.be.equal(1); + expect(pkSessionKeys[0].publicKeyID.toHex()).to.be.equals(subkey.keyPacket.getKeyID().toHex()); + const decrypted = await openpgp.decrypt({ message, decryptionKeys: newPrivateKey }); + expect(decrypted).to.exist; + expect(decrypted.data).to.be.equal(vData); + }) + )); + it('sign/verify data with the new subkey correctly using rsa', async function() { const privateKey = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key_rsa }), diff --git a/test/general/oid.js b/test/general/oid.js index 8e500c41..b351cfc5 100644 --- a/test/general/oid.js +++ b/test/general/oid.js @@ -1,9 +1,9 @@ -const { expect } = require('chai'); +import { expect } from 'chai'; -const OID = require('../../src/type/oid'); -const util = require('../../src/util'); +import OID from '../../src/type/oid.js'; +import util from '../../src/util.js'; -module.exports = () => describe('Oid tests', function() { +export default () => describe('Oid tests', function() { const p256_oid = new Uint8Array([0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07]); const p384_oid = new Uint8Array([0x2B, 0x81, 0x04, 0x00, 0x22]); const p521_oid = new Uint8Array([0x2B, 0x81, 0x04, 0x00, 0x23]); diff --git a/test/general/openpgp.js b/test/general/openpgp.js index 0b59cc32..0a420b7b 100644 --- a/test/general/openpgp.js +++ b/test/general/openpgp.js @@ -1,20 +1,21 @@ /* eslint-disable max-lines */ -/* globals tryTests: true */ -const spy = require('sinon/lib/sinon/spy'); -const stream = require('@openpgp/web-stream-tools'); -const { use: chaiUse, expect } = require('chai'); -chaiUse(require('chai-as-promised')); +/* globals tryTests, loadStreamsPolyfill */ +import sinon from 'sinon'; +import * as stream from '@openpgp/web-stream-tools'; +import { use as chaiUse, expect } from 'chai'; +import chaiAsPromised from 'chai-as-promised'; // eslint-disable-line import/newline-after-import +chaiUse(chaiAsPromised); -const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../..'); -const crypto = require('../../src/crypto'); -const random = require('../../src/crypto/random'); -const util = require('../../src/util'); -const keyIDType = require('../../src/type/keyid'); -const { isAEADSupported } = require('../../src/key'); +import openpgp from '../initOpenpgp.js'; +import crypto from '../../src/crypto'; +import * as random from '../../src/crypto/random.js'; +import util from '../../src/util.js'; +import keyIDType from '../../src/type/keyid.js'; +import { getPreferredCipherSuite } from '../../src/key'; -const input = require('./testInputs'); +import * as input from './testInputs.js'; -const detectNode = () => typeof globalThis.process === 'object' && typeof globalThis.process.versions === 'object'; +const detectBrowser = () => typeof navigator === 'object'; const pub_key = [ '-----BEGIN PGP PUBLIC KEY BLOCK-----', @@ -296,6 +297,25 @@ DECl1Qu4QyeXin29uEXWiekMpNlZVsEuc8icCw6ABhIZ =/7PI -----END PGP PRIVATE KEY BLOCK-----`; +const priv_key_sha3_512 = `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xUsGZN8edBsAAAAgdUMlFMFCVKNo7sdUd6FVBos6NNjpUpSdrodk6BfPb/kA+3bu +A2+WY2LwyxlX5o07WR2VSn+wuegC3v28yO0tClHCtwYfGw4AAABIBYJk3x50BAsJ +CAcHFQ4MCgkICwIWAAIXgAKbAwIeCSIhBpbSe0QWuaCNSSLaePhXEP3BxQ2VHX3W +pW1U6svHvCUiBScJAgcCAAAAACMZIP8aHixoyC9wS3q/TNV/IfOQa81f+U5Ucz6H +4I+c5bWRYUzH/piBB4n5FoYlld+/SViCQIBCQ+fynLmaj5wlf22+mISTt/9je1Zf +YWlJ+WSJyi5gY5EH9DubfuIU3VaqCM0aQmVybmFkZXR0ZSA8YkBleGFtcGxlLm9y +Zz7CugYTGw4AAABLBYJk3x50BAsJCAcHFQ4MCgkICwIWAAIXgAIZAQKbAwIeCSIh +BpbSe0QWuaCNSSLaePhXEP3BxQ2VHX3WpW1U6svHvCUiBScJAgcCAAAAAMMGIJEi +9+yqkFKsNwX1H5II0riPudFpwBx2ypVjNk4aNb7Exl56Aac4tXEhz4fH41q0dAzF +ww2erZaiUqmohQ4AFSw1jN/WOiDfb1DkjT/HJ8vXMGpwWdgFPoqsWzTNhd5VCcdL +BmTfHnQZAAAAIAGMcsqVCXLclRhVamWciSxmnYF1FFs80W7dNUH07HUOAHh/S601 +If+/eZKDIj3jq7oOe2PzHSYEK+mpQD1hBpF2wpsGGBsOAAAALAWCZN8edAKbDCIh +BpbSe0QWuaCNSSLaePhXEP3BxQ2VHX3WpW1U6svHvCUiAAAAANj3IBknZTPsMpWA +we0Jl5gw/Dj4lWAGoJfWfk+6s3Q86Hag3Hu8VBsapzmul+vzy0KJa+ZRcZz2n8aj +0vTl4sOZ0EcCdFDfkh/tR//gKkT6BiSBG86WoFq3f6U/RC+z0Ym7Dw== +-----END PGP PRIVATE KEY BLOCK-----`; + const passphrase = 'hello world'; const plaintext = input.createSomeMessage(); const password1 = 'I am a password'; @@ -848,6 +868,56 @@ MbVkSnU2Z+vhSmYubDCldNOSVwE= =bTUQ -----END PGP PRIVATE KEY BLOCK-----`; +const rsaKeyWithNoFeaturesFromRNP = `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xcLYBGX9lyEBCADMOR1JW9yPhMxeMmrUWf2MqKtX9WHvS+EFkPaVwWmYIM/bjcrngPgKEG5wTND9 +S1QJ/Op9mzN36kwAzX0bLx2R+HlyvNQOzOA05uY3uhbZePvkE2o76z9//w07DEYtMG8aLvLdpVLW +U7YwMMdlI8zSwi2Q5/QAUOfCJCylxndj/U7AF+9sqrSoSJmwtzu+AS3pnHDgqcHpum7KTL7sOgwa +8713qGb05YgXCtUkh2hT0t7Kz5OsQrM01yJJlVkgxw6BZXiRCLSCGCepZiUvtWO53cwZ2GgIZZKx +piAEo8AjMHGUxyiMBR5DvzDLADVexLf/DB5BTz9KywRu2zTGUtYlABEBAAEAB/4ifoUlVmhHL4GL +aY2kx3xtjTG/vhkoH5Nm6sjTm6MXyHcDWQxMFPsQTB0zR65HEjmkJD2BML09RGxI+GxosokWljcB +O8a/pzg5h0ScZgik++vj5qmbbE1B89UKw7R4voUNkZ+A84Mt417S/fI38ZePg6/JmXwbr46tuol2 +CLMyxpFVNvGgqj7/U3co/BygukHMqfpC/K4QTVWUFCLkzgpAzk+LekSLWNrVD2soVQGmUn68OOLY +Dnwxe7mP/4GAmmnAH4nfg9Uf+z1tHVoaL7sUHKFXXBIjopcimWBjLIzWm0ZvKzmvmOE49Fswuoyt +Q2oLWYCXd/lrDr4u/VN+/uKhBADTpPm6+eNJ9CDOsIHTlNWrKD6/iubsLXWju5uyOW8rWDkE5m8+ +Ik+l4Eq8b0iAHGSS/6QayeRupeUCCkHnmA956qAFvHzkeE2S5YUg/mtnV+qPZrHG4UfD3KU0gv/Y +3x3HcqOYU7Ph6kq4mcF7UO83+8E8gJWyJwWwqpKXrgDZtQQA9wX5T11+bOwWh37SDWd20mqxTwZM +pSuClCUwVs8Z2p/Fo+Kq/+uoNRQW38O1J2QM37fMaKOePeYN15cIM3ilZQ0mU3UwvKC1B3zxd0f9 +28cZ4fxlGDE3qzZygbB/BxiXnlcPezO2r03X9PJnjCP2y5Wi+PMR5ciFY1w5qaI9ELEEAL6Wvdl4 +zSwkT4qxM7rWzrpVPisdHRiwFT6E+vTmNpA+qXvwlbLEXrYmFVY7CphWjyJQEvqxaa0MtdJniwef +cZtSi2CirquB0R3RJtloMZkbobRJFV43URIc9qUhSgarZ+Pco80mG06hjY2pxFot49oJZp2FNm5d +wPOB/kCvczK3QRHNGkJlcm5hZGV0dGUgPGJAZXhhbXBsZS5vcmc+wsCNBBMBCAA3FiEEKUdMEpbT +txHSmeOgwAeWcPHMimkFAmX9lyEFCQPCZwACGwMECwkIBwUVCAkKCwUWAgMBAAAKCRDAB5Zw8cyK +aUZHB/9j2vE9niEiQS9tNczxrXFde2Kg8U+PzoSU83QvkkWM2D4FBukCZDHj7cppIzYcDcH0Gaox +Y2eUgJx/q/Nq8svsk2Z4X3dGsEut+EqQpsjWny4kUHKUBemSh3JGJoH5ongxXXNwNTe31zNa1CRA +nmc5KXnOmD2FX17ax5Xu2SaJ11eiKcmKXMtZBVzzLL41LwUd2RPizIw1TT2zckHIstwaLdO4y5If +ZEnTiiBqrex8HYf/Vta8GF8uvJVQvn+Du5m/FmdeSFxTBeDvf1Sj6k8XBS9QQHbjpH/K4ePz9Twh +ayLuvrvWmyN5edo6Xf1LRh/R6dskKbUOreAfS8pTWWp0x8LYBGX9lyEBCAC43ooLMCLT0QYslVWZ +suDNNrQ0LYY24i3vRfnHFM9rRUthWrcIKzpKFvq9jntETYWkNM7LIJ2YhHueVMuLyaF2Evrkn89q +dCbA+ICS4uJ5RlhXv5ZfQT+H9fQL/JBTk9vQpBoZHr8foPc2YNonOuZjmeLSa1ZcR6BumUQqTjuk +CEgWxebnRUz6aL6+Mdl/emU7+G6IINC15q3RWUIlkmx5gQwsmIaztxVF9LniRIkB+Fjwf7U7AqfL +Y87/F1LHqeaIzc6oZNfIIXX57A4im3lqJQAQq1n/omyYHwWO+lWqbCwCj8ri4OXcgGZOdkbMOHff +hGsxKy8YK7qyvBxYiINPABEBAAEAB/sFu2CsWCG8T47Rcw/kZBd0RW3w8DhpGzoxYQoNkiecO9nK +evWR20VDZtL/bZuE0qKCJOEEi05XnEP49Mga3XWUI6KD1DCqLE/HS+0woLhE6lly3w3ahjtiC841 +UO9op/z4yx3ECaADawo/NWGONdVO4UaXH5zd35qp0za52RMgUtPqwJBZ9cKfXkFmfu9QDpiW1J4N +8S76Hgol4ThESRTm80d82L2UMVbFgXZOgpMgq+A1NXSbloSJ3ZMI2hR21PbQt/uAdwCP/E+EQOe+ +s6HN/9+0XjzsE0wcu7DJa/3QTWGTnn5Jg/SufHnABWTqYymfDH74TTPlkkWNJB65Cz0BBADUMLdg +jfDgmlw2CtKHuMYvMDe//a+KgrV2Q89QRnQk8agwXkiY/NxtRehOj35yzzUs/AdEfmVT6jaC/dQD +tEqeY1B9LI4nL4ZiE+qLnSKVPeeWtOVNUsNJUFRX6oSvChTlWj90UWvYtQUUS0zAQrI+tcJsypTE +fiSBG8DOzefi1wQA3wnGkyIidc7N8u/NdCvBO2jUOU4Q6A1E1k2eVfcjK05gHEtChUgf03v80iFG +42VIGa00rxfv9RdWAJzWy+/qqjPAUGtlrbM0VljZsvLnsZLU4cTaC2A+3Lm00UgkHAGzw4IBe9vB +4IAHYpRnZEwIveyDwVLpioEP5wShHYVXTEkEAILWuum7EgK70yYoC4HUvmUufpj7aTfpC4vYqi+4 +Xw0n8PLPPRZ3AjJE2O41kicip/Y30B3HVIxwbwYMBIjqRTpTyYer6jkRHc17xau8vzyWKLETC+7A +WPrljucvjEJXpkrDYlV5fmpsMvqVHJiSQrJDMFX1SHF8UUnqelGg6Fv8QLvCwHwEGAEIACYWIQQp +R0wSltO3EdKZ46DAB5Zw8cyKaQUCZf2XIgUJA8JnAAIbDAAKCRDAB5Zw8cyKaY7PB/9qOmlz84mu +wNrHo00TdXefBykwoJxtDLjNzQE/8HGnzuFWJgHvRDD8FLaaevRwD1AGf6B3YySxBwICoRqbsYGr +wg9ng3wIUBPeAeS61e/ATkFEqknQnj2rIscaztxz56b1Sy6YEjW6dD7QngoinDViAmNT/zY2diK8 +85iB+47tNXrUOHD1FKs8XKr05FwWWjFmmqGSxC+LSdNeuDtP1UKZaoYROyZ+R3zKdguNOhtDHX6o +me8oJym/ILMHRGIc4JvY9+2wE5U1FBYTsze3WnVH5dP5mfA2Uk83TR5KewKANsb4kl/OEPlADWdR +8wENR68u88WrKOGN359vq/DKwd3A +=c2mQ +-----END PGP PRIVATE KEY BLOCK-----`; + function withCompression(tests) { const compressionTypes = Object.values(openpgp.enums.compression); @@ -863,8 +933,8 @@ function withCompression(tests) { let decompressSpy; beforeEach(function () { - compressSpy = spy(openpgp.CompressedDataPacket.prototype, 'compress'); - decompressSpy = spy(openpgp.CompressedDataPacket.prototype, 'decompress'); + compressSpy = sinon.spy(openpgp.CompressedDataPacket.prototype, 'compress'); + decompressSpy = sinon.spy(openpgp.CompressedDataPacket.prototype, 'decompress'); }); afterEach(function () { @@ -894,7 +964,7 @@ function withCompression(tests) { }); } -module.exports = () => describe('OpenPGP.js public api tests', function() { +export default () => describe('OpenPGP.js public api tests', function() { describe('readKey(s) and readPrivateKey(s) - unit tests', function() { it('readKey and readPrivateKey should create equal private keys', async function() { const key = await openpgp.readKey({ armoredKey: priv_key }); @@ -920,6 +990,62 @@ module.exports = () => describe('OpenPGP.js public api tests', function() { }); }); + it('readPrivateKey and readPrivateKeys should have consistent results', async function() { + const privateAndPublicKeyBlock = `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xVgEZkI3VBYJKwYBBAHaRw8BAQdA7nW1t5qRdtIYzEVEEhRSDgTgbk2JHofY +Ph8FuGsDqgwAAQDYVpZt1w6a+vxgb4M351aCpA2sCfx8kbFg23h8Irtm1xFY +zQ88dGVzdEB0ZXN0LmNvbT7CwBMEEBYKAIUFgmZCN1QDCwkHCZDuyoEpQ/KE +H0UUAAAAAAAcACBzYWx0QG5vdGF0aW9ucy5vcGVucGdwanMub3JnzKJ8mXOC +wnEp6lVJ/5+rRzR4UcwlL8EjhOS+rV0T8pAFFQgKDA4EFgACAQIZAQKbAwIe +ARYhBECs3D9sMFn6oOxAqu7KgSlD8oQfAAAPbwD+NjyEHt1CRI+0XmgHdiwZ +JN115IO+M37bOxgBnTbVoF0BAMGECXVQoSRVNy0TYf+AUUPQ6tSZ1zLXszwe +FK3w+CoGxjMEZkI3VBYJKwYBBAHaRw8BAQdA7nW1t5qRdtIYzEVEEhRSDgTg +bk2JHofYPh8FuGsDqgzNDzx0ZXN0QHRlc3QuY29tPsLAEwQQFgoAhQWCZkI3 +VAMLCQcJkO7KgSlD8oQfRRQAAAAAABwAIHNhbHRAbm90YXRpb25zLm9wZW5w +Z3Bqcy5vcmfMonyZc4LCcSnqVUn/n6tHNHhRzCUvwSOE5L6tXRPykAUVCAoM +DgQWAAIBAhkBApsDAh4BFiEEQKzcP2wwWfqg7ECq7sqBKUPyhB8AAA9vAP42 +PIQe3UJEj7ReaAd2LBkk3XXkg74zfts7GAGdNtWgXQEAwYQJdVChJFU3LRNh +/4BRQ9Dq1JnXMtezPB4UrfD4KgY= +-----END PGP PRIVATE KEY BLOCK-----`; + // readPrivateKey should read the first private key encountered in a key block + const key = await openpgp.readPrivateKey({ armoredKey: privateAndPublicKeyBlock }); + const privateKeys = await openpgp.readPrivateKeys({ armoredKeys: privateAndPublicKeyBlock }); + expect(privateKeys.length).to.equal(1); + expect(key.isPrivate()).to.be.true; + expect(privateKeys[0].isPrivate()).to.be.true; + expect(key.isDecrypted()).to.be.true; + expect(privateKeys[0].isDecrypted()).to.be.true; + expect(key.getKeyID().equals(privateKeys[0].getKeyID())).to.be.true; + }); + + it('readKey and readKeys should have consistent results', async function() { + const publicAndPrivateKeyBlock = `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xjMEZkI3VBYJKwYBBAHaRw8BAQdA7nW1t5qRdtIYzEVEEhRSDgTgbk2JHofY +Ph8FuGsDqgzNDzx0ZXN0QHRlc3QuY29tPsLAEwQQFgoAhQWCZkI3VAMLCQcJ +kO7KgSlD8oQfRRQAAAAAABwAIHNhbHRAbm90YXRpb25zLm9wZW5wZ3Bqcy5v +cmfMonyZc4LCcSnqVUn/n6tHNHhRzCUvwSOE5L6tXRPykAUVCAoMDgQWAAIB +AhkBApsDAh4BFiEEQKzcP2wwWfqg7ECq7sqBKUPyhB8AAA9vAP42PIQe3UJE +j7ReaAd2LBkk3XXkg74zfts7GAGdNtWgXQEAwYQJdVChJFU3LRNh/4BRQ9Dq +1JnXMtezPB4UrfD4KgbFWARmQjdUFgkrBgEEAdpHDwEBB0DudbW3mpF20hjM +RUQSFFIOBOBuTYkeh9g+HwW4awOqDAABANhWlm3XDpr6/GBvgzfnVoKkDawJ +/HyRsWDbeHwiu2bXEVjNDzx0ZXN0QHRlc3QuY29tPsLAEwQQFgoAhQWCZkI3 +VAMLCQcJkO7KgSlD8oQfRRQAAAAAABwAIHNhbHRAbm90YXRpb25zLm9wZW5w +Z3Bqcy5vcmfMonyZc4LCcSnqVUn/n6tHNHhRzCUvwSOE5L6tXRPykAUVCAoM +DgQWAAIBAhkBApsDAh4BFiEEQKzcP2wwWfqg7ECq7sqBKUPyhB8AAA9vAP42 +PIQe3UJEj7ReaAd2LBkk3XXkg74zfts7GAGdNtWgXQEAwYQJdVChJFU3LRNh +/4BRQ9Dq1JnXMtezPB4UrfD4KgY= +-----END PGP PRIVATE KEY BLOCK-----`; + // readKey should read the first key encountered in a key block + const key = await openpgp.readKey({ armoredKey: publicAndPrivateKeyBlock }); + const keys = await openpgp.readKeys({ armoredKeys: publicAndPrivateKeyBlock }); + expect(keys.length).to.equal(2); + expect(key.isPrivate()).to.be.false; + expect(keys[0].isPrivate()).to.be.false; + expect(keys[1].isPrivate()).to.be.true; + }); + it('readPrivateKey should throw on armored public key', async function() { await expect(openpgp.readPrivateKey({ armoredKey: pub_key })).to.be.rejectedWith(/Armored text not of type private key/); }); @@ -938,7 +1064,7 @@ module.exports = () => describe('OpenPGP.js public api tests', function() { await expect(test).to.eventually.be.rejectedWith(/Invalid user ID format/); }); - it('should fail for invalid user email address', async function() { + it('should fail for invalid user email address (missing @)', async function() { const opt = { userIDs: [{ name: 'Test User', email: 'textexample.com' }] }; @@ -946,14 +1072,6 @@ module.exports = () => describe('OpenPGP.js public api tests', function() { await expect(test).to.eventually.be.rejectedWith(/Invalid user ID format/); }); - it('should fail for invalid user email address', async function() { - const opt = { - userIDs: [{ name: 'Test User', email: 'text@examplecom' }] - }; - const test = openpgp.generateKey(opt); - await expect(test).to.eventually.be.rejectedWith(/Invalid user ID format/); - }); - it('should fail for string user ID', async function() { const opt = { userIDs: 'Test User ' @@ -992,7 +1110,25 @@ module.exports = () => describe('OpenPGP.js public api tests', function() { }); describe('generateKey - unit tests', function() { - it('should have default params set', function() { + it('should still support curve="curve25519" for ECC key type (v4 key)', function() { + const opt = { + userIDs: { name: 'Test User', email: 'text@example.com' }, + type: 'ecc', + curve: 'curve25519', + format: 'object' + }; + return openpgp.generateKey(opt).then(async function({ privateKey: key }) { + expect(key).to.exist; + expect(key.getAlgorithmInfo().rsaBits).to.equal(undefined); + expect(key.getAlgorithmInfo().algorithm).to.equal('eddsaLegacy'); + expect(key.getAlgorithmInfo().curve).to.equal('ed25519Legacy'); + expect(key.subkeys[0].getAlgorithmInfo().rsaBits).to.equal(undefined); + expect(key.subkeys[0].getAlgorithmInfo().algorithm).to.equal('ecdh'); + expect(key.subkeys[0].getAlgorithmInfo().curve).to.equal('curve25519Legacy'); + }); + }); + + it('should have default params set (v4 key)', function() { const now = util.normalizeDate(new Date()); const opt = { userIDs: { name: 'Test User', email: 'text@example.com' }, @@ -1003,16 +1139,48 @@ module.exports = () => describe('OpenPGP.js public api tests', function() { return openpgp.generateKey(opt).then(async function({ privateKey, publicKey }) { for (const key of [publicKey, privateKey]) { expect(key).to.exist; + expect(key.keyPacket.version).to.equal(4); expect(key.users.length).to.equal(1); expect(key.users[0].userID.name).to.equal('Test User'); expect(key.users[0].userID.email).to.equal('text@example.com'); expect(key.getAlgorithmInfo().rsaBits).to.equal(undefined); - expect(key.getAlgorithmInfo().curve).to.equal('ed25519'); + expect(key.getAlgorithmInfo().curve).to.equal('ed25519Legacy'); expect(+key.getCreationTime()).to.equal(+now); expect(await key.getExpirationTime()).to.equal(Infinity); expect(key.subkeys.length).to.equal(1); expect(key.subkeys[0].getAlgorithmInfo().rsaBits).to.equal(undefined); - expect(key.subkeys[0].getAlgorithmInfo().curve).to.equal('curve25519'); + expect(key.subkeys[0].getAlgorithmInfo().curve).to.equal('curve25519Legacy'); + expect(+key.subkeys[0].getCreationTime()).to.equal(+now); + expect(await key.subkeys[0].getExpirationTime()).to.equal(Infinity); + } + }); + }); + + it('should have default params set (v6 key)', function() { + const now = util.normalizeDate(new Date()); + const opt = { + userIDs: { name: 'Test User', email: 'text@example.com' }, + passphrase: 'secret', + date: now, + format: 'object', + config: { v6Keys: true } + }; + return openpgp.generateKey(opt).then(async function({ privateKey, publicKey }) { + for (const key of [publicKey, privateKey]) { + expect(key).to.exist; + expect(key.keyPacket.version).to.equal(6); + expect(key.users.length).to.equal(1); + expect(key.users[0].userID.name).to.equal('Test User'); + expect(key.users[0].userID.email).to.equal('text@example.com'); + expect(key.getAlgorithmInfo().algorithm).to.equal('ed25519'); + expect(key.getAlgorithmInfo().rsaBits).to.equal(undefined); + expect(key.getAlgorithmInfo().curve).to.equal(undefined); + expect(+key.getCreationTime()).to.equal(+now); + expect(await key.getExpirationTime()).to.equal(Infinity); + expect(key.subkeys.length).to.equal(1); + expect(key.subkeys[0].getAlgorithmInfo().algorithm).to.equal('x25519'); + expect(key.subkeys[0].getAlgorithmInfo().rsaBits).to.equal(undefined); + expect(key.subkeys[0].getAlgorithmInfo().curve).to.equal(undefined); expect(+key.subkeys[0].getCreationTime()).to.equal(+now); expect(await key.subkeys[0].getExpirationTime()).to.equal(Infinity); } @@ -1243,6 +1411,28 @@ VFBLG8uc9IiaKann/DYBAJcZNZHRSfpDoV2pUA5EAEi2MdjxkRysFQnYPRAu expect(unlocked.isDecrypted()).to.be.true; }); + it('should support encrypting with argon2 s2k', async function() { + const key = await openpgp.readKey({ armoredKey: gnuDummyKeySigningSubkey }); + const locked = await openpgp.encryptKey({ + privateKey: key, + passphrase: passphrase, + config: { + s2kType: openpgp.enums.s2k.argon2, + aeadProtect: true + } + }); + expect(key.isDecrypted()).to.be.true; + expect(locked.isDecrypted()).to.be.false; + expect(locked.keyPacket.isDummy()).to.be.true; + const unlocked = await openpgp.decryptKey({ + privateKey: locked, + passphrase: passphrase + }); + expect(key.isDecrypted()).to.be.true; + expect(unlocked.isDecrypted()).to.be.true; + expect(unlocked.keyPacket.isDummy()).to.be.true; + }); + it('should encrypt gnu-dummy key', async function() { const key = await openpgp.readKey({ armoredKey: gnuDummyKeySigningSubkey }); const locked = await openpgp.encryptKey({ @@ -1267,7 +1457,7 @@ VFBLG8uc9IiaKann/DYBAJcZNZHRSfpDoV2pUA5EAEi2MdjxkRysFQnYPRAu beforeEach(async function() { minRSABitsVal = openpgp.config.minRSABits; - openpgp.config.minRSABits = 512; + openpgp.config.minRSABits = 1024; }); afterEach(function() { @@ -1578,7 +1768,7 @@ aOU= message: await openpgp.readMessage({ armoredMessage: encrypted }), decryptionKeys: privateKeyRSA, config - })).to.be.rejectedWith(/Session key decryption failed/); + })).to.be.rejectedWith(/No decryption key packets found/); // decryption using ECC key should succeed (PKCS1 is not used, so constant time countermeasures are not applied) const { data } = await openpgp.decrypt({ message: await openpgp.readMessage({ armoredMessage: encrypted }), @@ -1767,6 +1957,31 @@ aOU= })).to.be.rejectedWith(/No signing keys provided/); }); + it('Signing with key which uses sha3 should generate a valid sha3 signature if `config.preferredHashAlgorithm` has been set accordingly', async function() { + const privKey = await openpgp.readKey({ armoredKey: priv_key_sha3_512 }); + const pubKey = privKey.toPublic(); + const text = 'Hello, world.'; + const message = await openpgp.createCleartextMessage({ text }); + + const cleartextMessage = await openpgp.sign({ message, signingKeys: privKey, format: 'armored' }); + const parsedArmored = await openpgp.readCleartextMessage({ cleartextMessage }); + expect(parsedArmored.signature.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1); + expect( + parsedArmored.signature.packets.filterByTag(openpgp.enums.packet.signature)[0].hashAlgorithm + ).to.equal(openpgp.config.preferredHashAlgorithm); + const cleartextMessageWithSHA3 = await openpgp.sign({ message, signingKeys: privKey, format: 'armored', config: { preferredHashAlgorithm: openpgp.enums.hash.sha3_512 } }); + const parsedArmoredSHA3 = await openpgp.readCleartextMessage({ cleartextMessage: cleartextMessageWithSHA3 }); + expect(parsedArmoredSHA3.signature.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1); + expect( + parsedArmoredSHA3.signature.packets.filterByTag(openpgp.enums.packet.signature)[0].hashAlgorithm + ).to.equal(openpgp.enums.hash.sha3_512); + + const verified = await openpgp.verify({ message: parsedArmored, verificationKeys: pubKey, expectSigned: true }); + const verifiedSHA3 = await openpgp.verify({ message: parsedArmoredSHA3, verificationKeys: pubKey, expectSigned: true }); + expect(verified.data).to.equal(text); + expect(verifiedSHA3.data).to.equal(text); + }); + it('should output cleartext message of expected format', async function() { const text = 'test'; const message = await openpgp.createCleartextMessage({ text }); @@ -1947,16 +2162,50 @@ aOU= }); expect(await stream.readToEnd(streamedData)).to.equal(text); }); + + it('should sign using hash algorithm preferred by `recipientKeys` if given', async function() { + const signingKeyWithoutSHA3Pref = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xVgEZyID/RYJKwYBBAHaRw8BAQdAcdaUl/UXEQaT6rKNSEPmyKypikz9rIsf +BlFAQYjtsF8AAQDiW9ls2uBBRa3vA1Odl0NNNguRBolWhR9XGpdXnVBF3w5E +zQ48dGVzdEB0ZXN0Lml0PsLAEQQTFgoAgwWCZyID/QMLCQcJkJuH6wXn78D5 +RRQAAAAAABwAIHNhbHRAbm90YXRpb25zLm9wZW5wZ3Bqcy5vcmcNolfauRaj +NnItFJ0TOsiyZZhd6bMWVR4032v64tYRywMVCAoEFgACAQIZAQKbAwIeARYh +BGsOUiBRfu57iwuxh5uH6wXn78D5AACEQQEAz4YXoEKgOElvxRrIrkglUlpb +ilLZVU6mXqLxRSEtZi0BAK5xooNiLYbjF42eJuCDWUWriXufI9acT/vnruFr +p34Px10EZyID/RIKKwYBBAGXVQEFAQEHQOC8KcmOQ9+qEgoWBzc8xNgPUvoe +IVNw+mHbljD9eFBfAwEIBwAA/3iHMqnBfuM/c9tOIWKI4advW92aMYnjexrU +HdzPS2IoEU3CvgQYFgoAcAWCZyID/QmQm4frBefvwPlFFAAAAAAAHAAgc2Fs +dEBub3RhdGlvbnMub3BlbnBncGpzLm9yZ5M4VuJhTqDkHF/14D0i/wL8GTtM +fm9AIukMoYWXjGSGApsMFiEEaw5SIFF+7nuLC7GHm4frBefvwPkAAL0QAP9Z +oR7Vxyfuje3vAyEbef1gyfMN/RkIVbMKSiwy3A2W9AEA6QcBF5zUvwmHPpA4 ++SkLLMuq/yUGT6WhAq6kASQ8vgM= +=lluz +-----END PGP PRIVATE KEY BLOCK-----` }); + const recipientKeyWithSHA3Pref = await openpgp.readKey({ armoredKey: priv_key_sha3_512 }); + + const text = 'Hello, world.'; + const message = await openpgp.createCleartextMessage({ text }); + + // SHA3-512 is first preference of recipient key, and should be picked, + // even if not declared in the signing key prefs + const cleartextMessage = await openpgp.sign({ + message, + signingKeys: signingKeyWithoutSHA3Pref, + recipientKeys: recipientKeyWithSHA3Pref, + format: 'armored', + // the preferred hash algo is expected to picked when supported by the recipient keys + config: { preferredHashAlgorithm: openpgp.enums.hash.sha3_512 } + }); + const parsedArmored = await openpgp.readCleartextMessage({ cleartextMessage }); + expect(parsedArmored.signature.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1); + expect( + parsedArmored.signature.packets.filterByTag(openpgp.enums.packet.signature)[0].hashAlgorithm + ).to.equal(openpgp.enums.hash.sha3_512); + }); }); describe('encrypt - unit tests', function() { - it('Does not encrypt to expired key (expiration time subpacket on a direct-key signature)', async function() { - const expiredKey = await openpgp.readKey({ armoredKey: expiredPublicKeyThroughDirectSignature }); - await expect( - openpgp.encrypt({ message: await openpgp.createMessage({ text: 'test' }), encryptionKeys: expiredKey }) - ).to.be.rejectedWith(/Primary key is expired/); - }); - it('should output message of expected format', async function() { const passwords = 'password'; const text = 'test'; @@ -2018,65 +2267,6 @@ aOU= expect(await stream.readToEnd(streamedData)).to.equal(text); }); - it('supports decrypting new x25519 format', async function () { - // v4 key - const privateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- - -xUkEZIbSkxsHknQrXGfb+kM2iOsOvin8yE05ff5hF8KE6k+saspAZQCy/kfFUYc2 -GkpOHc42BI+MsysKzk4ofjBAfqM+bb7goQ3hzRV1c2VyIDx1c2VyQHRlc3QudGVz -dD7ChwQTGwgAPQUCZIbSkwmQQezK2iB2tIkWIQRqZza9wQZcwxpjGYNB7MraIHa0 -iQIbAwIeAQIZAQILBwIVCAIWAAMnBwIAAFOeZ7jrKZsCzRfu1ffFa77074st0zRo -BTJXoXBQ1ZzLjsh+ZO6fB2odnYJtQYstv45H/3JyLVogcMnFeYmHeSP3AMdJBGSG -0pMZfpd7TiOQv7uKSK+k4HT9lKr5+dmvb7vox/8ids6unEkAF1v8fCKogIrtBWVT -nVbwnovjM3LLexpXFZSgTKRcNMgPRMJ0BBgbCAAqBQJkhtKTCZBB7MraIHa0iRYh -BGpnNr3BBlzDGmMZg0HsytogdrSJAhsMAADCYs2I9wBakIu9Hhxs4R3Jq9F8J7AH -yxsNL0GomZ+hxiE0MOZwRr10DxfVaRabF1fcf9PHSHX2SwEFXUKMIHgbMQs= -=bJqd ------END PGP PRIVATE KEY BLOCK-----` }); - - const messageToDecrypt = `-----BEGIN PGP MESSAGE----- - -wUQDYc6clYlCdtoZ3rAsvBDIwvoLmvM0zwViG8Ec0PgFfN5R6C4BqEZD53UZB1WM -J68hXSj1Sa235XAUYE1pZerTKhglvdI9Aeve8+L0w5RDMjmBBA50Yv/YT8liqhNi -mNwbfFbSNhZYWjFada77EKBn60j8QT/xCQzLR1clci7ieW2knw== -=NKye ------END PGP MESSAGE-----`; - const { data } = await openpgp.decrypt({ - message: await openpgp.readMessage({ armoredMessage: messageToDecrypt }), - decryptionKeys: privateKey - }); - expect(data).to.equal('Hello World!'); - }); - - it('supports encrypting new x25519 format', async function () { - // v4 key - const privateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- - -xUkEZIbSkxsHknQrXGfb+kM2iOsOvin8yE05ff5hF8KE6k+saspAZQCy/kfFUYc2 -GkpOHc42BI+MsysKzk4ofjBAfqM+bb7goQ3hzRV1c2VyIDx1c2VyQHRlc3QudGVz -dD7ChwQTGwgAPQUCZIbSkwmQQezK2iB2tIkWIQRqZza9wQZcwxpjGYNB7MraIHa0 -iQIbAwIeAQIZAQILBwIVCAIWAAMnBwIAAFOeZ7jrKZsCzRfu1ffFa77074st0zRo -BTJXoXBQ1ZzLjsh+ZO6fB2odnYJtQYstv45H/3JyLVogcMnFeYmHeSP3AMdJBGSG -0pMZfpd7TiOQv7uKSK+k4HT9lKr5+dmvb7vox/8ids6unEkAF1v8fCKogIrtBWVT -nVbwnovjM3LLexpXFZSgTKRcNMgPRMJ0BBgbCAAqBQJkhtKTCZBB7MraIHa0iRYh -BGpnNr3BBlzDGmMZg0HsytogdrSJAhsMAADCYs2I9wBakIu9Hhxs4R3Jq9F8J7AH -yxsNL0GomZ+hxiE0MOZwRr10DxfVaRabF1fcf9PHSHX2SwEFXUKMIHgbMQs= -=bJqd ------END PGP PRIVATE KEY BLOCK-----` }); - const plaintext = 'plaintext'; - - const signed = await openpgp.encrypt({ - message: await openpgp.createMessage({ text: plaintext }), - encryptionKeys: privateKey - }); - - const { data } = await openpgp.decrypt({ - message: await openpgp.readMessage({ armoredMessage: signed }), - decryptionKeys: privateKey - }); - expect(data).to.equal(plaintext); - }); - it('should support encrypting with encrypted key with unknown s2k (unparseableKeyMaterial)', async function() { const originalDecryptedKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- @@ -2123,6 +2313,68 @@ VFBLG8uc9IiaKann/DYBAJcZNZHRSfpDoV2pUA5EAEi2MdjxkRysFQnYPRAu }); expect(decrypted.data).to.equal('test'); }); + + it('does not encrypt to expired key (expiration time subpacket on a direct-key signature)', async function() { + const expiredKey = await openpgp.readKey({ armoredKey: expiredPublicKeyThroughDirectSignature }); + await expect( + openpgp.encrypt({ message: await openpgp.createMessage({ text: 'test' }), encryptionKeys: expiredKey }) + ).to.be.rejectedWith(/Primary key is expired/); + }); + + it('uses AEAD when the encryption key prefs support it (SEIPDv2', async function() { + const v4PrivateKeyWithOCBPref = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMAGXKB +exK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJjh3/jAwsJ +BwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6 +2azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2kCcUmKfvBXbAf6rh +RYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaEQsiPlR4zxP/TP7mhfVEe +7XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/ +LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u/tVY6a//1q0NWC1X+yui3O24wpsG +GBsKAAAALAWCY4d/4wKbDCIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6 +2azJAAAAAAQBIKbpGG2dWTX8j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDE +M0g12vYxoWM8Y81W+bHBw805I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUr +k0mXubZvyl4GBg== +-----END PGP PRIVATE KEY BLOCK-----` }); + const v6PrivateKeyWithOCBPref = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMAGXKB +exK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJjh3/jAwsJ +BwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6 +2azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2kCcUmKfvBXbAf6rh +RYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaEQsiPlR4zxP/TP7mhfVEe +7XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/ +LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u/tVY6a//1q0NWC1X+yui3O24wpsG +GBsKAAAALAWCY4d/4wKbDCIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6 +2azJAAAAAAQBIKbpGG2dWTX8j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDE +M0g12vYxoWM8Y81W+bHBw805I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUr +k0mXubZvyl4GBg== +-----END PGP PRIVATE KEY BLOCK-----` }); + + const encrypted = await openpgp.encrypt({ + message: await openpgp.createMessage({ text: 'test' }), + encryptionKeys: [v4PrivateKeyWithOCBPref, v6PrivateKeyWithOCBPref], + format: 'object' + }); + + const seipd = encrypted.packets[2]; + expect(seipd).to.be.instanceOf(openpgp.SymEncryptedIntegrityProtectedDataPacket); + expect(seipd.version).to.equal(2); + expect(seipd.aeadAlgorithm).to.equal(openpgp.enums.aead.ocb); + }); + + it('should support encrypting to a key without features (missing SEIPDv1 feature)', async function () { + const key = await openpgp.readKey({ armoredKey: rsaKeyWithNoFeaturesFromRNP }); + const encrypted = await openpgp.encrypt({ + message: await openpgp.createMessage({ text: 'test' }), + encryptionKeys: key + }); + const decrypted = await openpgp.decrypt({ + message: await openpgp.readMessage({ armoredMessage: encrypted }), + decryptionKeys: key + }); + expect(decrypted.data).to.equal('test'); + }); }); describe('encryptSessionKey - unit tests', function() { @@ -2150,6 +2402,114 @@ VFBLG8uc9IiaKann/DYBAJcZNZHRSfpDoV2pUA5EAEi2MdjxkRysFQnYPRAu data: util.hexToUint8Array('3e99c1bb485e70a1fcef09a7ad8d38d171015243bbdd853e1a2b0e334d122ff3') })).to.be.rejectedWith(/No encryption keys or passwords provided/); }); + + // keep this after the 'memory-heavy' test to confirm that the Wasm module was successfully reloaded + it('supports encrypting with argon2 s2k', async function() { + const config = { s2kType: openpgp.enums.s2k.argon2 }; + const passwords = 'password'; + const sessionKey = { + algorithm: 'aes128', + data: util.hexToUint8Array('01FE16BBACFD1E7B78EF3B865187374F') + }; + const encrypted = await openpgp.encryptSessionKey({ ...sessionKey, passwords, config, format: 'object' }); + expect(encrypted.packets).to.have.length(1); + const skesk = encrypted.packets[0]; + expect(skesk.s2k.type).to.equal('argon2'); + const [decryptedSessionKey] = await openpgp.decryptSessionKeys({ message: encrypted, passwords }); + expect(decryptedSessionKey).to.deep.equal(sessionKey); + }); + }); + + describe('decryptSessionKeys - unit tests', function() { + it('should decrypt message with two SKESKs where the wrong password returns a symmetric algo equal to 0', async function () { + // SKESK packets do not have an intrisic integrity check, and the session key must be used to check its validity. + // This message is such that when the password is used to try and decrypt the first (mismatching) SKESK, the result returns a '0' byte for + // the session key algo. This corresponds to the 'plaintext' algo identifier (https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#section-12.2.1), + // now removed from OpenPGP.js . + // This test guards against regressions caused by mishandling this value on SKESK decryption. + const message = await openpgp.readMessage({ armoredMessage:`-----BEGIN PGP MESSAGE----- + +wy4ECQMIf6HA/a6XkOAAlaU1z+uVfU5kmHmqNqahQH859nAMresQ6w1uIjsL +ZE5bwy4ECQMIKVfCf+GWBesA5KFPuIDa6kLLn/AvEgbCi5DOg2xdIf73SNSN +Tqy7nfex0sANAVtHHRkHVTRVTVa3MFjjiWeBEDtnfyVMntWJ21ihrIU9eb9p +qS7UljZZ0u++xSWclU2IGBXCIdO0wLuS6hYk3q5OFexWj8OIoYJX88nkA2iW +5xyGd9EFRWVsR4CREt8lwrIE2t/h8XpRlhJmY6Iefg8+2DeN8vDdhNs/B02o +0zAE0hF+3xvwZTLi4hDrhBZEgBedPaeX4jlmDc3qzh2wlgV/Mq/FUakYfSLl +bc1PCpfqkLZSuCfv4eoTrWohWi9lS/pRXY9hzzHtlnjo6w== +-----END PGP MESSAGE-----` }); + const sessionKeys = await openpgp.decryptSessionKeys({ message, passwords: 'I am another password' }); + expect(sessionKeys).to.have.length(1); // first SKESK dropped due to '0' being treated as invalid algo identifier + expect(sessionKeys[0].algorithm).to.equal('aes256'); + + // decrypt() used to fail on this type of input + const decrypted = await openpgp.decrypt({ message, passwords: 'I am another password', format: 'binary' }); + expect(decrypted.data).to.deep.equal( + util.hexToUint8Array('e280872009d699e0b5bae18ba1e28c86d280d184e1b888e1a8b3e28ab7e0afa8e0b98be283bde28db5e1b1afd48de1b5b2e280a7e199b0e1a487e185afd3a1e18ca5e0bfb4e28892e19e98e29bb8e29594e0baaae0b681e1929af09f9882f09f9897f09f9882f09f98abf09f988ff09f98b6f09f98bcf09f98bcf09f9981f09f9982e2808720090d0aed959ceab5adec96b42feca1b0ec84a0eba790') + ); + expect(decrypted.signatures.length).to.equal(0); + }); + + it('should decrypt PKESK v6 and return a null symmetric algorithm', async function() { + // test vector https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#appendix-A.8 + const armoredMessage = `-----BEGIN PGP MESSAGE----- + +wV0GIQYSyD8ecG9jCP4VGkF3Q6HwM3kOk+mXhIjR2zeNqZMIhRmHzxjV8bU/gXzO +WgBM85PMiVi93AZfJfhK9QmxfdNnZBjeo1VDeVZheQHgaVf7yopqR6W1FT6NOrfS +aQIHAgZhZBZTW+CwcW1g4FKlbExAf56zaw76/prQoN+bAzxpohup69LA7JW/Vp0l +yZnuSj3hcFj0DfqLTGgr4/u717J+sPWbtQBfgMfG9AOIwwrUBqsFE9zW+f1zdlYo +bhF30A+IitsxxA== +-----END PGP MESSAGE-----`; + + const privateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMAGXKB +exK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJjh3/jAwsJ +BwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6 +2azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2kCcUmKfvBXbAf6rh +RYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaEQsiPlR4zxP/TP7mhfVEe +7XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/ +LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u/tVY6a//1q0NWC1X+yui3O24wpsG +GBsKAAAALAWCY4d/4wKbDCIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6 +2azJAAAAAAQBIKbpGG2dWTX8j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDE +M0g12vYxoWM8Y81W+bHBw805I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUr +k0mXubZvyl4GBg== +-----END PGP PRIVATE KEY BLOCK-----` }); + + const sessionKeys = await openpgp.decryptSessionKeys({ + message: await openpgp.readMessage({ armoredMessage }), + decryptionKeys: privateKey + }); + + expect(sessionKeys).to.have.length(1); + expect(sessionKeys[0].algorithm).to.equal(null); // PKESK v6 does not include the algo info + }); + + it('supports decrypting with argon2 s2k (memory-heavy params)', async function() { + const passwords = 'password'; + // Test vector from https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-07.html#appendix-A.8.1 + const armoredMessage = `-----BEGIN PGP MESSAGE----- +Comment: Encrypted using AES with 128-bit key +Comment: Session key: 01FE16BBACFD1E7B78EF3B865187374F + +wycEBwScUvg8J/leUNU1RA7N/zE2AQQVnlL8rSLPP5VlQsunlO+ECxHSPgGYGKY+ +YJz4u6F+DDlDBOr5NRQXt/KJIf4m4mOlKyC/uqLbpnLJZMnTq3o79GxBTdIdOzhH +XfA3pqV4mTzF +-----END PGP MESSAGE-----`; + const expectedSessionKey = util.hexToUint8Array('01FE16BBACFD1E7B78EF3B865187374F'); + + try { + const [decryptedSessionKey] = await openpgp.decryptSessionKeys({ + message: await openpgp.readMessage({ armoredMessage }), + passwords + }); + expect(decryptedSessionKey.data).to.deep.equal(expectedSessionKey); + expect(decryptedSessionKey.algorithm).to.equal('aes128'); + } catch (err) { + if (detectBrowser()) { // Expected to fail in the CI, especially in Browserstack + expect(err.message).to.match(/Could not allocate required memory/); + } + } + }); }); describe('encrypt, decrypt, sign, verify - integration tests', function() { @@ -2166,7 +2526,7 @@ VFBLG8uc9IiaKann/DYBAJcZNZHRSfpDoV2pUA5EAEi2MdjxkRysFQnYPRAu let aeadProtectVal; let preferredAEADAlgorithmVal; let aeadChunkSizeByteVal; - let v5KeysVal; + let v6KeysVal; let minRSABitsVal; beforeEach(async function() { @@ -2183,7 +2543,7 @@ VFBLG8uc9IiaKann/DYBAJcZNZHRSfpDoV2pUA5EAEi2MdjxkRysFQnYPRAu aeadProtectVal = openpgp.config.aeadProtect; preferredAEADAlgorithmVal = openpgp.config.preferredAEADAlgorithm; aeadChunkSizeByteVal = openpgp.config.aeadChunkSizeByte; - v5KeysVal = openpgp.config.v5Keys; + v6KeysVal = openpgp.config.v6Keys; minRSABitsVal = openpgp.config.minRSABits; openpgp.config.minRSABits = 512; @@ -2193,7 +2553,7 @@ VFBLG8uc9IiaKann/DYBAJcZNZHRSfpDoV2pUA5EAEi2MdjxkRysFQnYPRAu openpgp.config.aeadProtect = aeadProtectVal; openpgp.config.preferredAEADAlgorithm = preferredAEADAlgorithmVal; openpgp.config.aeadChunkSizeByte = aeadChunkSizeByteVal; - openpgp.config.v5Keys = v5KeysVal; + openpgp.config.v6Keys = v6KeysVal; openpgp.config.minRSABits = minRSABitsVal; }); @@ -2221,24 +2581,24 @@ VFBLG8uc9IiaKann/DYBAJcZNZHRSfpDoV2pUA5EAEi2MdjxkRysFQnYPRAu } }); - tryTests('CFB mode (asm.js)', tests, { + tryTests('CFB mode', tests, { if: true, beforeEach: function() { openpgp.config.aeadProtect = false; } }); - tryTests('GCM mode (V5 keys)', tests, { + tryTests('GCM mode (V6 keys)', tests, { if: true, beforeEach: function() { openpgp.config.aeadProtect = true; - openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.experimentalGCM; - openpgp.config.v5Keys = true; + openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.gcm; + openpgp.config.v6Keys = true; - // Monkey-patch AEAD feature flag - publicKey.users[0].selfCertifications[0].features = [7]; - publicKey_2000_2008.users[0].selfCertifications[0].features = [7]; - publicKey_2038_2045.users[0].selfCertifications[0].features = [7]; + // Monkey-patch SEIPD V2 feature flag + publicKey.users[0].selfCertifications[0].features = [9]; + publicKey_2000_2008.users[0].selfCertifications[0].features = [9]; + publicKey_2038_2045.users[0].selfCertifications[0].features = [9]; } }); @@ -2247,30 +2607,31 @@ VFBLG8uc9IiaKann/DYBAJcZNZHRSfpDoV2pUA5EAEi2MdjxkRysFQnYPRAu beforeEach: function() { openpgp.config.aeadProtect = true; openpgp.config.aeadChunkSizeByte = 0; + openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.eax; - // Monkey-patch AEAD feature flag - publicKey.users[0].selfCertifications[0].features = [7]; - publicKey_2000_2008.users[0].selfCertifications[0].features = [7]; - publicKey_2038_2045.users[0].selfCertifications[0].features = [7]; + // Monkey-patch SEIPD V2 feature flag + publicKey.users[0].selfCertifications[0].features = [9]; + publicKey_2000_2008.users[0].selfCertifications[0].features = [9]; + publicKey_2038_2045.users[0].selfCertifications[0].features = [9]; } }); tryTests('OCB mode', tests, { - if: !openpgp.config.ci, + if: true, beforeEach: function() { openpgp.config.aeadProtect = true; openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.ocb; - // Monkey-patch AEAD feature flag - publicKey.users[0].selfCertifications[0].features = [7]; - publicKey_2000_2008.users[0].selfCertifications[0].features = [7]; - publicKey_2038_2045.users[0].selfCertifications[0].features = [7]; + // Monkey-patch SEIPD V2 feature flag + publicKey.users[0].selfCertifications[0].features = [9]; + publicKey_2000_2008.users[0].selfCertifications[0].features = [9]; + publicKey_2038_2045.users[0].selfCertifications[0].features = [9]; } }); function tests() { describe('encryptSessionKey, decryptSessionKeys', function() { - const sk = new Uint8Array([0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01]); + const sk = new Uint8Array([0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01]); let decryptedPrivateKey; // to avoid decrypting key before each test beforeEach(async function() { @@ -2330,7 +2691,7 @@ VFBLG8uc9IiaKann/DYBAJcZNZHRSfpDoV2pUA5EAEi2MdjxkRysFQnYPRAu }).then(() => { throw new Error('Should not decrypt with invalid key'); }).catch(error => { - expect(error.message).to.match(/Error decrypting session keys: Session key decryption failed./); + expect(error.message).to.match(/Error decrypting session keys: Could not find valid subkey binding signature in key/); }); }); }); @@ -2482,27 +2843,30 @@ VFBLG8uc9IiaKann/DYBAJcZNZHRSfpDoV2pUA5EAEi2MdjxkRysFQnYPRAu }); }); - it('should encrypt then decrypt with wildcard', async function () { - const encOpt = { + it('should encrypt then decrypt with wildcard (anonymous recipient)', async function () { + const { privateKey: privateKeyV4orV6 } = await openpgp.generateKey({ userIDs: { email: 'test@test.it' }, format: 'object' }); + const plaintext = 'hello world'; + + const encryptedMessage = await openpgp.encrypt({ message: await openpgp.createMessage({ text: plaintext }), - encryptionKeys: publicKey, - wildcard: true - }; - const decOpt = { - decryptionKeys: privateKey - }; - return openpgp.encrypt(encOpt).then(async function (encrypted) { - expect(encrypted).to.match(/^-----BEGIN PGP MESSAGE/); - decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted }); - return openpgp.decrypt(decOpt); - }).then(function (decrypted) { - expect(decrypted.data).to.equal(plaintext); - expect(decrypted.signatures).to.exist; - expect(decrypted.signatures.length).to.equal(0); + encryptionKeys: privateKeyV4orV6, + wildcard: true, + format: 'object' }); + + expect(encryptedMessage.getEncryptionKeyIDs().every(keyID => keyID.isWildcard())).to.be.true; + const armoredMessage = encryptedMessage.armor(); + + const parsedEncryptedMessage = await openpgp.readMessage({ armoredMessage }); + expect(parsedEncryptedMessage.getEncryptionKeyIDs().every(keyID => keyID.isWildcard())).to.be.true; + + const decrypted = await openpgp.decrypt({ message: parsedEncryptedMessage, decryptionKeys: privateKeyV4orV6 }); + expect(decrypted.data).to.equal(plaintext); + expect(decrypted.signatures).to.exist; + expect(decrypted.signatures.length).to.equal(0); }); - it('should encrypt then decrypt with wildcard with multiple private keys', async function () { + it('should encrypt then decrypt with wildcard with multiple private keys (anonymous recipient)', async function () { const privKeyDE = await openpgp.decryptKey({ privateKey: await openpgp.readKey({ armoredKey: priv_key_de }), passphrase @@ -2561,7 +2925,7 @@ VFBLG8uc9IiaKann/DYBAJcZNZHRSfpDoV2pUA5EAEi2MdjxkRysFQnYPRAu return openpgp.encrypt(encOpt).then(async function (encrypted) { expect(encrypted).to.match(/^-----BEGIN PGP MESSAGE/); decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted }); - expect(!!decOpt.message.packets.findPacket(openpgp.enums.packet.aeadEncryptedData)).to.equal(false); + expect(decOpt.message.packets.findPacket(openpgp.enums.packet.symEncryptedIntegrityProtectedData).version === 2).to.equal(false); return openpgp.decrypt(decOpt); }).then(function (decrypted) { expect(decrypted.data).to.equal(plaintext); @@ -2584,7 +2948,7 @@ VFBLG8uc9IiaKann/DYBAJcZNZHRSfpDoV2pUA5EAEi2MdjxkRysFQnYPRAu return openpgp.encrypt(encOpt).then(async function (encrypted) { expect(encrypted).to.match(/^-----BEGIN PGP MESSAGE/); decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted }); - expect(!!decOpt.message.packets.findPacket(openpgp.enums.packet.aeadEncryptedData)).to.equal(false); + expect(decOpt.message.packets.findPacket(openpgp.enums.packet.symEncryptedIntegrityProtectedData).version === 2).to.equal(false); return openpgp.decrypt(decOpt); }).then(function (decrypted) { expect(decrypted.data).to.equal(plaintext); @@ -2603,7 +2967,8 @@ VFBLG8uc9IiaKann/DYBAJcZNZHRSfpDoV2pUA5EAEi2MdjxkRysFQnYPRAu }; return openpgp.encrypt(encOpt).then(async function (encrypted) { decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted }); - expect(!!decOpt.message.packets.findPacket(openpgp.enums.packet.aeadEncryptedData)).to.equal(openpgp.config.aeadProtect); + const supportsSEIPDv2 = !!(publicKey.users[0].selfCertifications[0].features?.[0] & openpgp.enums.features.seipdv2); + expect(decOpt.message.packets.findPacket(openpgp.enums.packet.symEncryptedIntegrityProtectedData).version).to.equal(supportsSEIPDv2 ? 2 : 1); return openpgp.decrypt(decOpt); }).then(async function (decrypted) { expect(decrypted.data).to.equal(plaintext); @@ -2627,7 +2992,8 @@ VFBLG8uc9IiaKann/DYBAJcZNZHRSfpDoV2pUA5EAEi2MdjxkRysFQnYPRAu }; return openpgp.encrypt(encOpt).then(async function (encrypted) { decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted }); - expect(!!decOpt.message.packets.findPacket(openpgp.enums.packet.aeadEncryptedData)).to.equal(openpgp.config.aeadProtect); + const supportsSEIPDv2 = !!(publicKey.users[0].selfCertifications[0].features?.[0] & openpgp.enums.features.seipdv2); + expect(decOpt.message.packets.findPacket(openpgp.enums.packet.symEncryptedIntegrityProtectedData).version).to.equal(supportsSEIPDv2 ? 2 : 1); return openpgp.decrypt(decOpt); }).then(async function (decrypted) { expect(decrypted.data).to.equal(plaintext); @@ -2650,7 +3016,7 @@ VFBLG8uc9IiaKann/DYBAJcZNZHRSfpDoV2pUA5EAEi2MdjxkRysFQnYPRAu }; return openpgp.encrypt(encOpt).then(async function (encrypted) { decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted }); - expect(!!decOpt.message.packets.findPacket(openpgp.enums.packet.aeadEncryptedData)).to.equal(false); + expect(decOpt.message.packets.findPacket(openpgp.enums.packet.symEncryptedIntegrityProtectedData).version).to.equal(1); return openpgp.decrypt(decOpt); }).then(async function (decrypted) { expect(decrypted.data).to.equal(plaintext); @@ -2667,6 +3033,7 @@ VFBLG8uc9IiaKann/DYBAJcZNZHRSfpDoV2pUA5EAEi2MdjxkRysFQnYPRAu }; return openpgp.generateKey(genOpt).then(async function(newKey) { + const supportsSEIPDv2 = openpgp.config.aeadProtect; const newPublicKey = await openpgp.readKey({ armoredKey: newKey.publicKey }); const newPrivateKey = await openpgp.readKey({ armoredKey: newKey.privateKey }); @@ -2681,7 +3048,7 @@ VFBLG8uc9IiaKann/DYBAJcZNZHRSfpDoV2pUA5EAEi2MdjxkRysFQnYPRAu }; return openpgp.encrypt(encOpt).then(async function (encrypted) { decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted }); - expect(!!decOpt.message.packets.findPacket(openpgp.enums.packet.aeadEncryptedData)).to.equal(openpgp.config.aeadProtect); + expect(decOpt.message.packets.findPacket(openpgp.enums.packet.symEncryptedIntegrityProtectedData).version).to.equal(supportsSEIPDv2 ? 2 : 1); return openpgp.decrypt(decOpt); }).then(async function (decrypted) { expect(decrypted.data).to.equal(plaintext); @@ -2697,6 +3064,7 @@ VFBLG8uc9IiaKann/DYBAJcZNZHRSfpDoV2pUA5EAEi2MdjxkRysFQnYPRAu const newKey = await openpgp.generateKey({ userIDs: [{ name: 'Test User', email: 'text@example.com' }] }); + const supportsSEIPDv2 = openpgp.config.aeadProtect; const newPublicKey = await openpgp.readKey({ armoredKey: newKey.publicKey }); const newPrivateKey = await openpgp.readKey({ armoredKey: newKey.privateKey }); @@ -2710,7 +3078,7 @@ VFBLG8uc9IiaKann/DYBAJcZNZHRSfpDoV2pUA5EAEi2MdjxkRysFQnYPRAu detached: true }); const message = await openpgp.readMessage({ armoredMessage: encrypted }); - expect(!!message.packets.findPacket(openpgp.enums.packet.aeadEncryptedData)).to.equal(openpgp.config.aeadProtect); + expect(message.packets.findPacket(openpgp.enums.packet.symEncryptedIntegrityProtectedData).version).to.equal(supportsSEIPDv2 ? 2 : 1); const decrypted = await openpgp.decrypt({ message, signature: await openpgp.readSignature({ armoredSignature: signed }), @@ -2987,68 +3355,92 @@ VFBLG8uc9IiaKann/DYBAJcZNZHRSfpDoV2pUA5EAEi2MdjxkRysFQnYPRAu }); it('should fail to decrypt modified message', async function() { - const allowUnauthenticatedStream = openpgp.config.allowUnauthenticatedStream; - const { privateKey: key } = await openpgp.generateKey({ userIDs: [{ email: 'test@email.com' }], format: 'object' }); - expect(await isAEADSupported([key])).to.equal(openpgp.config.aeadProtect); + await loadStreamsPolyfill(); + // need to generate new key with AEAD support + const { privateKey } = await openpgp.generateKey({ userIDs: [{ email: 'test@email.com' }], type: 'rsa', format: 'object' }); + const { aeadAlgo } = await getPreferredCipherSuite([privateKey], undefined, undefined, openpgp.config); + // sanity check + expect(aeadAlgo).to.equal(openpgp.config.aeadProtect ? openpgp.config.preferredAEADAlgorithm : undefined); - const data = await openpgp.encrypt({ message: await openpgp.createMessage({ binary: new Uint8Array(500) }), encryptionKeys: [key.toPublic()] }); - let badSumEncrypted = data.replace(/\n=[a-zA-Z0-9/+]{4}/, '\n=aaaa'); - if (badSumEncrypted === data) { // checksum was already =aaaa - badSumEncrypted = data.replace(/\n=[a-zA-Z0-9/+]{4}/, '\n=bbbb'); + const encrypted = await openpgp.encrypt({ + message: await openpgp.createMessage({ binary: new Uint8Array(500) }), + encryptionKeys: privateKey + }); + // corrupt the SEIPD packet + const encryptedCorrupted = encrypted.substr(0, 1000) + (encrypted[1000] === 'a' ? 'b' : 'a') + encrypted.substr(1001); + + const generateSingleChunkStream = () => ( + new ReadableStream({ + start(controller) { + controller.enqueue(encryptedCorrupted); + controller.close(); + } + }) + ); + const generateMultiChunkStream = () => ( + new ReadableStream({ + start() { + this.remaining = encryptedCorrupted.split('\n'); + }, + async pull(controller) { + if (this.remaining.length) { + // sleep to slow down enqeueing + await new Promise(resolve => { setTimeout(resolve); }); + controller.enqueue(this.remaining.shift() + '\n'); + } else { + controller.close(); + } + } + }) + ); + + if (openpgp.config.aeadProtect) { + const expectedError = /Authentication tag mismatch|Unsupported state or unable to authenticate data/; + // AEAD fails either on AEAD chunk decryption or when reading the decrypted stream: + // if the corruption is in the first AEAD chunk, then `openpgp.decrypt` will throw + // when reading the decrypted stream to parse the packet list. + await Promise.all([ + testStreamingDecryption(encryptedCorrupted, true, expectedError, true), + testStreamingDecryption(encryptedCorrupted, false, expectedError, true), + // `config.allowUnauthenticatedStream` does not apply to AEAD + testStreamingDecryption(generateSingleChunkStream(), true, expectedError, openpgp.config.aeadChunkSizeByte > 0), + testStreamingDecryption(generateSingleChunkStream(), false, expectedError, openpgp.config.aeadChunkSizeByte > 0), + // Increasing number of streaming chunks should not affect the result + testStreamingDecryption(generateMultiChunkStream(), true, expectedError, openpgp.config.aeadChunkSizeByte > 0), + testStreamingDecryption(generateMultiChunkStream(), false, expectedError, openpgp.config.aeadChunkSizeByte > 0) + ]); + } else { + const expectedError = /Modification detected/; + await Promise.all([ + testStreamingDecryption(encryptedCorrupted, true, expectedError, true), + testStreamingDecryption(encryptedCorrupted, false, expectedError, true), + testStreamingDecryption(generateSingleChunkStream(), true, expectedError, false), + testStreamingDecryption(generateSingleChunkStream(), false, expectedError, true), + // Increasing number of streaming chunks should not affect the result + testStreamingDecryption(generateMultiChunkStream(), true, expectedError, false), + testStreamingDecryption(generateMultiChunkStream(), false, expectedError, true) + ]); } - if (badSumEncrypted === data) { - throw new Error('Was not able to successfully modify checksum'); - } - const badBodyEncrypted = data.replace(/\n=([a-zA-Z0-9/+]{4})/, 'aaa\n=$1'); - await stream.loadStreamsPonyfill(); - try { - for (const allowStreaming of [true, false]) { - openpgp.config.allowUnauthenticatedStream = allowStreaming; - await Promise.all([badSumEncrypted, badBodyEncrypted].map(async (encrypted, i) => { - await Promise.all([ - encrypted, - new stream.ReadableStream({ - start(controller) { - controller.enqueue(encrypted); - controller.close(); - } - }), - new stream.ReadableStream({ - start() { - this.remaining = encrypted.split('\n'); - }, - async pull(controller) { - if (this.remaining.length) { - await new Promise(res => { setTimeout(res); }); - controller.enqueue(this.remaining.shift() + '\n'); - } else { - controller.close(); - } - } - }) - ].map(async (encrypted, j) => { - let stepReached = 0; - try { - const message = await openpgp.readMessage({ armoredMessage: encrypted }); - stepReached = 1; - const { data: decrypted } = await openpgp.decrypt({ message: message, decryptionKeys: [key] }); - stepReached = 2; - await stream.readToEnd(decrypted); - } catch (e) { - expect(e.message).to.match(/Ascii armor integrity check failed/); - expect(stepReached).to.equal( - j === 0 ? 0 : - (openpgp.config.aeadChunkSizeByte === 0 && (j === 2 || detectNode() || util.getHardwareConcurrency() < 8)) || (!openpgp.config.aeadProtect && openpgp.config.allowUnauthenticatedStream) ? 2 : - 1 - ); - return; - } - throw new Error(`Expected "Ascii armor integrity check failed" error in subtest ${i}.${j}`); - })); - })); + + async function testStreamingDecryption(encryptedDataOrStream, allowUnauthenticatedStream, expectedErrorMessage, expectedFailureOnDecrypt = null) { + // parsing the message won't fail since armor checksum is ignored + const message = await openpgp.readMessage({ armoredMessage: encryptedDataOrStream }); + let didFailOnDecrypt = true; + + try { + const { data: decrypted } = await openpgp.decrypt({ + message, + decryptionKeys: [privateKey], + config: { allowUnauthenticatedStream } + }); + didFailOnDecrypt = false; + await stream.readToEnd(decrypted); + // expected to have thrown + throw new Error(`Expected decryption to fail with error ${expectedErrorMessage}`); + } catch (e) { + expect(e.message).to.match(expectedErrorMessage); + expect(didFailOnDecrypt).to.equal(expectedFailureOnDecrypt); } - } finally { - openpgp.config.allowUnauthenticatedStream = allowUnauthenticatedStream; } }); @@ -3264,10 +3656,8 @@ VFBLG8uc9IiaKann/DYBAJcZNZHRSfpDoV2pUA5EAEi2MdjxkRysFQnYPRAu it('Streaming encrypt and decrypt small message roundtrip', async function() { const plaintext = []; let i = 0; - const useNativeStream = (() => { try { new global.ReadableStream(); return true; } catch (e) { return false; } })(); // eslint-disable-line no-new - await stream.loadStreamsPonyfill(); - const ReadableStream = useNativeStream ? global.ReadableStream : stream.ReadableStream; - const data = new ReadableStream({ + await loadStreamsPolyfill(); + const data = new globalThis.ReadableStream({ pull(controller) { if (i++ < 4) { const randomBytes = random.getRandomBytes(10); @@ -3282,7 +3672,7 @@ VFBLG8uc9IiaKann/DYBAJcZNZHRSfpDoV2pUA5EAEi2MdjxkRysFQnYPRAu message: await openpgp.createMessage({ binary: data }), passwords: ['test'] })); - expect(stream.isStream(encrypted)).to.equal(useNativeStream ? 'web' : 'web-like'); + expect(stream.isStream(encrypted)).to.equal('web'); const message = await openpgp.readMessage({ armoredMessage: encrypted }); const decrypted = await openpgp.decrypt({ @@ -3290,7 +3680,7 @@ VFBLG8uc9IiaKann/DYBAJcZNZHRSfpDoV2pUA5EAEi2MdjxkRysFQnYPRAu message, format: 'binary' }); - expect(stream.isStream(decrypted.data)).to.equal(useNativeStream ? 'web' : 'web-like'); + expect(stream.isStream(decrypted.data)).to.equal('web'); expect(await stream.readToEnd(decrypted.data)).to.deep.equal(util.concatUint8Array(plaintext)); }); }); @@ -3504,7 +3894,9 @@ VFBLG8uc9IiaKann/DYBAJcZNZHRSfpDoV2pUA5EAEi2MdjxkRysFQnYPRAu signingKeys: privateKey_1337, detached: true, date: past, - format: 'binary' + format: 'binary', + // SHA-512 cannot be used with a 512-bit RSA key (digest too long) + config: { minRSABits: 512, preferredHashAlgorithm: openpgp.enums.hash.sha256 } }; const verifyOpt = { message, @@ -3593,17 +3985,13 @@ VFBLG8uc9IiaKann/DYBAJcZNZHRSfpDoV2pUA5EAEi2MdjxkRysFQnYPRAu it('should streaming sign and verify binary data without one-pass signature', async function () { const data = new Uint8Array([3, 14, 15, 92, 65, 35, 59]); - const dataStream = global.ReadableStream ? new global.ReadableStream({ + const dataStream = new globalThis.ReadableStream({ start(controller) { controller.enqueue(data); controller.close(); } - }) : new (require('stream').Readable)({ - read() { - this.push(data); - this.push(null); - } }); + const signOpt = { message: await openpgp.createMessage({ binary: dataStream }), signingKeys: privateKey, @@ -3614,20 +4002,18 @@ VFBLG8uc9IiaKann/DYBAJcZNZHRSfpDoV2pUA5EAEi2MdjxkRysFQnYPRAu format: 'binary' }; return openpgp.sign(signOpt).then(async function (signed) { - expect(stream.isStream(signed)).to.equal(global.ReadableStream ? 'web' : 'node'); + expect(stream.isStream(signed)).to.equal('web'); const message = await openpgp.readMessage({ binaryMessage: signed }); message.packets.push(...await stream.readToEnd(message.packets.stream, _ => _)); const packets = new openpgp.PacketList(); packets.push(message.packets.findPacket(openpgp.enums.packet.signature)); packets.push(message.packets.findPacket(openpgp.enums.packet.literalData)); verifyOpt.message = await openpgp.readMessage({ - binaryMessage: stream[ - global.ReadableStream ? (global.ReadableStream === stream.ReadableStream ? 'toStream' : 'toNativeReadable') : 'webToNode' - ](packets.write()) + binaryMessage: stream.toStream(packets.write()) }); return openpgp.verify(verifyOpt); }).then(async function (verified) { - expect(stream.isStream(verified.data)).to.equal(global.ReadableStream ? 'web' : 'node'); + expect(stream.isStream(verified.data)).to.equal('web'); expect([].slice.call(await stream.readToEnd(verified.data))).to.deep.equal([].slice.call(data)); expect(await verified.signatures[0].verified).to.be.true; expect(await privateKey.getSigningKey(verified.signatures[0].keyID)) @@ -3773,7 +4159,7 @@ VFBLG8uc9IiaKann/DYBAJcZNZHRSfpDoV2pUA5EAEi2MdjxkRysFQnYPRAu }).then(function() { throw new Error('Should not encrypt with revoked key'); }).catch(function(error) { - expect(error.message).to.match(/Error encrypting message: Primary key is revoked/); + expect(error.message).to.match(/Primary key is revoked/); }); }); }); @@ -3839,7 +4225,7 @@ VFBLG8uc9IiaKann/DYBAJcZNZHRSfpDoV2pUA5EAEi2MdjxkRysFQnYPRAu decryptionKeys: decryptedKeyDE }; // binding signature is invalid - await expect(openpgp.decrypt(decOpt)).to.be.rejectedWith(/Session key decryption failed/); + await expect(openpgp.decrypt(decOpt)).to.be.rejectedWith(/Could not find valid subkey binding signature in key/); }); it('RSA decryption with PKCS1 padding of wrong length should fail', async function() { @@ -4082,9 +4468,10 @@ bsZgJWVlAa5eil6J9ePX2xbo1vVAkLQdzE9+1jL+l7PRIZuVBQ== expect(data).to.equal('test'); }); - it('should enforce using AES session keys with x25519 keys (new format)', async function () { - // x25519 key (v4) with cast5 as preferred cipher - const privateKeyCast5 = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- + describe('X25519/Ed25519 (new format)', async function () { + it('should enforce using AES session keys with x25519 keys (v4 key)', async function () { + // x25519 key (v4) with cast5 as preferred cipher + const privateKeyCast5 = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- xUkEZK8BixuMghYwdEgHl+3ASI4VZkn048KG4DVuugT1bMe4QTtFtQCoKBOG JxrZh8E+7I5nK7McXP2U9gyC0+RFcD46AxSmRA46zQDCiAQQGwgAPgWCZK8B @@ -4099,30 +4486,483 @@ kl0L =SYJZ -----END PGP PRIVATE KEY BLOCK-----` }); - await expect(openpgp.generateSessionKey({ - encryptionKeys: privateKeyCast5, - config: { preferredSymmetricAlgorithm: openpgp.enums.symmetric.cast5 } - })).to.be.rejectedWith(/Could not generate a session key compatible with the given `encryptionKeys`/); + await expect(openpgp.generateSessionKey({ + encryptionKeys: privateKeyCast5, + config: { preferredSymmetricAlgorithm: openpgp.enums.symmetric.cast5 } + })).to.be.rejectedWith(/Could not generate a session key compatible with the given `encryptionKeys`/); - await expect(openpgp.encrypt({ - message: await openpgp.createMessage({ text: plaintext }), - encryptionKeys: privateKeyCast5, - sessionKey: { data: new Uint8Array(16).fill(1), algorithm: 'cast5' } - })).to.be.rejectedWith(/X25519 keys can only encrypt AES session keys/); - - await expect(openpgp.decryptSessionKeys({ - message: await openpgp.readMessage({ armoredMessage: `-----BEGIN PGP MESSAGE----- + await expect(openpgp.encrypt({ + message: await openpgp.createMessage({ text: plaintext }), + encryptionKeys: privateKeyCast5, + sessionKey: { data: new Uint8Array(16).fill(1), algorithm: 'cast5' } + })).to.be.rejectedWith(/X25519 and X448 keys can only encrypt AES session keys/); + await expect(openpgp.decryptSessionKeys({ + message: await openpgp.readMessage({ armoredMessage: `-----BEGIN PGP MESSAGE----- + wUQD66NYAXF0vfYZNWpc7s9eihtgj7EhHBeLOq2Ktw79artbhN5JMs+9aCIZ A7sB7uYCTVCLIMfPFwVZH+c29gpCzPxSXQ== =Dr02 -----END PGP MESSAGE-----` }), - decryptionKeys: privateKeyCast5 - })).to.be.rejectedWith(/AES session key expected/); + decryptionKeys: privateKeyCast5 + })).to.be.rejectedWith(/AES session key expected/); + }); + + it('supports decrypting new x25519 format (v4 key)', async function () { + // v4 key + const privateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xUkEZIbSkxsHknQrXGfb+kM2iOsOvin8yE05ff5hF8KE6k+saspAZQCy/kfFUYc2 +GkpOHc42BI+MsysKzk4ofjBAfqM+bb7goQ3hzRV1c2VyIDx1c2VyQHRlc3QudGVz +dD7ChwQTGwgAPQUCZIbSkwmQQezK2iB2tIkWIQRqZza9wQZcwxpjGYNB7MraIHa0 +iQIbAwIeAQIZAQILBwIVCAIWAAMnBwIAAFOeZ7jrKZsCzRfu1ffFa77074st0zRo +BTJXoXBQ1ZzLjsh+ZO6fB2odnYJtQYstv45H/3JyLVogcMnFeYmHeSP3AMdJBGSG +0pMZfpd7TiOQv7uKSK+k4HT9lKr5+dmvb7vox/8ids6unEkAF1v8fCKogIrtBWVT +nVbwnovjM3LLexpXFZSgTKRcNMgPRMJ0BBgbCAAqBQJkhtKTCZBB7MraIHa0iRYh +BGpnNr3BBlzDGmMZg0HsytogdrSJAhsMAADCYs2I9wBakIu9Hhxs4R3Jq9F8J7AH +yxsNL0GomZ+hxiE0MOZwRr10DxfVaRabF1fcf9PHSHX2SwEFXUKMIHgbMQs= +=bJqd +-----END PGP PRIVATE KEY BLOCK-----` }); + + const messageToDecrypt = `-----BEGIN PGP MESSAGE----- + +wUQDYc6clYlCdtoZ3rAsvBDIwvoLmvM0zwViG8Ec0PgFfN5R6C4BqEZD53UZB1WM +J68hXSj1Sa235XAUYE1pZerTKhglvdI9Aeve8+L0w5RDMjmBBA50Yv/YT8liqhNi +mNwbfFbSNhZYWjFada77EKBn60j8QT/xCQzLR1clci7ieW2knw== +=NKye +-----END PGP MESSAGE-----`; + const { data } = await openpgp.decrypt({ + message: await openpgp.readMessage({ armoredMessage: messageToDecrypt }), + decryptionKeys: privateKey + }); + expect(data).to.equal('Hello World!'); + }); + + it('supports encrypting/decrypting new x25519 format (v4 key)', async function () { + // v4 key + const privateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xUkEZIbSkxsHknQrXGfb+kM2iOsOvin8yE05ff5hF8KE6k+saspAZQCy/kfFUYc2 +GkpOHc42BI+MsysKzk4ofjBAfqM+bb7goQ3hzRV1c2VyIDx1c2VyQHRlc3QudGVz +dD7ChwQTGwgAPQUCZIbSkwmQQezK2iB2tIkWIQRqZza9wQZcwxpjGYNB7MraIHa0 +iQIbAwIeAQIZAQILBwIVCAIWAAMnBwIAAFOeZ7jrKZsCzRfu1ffFa77074st0zRo +BTJXoXBQ1ZzLjsh+ZO6fB2odnYJtQYstv45H/3JyLVogcMnFeYmHeSP3AMdJBGSG +0pMZfpd7TiOQv7uKSK+k4HT9lKr5+dmvb7vox/8ids6unEkAF1v8fCKogIrtBWVT +nVbwnovjM3LLexpXFZSgTKRcNMgPRMJ0BBgbCAAqBQJkhtKTCZBB7MraIHa0iRYh +BGpnNr3BBlzDGmMZg0HsytogdrSJAhsMAADCYs2I9wBakIu9Hhxs4R3Jq9F8J7AH +yxsNL0GomZ+hxiE0MOZwRr10DxfVaRabF1fcf9PHSHX2SwEFXUKMIHgbMQs= +=bJqd +-----END PGP PRIVATE KEY BLOCK-----` }); + const plaintext = 'plaintext'; + + const signed = await openpgp.encrypt({ + message: await openpgp.createMessage({ text: plaintext }), + encryptionKeys: privateKey + }); + + const { data } = await openpgp.decrypt({ + message: await openpgp.readMessage({ armoredMessage: signed }), + decryptionKeys: privateKey + }); + expect(data).to.equal(plaintext); + }); + + it('should decrypt test vector X25519-AEAD-OCB (PKESK v6, SEIPD v2)', async function() { + // test vector https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#appendix-A.8 + const armoredMessage = `-----BEGIN PGP MESSAGE----- + +wV0GIQYSyD8ecG9jCP4VGkF3Q6HwM3kOk+mXhIjR2zeNqZMIhRmHzxjV8bU/gXzO +WgBM85PMiVi93AZfJfhK9QmxfdNnZBjeo1VDeVZheQHgaVf7yopqR6W1FT6NOrfS +aQIHAgZhZBZTW+CwcW1g4FKlbExAf56zaw76/prQoN+bAzxpohup69LA7JW/Vp0l +yZnuSj3hcFj0DfqLTGgr4/u717J+sPWbtQBfgMfG9AOIwwrUBqsFE9zW+f1zdlYo +bhF30A+IitsxxA== +-----END PGP MESSAGE-----`; + + const privateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMAGXKB +exK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJjh3/jAwsJ +BwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6 +2azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2kCcUmKfvBXbAf6rh +RYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaEQsiPlR4zxP/TP7mhfVEe +7XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/ +LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u/tVY6a//1q0NWC1X+yui3O24wpsG +GBsKAAAALAWCY4d/4wKbDCIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6 +2azJAAAAAAQBIKbpGG2dWTX8j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDE +M0g12vYxoWM8Y81W+bHBw805I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUr +k0mXubZvyl4GBg== +-----END PGP PRIVATE KEY BLOCK-----` }); + + const { data: decryptedData } = await openpgp.decrypt({ + message: await openpgp.readMessage({ armoredMessage }), + decryptionKeys: privateKey + }); + + expect(decryptedData).to.equal('Hello, world!'); + }); + }); + + describe('X448/Ed448', async function () { + it('should enforce using AES session keys with x448 keys (v4 key)', async function () { + // X448 key (v4) with cast5 as preferred cipher + const privateKeyCast5 = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xXsEZRrtaRyScvyNjK0o5ccICztnWhA1MSij7WdzPfuNy7ryUzB+kqzpziBR +IIKp5PN0NW3mOYRDnUyo7QHBl4AA30tR5ED8u5v/rNIzKz/mKsD6XeYy+d0Q +5utwuR8BUxx9mcIUGdS65z9H6PUMGnfCwqAGVCTzBrSCHgTNAMK3BBAcCgA7 +BYJlGu1pAwsDBwmQkFi4G9HqQDwDFQgKAhYAAhkBApsDAh4BFiEE7kZAI1Dd +SVlLtf4QkFi4G9HqQDwAAPA7E+p0vwVLtUCfT0aBFzapFn8xjoow6jrUNTo3 +8EtaN0fqP2vaeQwW/vv26wobD+hbL2RwyFtAEV6AeeDsPVhbx7WA7yKHPzvl +GOYEGw0h57DuhvSxGciuyt0Y5PR2Vrz/2/wHGcEHzsrhTNysUetluxEAx3kE +ZRrtaRrySCLAqKQSATJOXdoRoNKVasJHlKrG3qgMbt1U6uSdctHBitTiHHTf +GU/Jg0ADA3Eg0bCyDupWNACmHJGu7q0o7O7BTAm0AsMbHxoIkNN9JsijwAp5 +FLtdXK9cAOkNaXPMkEGQkt1hmoW50lUq0iWcGBpzwqYEGBwKACoFgmUa7WkJ +kJBYuBvR6kA8ApsMFiEE7kZAI1DdSVlLtf4QkFi4G9HqQDwAAD3uf3qdwHY8 +65W22GR17PbqF+9uvkPpXLBi32FVPFkxJqYvIN5/LAQ33xdEE0mzO4As4+Oi +x8fsFb2AEXLEwlSnL+Eo0O+iUQd3/94yMbMFRlNxrdaqZ3+7CehbnieI/vby +LIEnN38XBi0HE70uoU5prxUA +-----END PGP PRIVATE KEY BLOCK-----` }); + + await expect(openpgp.generateSessionKey({ + encryptionKeys: privateKeyCast5, + config: { preferredSymmetricAlgorithm: openpgp.enums.symmetric.cast5 } + })).to.be.rejectedWith(/Could not generate a session key compatible with the given `encryptionKeys`/); + + await expect(openpgp.encrypt({ + message: await openpgp.createMessage({ text: plaintext }), + encryptionKeys: privateKeyCast5, + sessionKey: { data: new Uint8Array(16).fill(1), algorithm: 'cast5' } + })).to.be.rejectedWith(/X25519 and X448 keys can only encrypt AES session keys/); + + await expect(openpgp.decryptSessionKeys({ + message: await openpgp.readMessage({ armoredMessage: `-----BEGIN PGP MESSAGE----- + +wVwD2k7TUuqJwZkaXvEGk7B3pklJ5uRcRdKwwDJ40yKT0m5ic1e/2F+Se3xQ +zDE+N2DZ0B37pu4NUzTGBRo0oLD9EwwZA9+oJpBBOOry3cGmBYWvQHbvBpNE +5X5l8A== +-----END PGP MESSAGE-----` }), + decryptionKeys: privateKeyCast5 + })).to.be.rejectedWith(/AES session key expected/); + }); + + it('should enforce using 512-bit signature digest', async function () { + // X448 key using sha256 for self signatures + const privateKeySHA256 = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xXsEZCWHXBwwtqciq6ZFU13s+dyhkWR5tOEmF1oX8OiP1B5ypfqyGVM8DkQh +5eTIMwB1oqJCROANoyA0q2dSigAAbDA5xr74DeClPPXC4ZXJ9uzuJWKvQvE8 +x3EflhgoQCGBM7JfvH5zwdrJvPt8RKDvm0QkZzhPvnFoHnzNBHRlc3TCugQQ +HAgAPgWCZCWHXAQLCQcICZDsN6h/ys3ppwMVCAoEFgACAQIZAQKbAwIeARYh +BOJyE9P2eIcU2N2Ne+w3qH/KzemnAAAh1hTFCcEU77bU3YelrJTCNIOQnvt7 +Hs6yZz2053CQTOC+wHkUQLaYYBEXSNyLZxoyv+NuGTiwbuYtAOlbE2erM7Cx +8B2Qz7M29UkFLMBUfb+yi+gTYYUWCXVQ7Um7MGjjgUG8+9p452i6f28mhRD8 +tTgNAMd5BGQlh1wavTIFgILtbzrqQCiwDGx0YcFNzu9+FZ8vK5Mmm7UEZj0a +y7FWQtZw8tTaU6mY+RrSa52RjzkGLtQAQO++tgYqc+BnCFdCZ3ZYPRvD3mof +ffoo3l4xmto+iyvJZbQ4wQPXttg7VjCpEfOsL9TW9Xs09aIbysKmBBgcCAAq +BYJkJYdcCZDsN6h/ys3ppwKbDBYhBOJyE9P2eIcU2N2Ne+w3qH/KzemnAAC0 +6/eZhh/Oj2gRdab2JeFGWACGIRDKxPXsWRCXR4YrSxcvCKK6rOvsyxQsgIsJ +JyPYkRPfmbKcseUDAEkSBLAfeizDGh7ea0GOdIMhwE/CW4f/H8ULbwi36y13 +x3oMNVaYsI9dZ588Gpi8XYy2jOtqIPQ1AA== +-----END PGP PRIVATE KEY BLOCK-----` }); + + await expect(privateKeySHA256.getSigningKey()).to.be.rejectedWith(/Hash algorithm too weak for EdDSA/); + }); + + it('supports encrypting/decrypting with x448 (v4 key)', async function () { + // v4 key + const privateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xXsEZRqJ5BwHESfKnw5YJly5WobjigVm0kKY84NxrP6JKeIvIWiFqqSlozGpKZyR +50YbVTHmxpUCuJ7YNwX0UoAAoSO8IXmMM/XMd4ph00ju+fbSHdtQfyNhfFTi3UoM +V5DiFT+uOYDP+zwAwLWCR86csxmCWn6O10DNHcDNF1VzZXJBIDxVc2VyQUB0ZXN0 +LnRlc3Q+wroEExwKAD4FAmUaieQJEC8lwIrxSM+5FiEEGR2s5Bj5WVDN0Px6LyXA +ivFIz7kCGwMCHgkCGQECCwcDFQoIAhYAAycHAgAA21/PqAuGDL5+3qrf3YoVOP+5 +0BoJ+ZMhzcgax+cQTyndmOZYBfOqV/SJ8mf6CRhbB76JhGIvmRMtyYDQgDMVvcoA +yojVNs6e/Jco16bVJxM85wKDXJuq6AhtPQ8w/0WaCJtEf1uxqeQPEbOM+KtT/xY2 +KgDHeQRlGonkGuOtAhogSIU3z/+gFzF8U7JQe7QDRYr9VWfi2WXFFarzg/3DMRur +oIB7mqkaaSatrvVuud1ZmRCWAMM4f57dvSdCKsVqSe+tlS225OmdWmnGLqyErBb6 +44E2oENhDUom9OUGUPm8dXUjQbrmw6ec9hNLHWXCpgQYHAoAKgUCZRqJ5AkQLyXA +ivFIz7kWIQQZHazkGPlZUM3Q/HovJcCK8UjPuQIbDAAAZka10c8KlmwftJuboIV5 +DalGWrZhbywJpEZRfoikcebSYi5++w1SbpXZGu27sl+BznGyyyqAfxyJjoCZaqCs +ewbKh04DNAg4v4v0W0a8UvD3j/CuciEMXjK9nUErt91zEwxNZy43yrQY2aAayDs8 +94FqMAA= +=GBh1 +-----END PGP PRIVATE KEY BLOCK-----` }); + const plaintext = 'plaintext'; + + const signed = await openpgp.encrypt({ + message: await openpgp.createMessage({ text: plaintext }), + encryptionKeys: privateKey + }); + + const { data } = await openpgp.decrypt({ + message: await openpgp.readMessage({ armoredMessage: signed }), + decryptionKeys: privateKey + }); + expect(data).to.equal(plaintext); + }); + + it('supports encrypting/decrypting with x448 (v6 key)', async function () { + const privateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xX0GZRqLYhwAAAA52IEq/TpKiPp6RofQaq4uhCruTtiG+qiVFnwsQgeh0ui34kHD +Y1E04mBai0pCoDiFVokwsKt3F5sAAC8lDYfVP/p3atbXJDTJB2W9WmZxIS7pUGhS +bjlWpZB/OVTBsoIfP/2J+Hi4ESwBRfDUDgwK4aJVKsLAIAYfHAoAAAA/BQJlGoti +IqEGobsxt8WKsMuJWANyTXpWMdC1QN/7EyJClfcs+nBgqdUCGwMCHgkCCwcDFQoI +AhYABScHAwcCAAAAAPiGIG2qmhCULQ/+H4rKV0XEM1x0uVY3l878Pa6ijZLouZU/ +VRd5PnbGyLPL++q3LDViUUdZ1uusRc01f677Q6wpUU90k8MH/oULwI0+KPtqe1N4 +6nr1NTERsAmAaPjUdf4ZUXX/GWiTd/AlsS5JqGnAQxKRJkzCJacOTOElRMjzGUX7 +CGaAnhSC86YRZ68ocTPfZysAzRdVc2VyQiA8VXNlckJAdGVzdC50ZXN0PsLADQYT +HAoAAAAsBQJlGotiIqEGobsxt8WKsMuJWANyTXpWMdC1QN/7EyJClfcs+nBgqdUC +GQEAAAAASKwgVzMoPb2Hbr3lbNI1CRWECokYLokL7F8MbYiMnlg+v6QXLdStvT13 +ZjxdrWQAx3MbihSOUSXbdAys90yMOAdtognj+x418J/TaYFMtIGBHwoHv8gQVnx9 +9ICv8ezx1T5VvGBYNuKZ5Ww0WPEpYMf1VA+Y9JxpohdcRenNBdSug4tLWla2y8NH +aO28Fltpb4AuGQDHewZlGotiGgAAADjdabr1ohAOnbSUUkVhtUM/LVdnYgDLhmaj +YZ1N7TWY0fqEpMk2LLo2165HOmhddRPeTB1TWbuwBwB8lKc3czFUzYcAgvZ08T5S +UUHjfIhjeJeY4yd0OZDfzPw1vbegCc7t94bT+XGoIQbC/Bl7HCyAiMLADQYYHAoA +AAAsBQJlGotiIqEGobsxt8WKsMuJWANyTXpWMdC1QN/7EyJClfcs+nBgqdUCGwwA +AAAAHh0gf2kdqLoXdFX+aNVORr5VCVgcm2gBw8l68lDJ1ftA71bMllFi6Q5sLPUr +6UCpJYYk3o3hYUYzIHmCIZGVYe1pxRgIUNqxydXEfzylJmN5NbiJNkjJizrI7oAR +1mIcEEb/hmRMOUs1V2mcGuoeALBI/r/SyqDE2GRjH6d6g1RS7ZARPPHlZlY4CTqC +4a7L+8odDwA= +=chx0 +-----END PGP PRIVATE KEY BLOCK-----` }); + const plaintext = 'plaintext'; + + const signed = await openpgp.encrypt({ + message: await openpgp.createMessage({ text: plaintext }), + encryptionKeys: privateKey + }); + + const { data } = await openpgp.decrypt({ + message: await openpgp.readMessage({ armoredMessage: signed }), + decryptionKeys: privateKey + }); + expect(data).to.equal(plaintext); + }); + + it('decrypt/verify should succeed using X448/Ed448 (PKESK v4, SEIPD v2, GCM)', async function() { + // data generated by gopenpgp + const armoredMessage = `-----BEGIN PGP MESSAGE----- + +wWkGFQQ70agVm6o5r3tEzY5mrYaOV8yHChpUetZ33zrKGtw1F4PeFrE4bYkcTMQM +IcGoXcZj/0GJDCkOLLleSwrvuAuUwZV11bHBZ6eNTyj+XxhLdVflV/zqPmBhTHY9 +SMn0YYHwgiQFk6PSwGICBwMMRYkTpsy0dE4YKasf6b4Oh9cn6HYY5rjnrtvrwD+F +LrsELfudYwpwHBA5jnO11Hl5mUyXhhWSdPoLGdeiYP5R/vZjqoZr3P6FL4dCdVni +fGChUUSYmpO4HIFrRBt2gAxl+f0Q8GCOG8c7EQ7c5600kJOlHM7SuoLqsxd482V1 +H/1Rxd3cPwTDfOjH26KuDv60p0XjdCGyQXcDQMCPV+ZTs0TQl4wTFogZGaRMd9GC +5D5t/guKzR+H1ipXSFjFdWWTEehx8m0RKKKT3Bl81awKZb8ulR6YKI5x39nwOySN +azDRR3gn9xlKjcpa83k5sSZbUTxC8lzTeuMP0PkDrU2IpZUZOlzOhGYOGrtTFATK +PSoZU33h2h3hJqiX9aKrnw== +=Is5l +-----END PGP MESSAGE-----`; + + const privateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xXsEZRqJ5BxV/xdxR4KvPofk3EzKaIZqM0Wlw904q+2S6Z84OmXN6Q1xCYurcwN1 +wLOlGJ/CO2QhByEdGlUldwAAIQSMnMITcmXQU3EWK5S81FS+u1IZFP55j51bA5mS +HZ/A7MOpyN40ybW8mhMIXXUYB7kC/bOTmwVHGt3NF1VzZXJCIDxVc2VyQkB0ZXN0 +LnRlc3Q+wrwEExwKAEAFAmUaieQJEP3WralmOT0PFiEEq4zuQzIBSh+Nk6G9/dat +qWY5PQ8CGwMCHgkCGQECCwcDFQoIAhYABScHAwcCAADAvnnhLp0DJYk7E0GfksCg +pUnnCjEePMVvRPVY3dwr9wLpdL/7T70fz541XVE8giYiZD7eiKvfc/nMgOhu1eqK +uXGUtDGBeabitJcrbquy2Tp/ENuW6rRHP7sAbu0mj6XxYEeCzKjGRT5Iq25AMevm +yeoIAMd5BGUaieQaqj/dF+uZGt9QLuji2eOlDC0/quq/sAtdJTbI1xj04aF0X7kJ +lVhEKeWZeAEpD4rVOCsrhMvr21gAu/BaFVKGUOuf0+ZE5jGcFcBvEP7OGyO296ry +zV7ONWS/FuoZ/NZmgWo9m9ftPtwqKDsgOWxiIj4cesKmBBgcCgAqBQJlGonkCRD9 +1q2pZjk9DxYhBKuM7kMyAUofjZOhvf3WralmOT0PAhsMAADWb+0aY+NblShwsym/ +2geh6XaqQUCJgdRfEl8xYLau/o8QQAzRp0ZBA+KeK3uwhRW3RizuqIw5iribAK3+ +30Si5nvv0TivalPK2C9yAqzh9rkNNUQa9b17IHYs/WwQrvP3F5EZ3V+StqdveAEo +FecSL/wTAA== +=FANS +-----END PGP PRIVATE KEY BLOCK-----` }); + + const senderKey = await openpgp.readKey({ + armoredKey: `-----BEGIN PGP PUBLIC KEY BLOCK----- + +xj8EZRqJ5BwHESfKnw5YJly5WobjigVm0kKY84NxrP6JKeIvIWiFqqSlozGpKZyR +50YbVTHmxpUCuJ7YNwX0UoDNF1VzZXJBIDxVc2VyQUB0ZXN0LnRlc3Q+wroEExwK +AD4FAmUaieQJEC8lwIrxSM+5FiEEGR2s5Bj5WVDN0Px6LyXAivFIz7kCGwMCHgkC +GQECCwcDFQoIAhYAAycHAgAA21/PqAuGDL5+3qrf3YoVOP+50BoJ+ZMhzcgax+cQ +TyndmOZYBfOqV/SJ8mf6CRhbB76JhGIvmRMtyYDQgDMVvcoAyojVNs6e/Jco16bV +JxM85wKDXJuq6AhtPQ8w/0WaCJtEf1uxqeQPEbOM+KtT/xY2KgDOPgRlGonkGuOt +AhogSIU3z/+gFzF8U7JQe7QDRYr9VWfi2WXFFarzg/3DMRuroIB7mqkaaSatrvVu +ud1ZmRCWwqYEGBwKACoFAmUaieQJEC8lwIrxSM+5FiEEGR2s5Bj5WVDN0Px6LyXA +ivFIz7kCGwwAAGZGtdHPCpZsH7Sbm6CFeQ2pRlq2YW8sCaRGUX6IpHHm0mIufvsN +Um6V2Rrtu7Jfgc5xsssqgH8ciY6AmWqgrHsGyodOAzQIOL+L9FtGvFLw94/wrnIh +DF4yvZ1BK7fdcxMMTWcuN8q0GNmgGsg7PPeBajAA +=VA/P +-----END PGP PUBLIC KEY BLOCK-----` }); + + const { data: decryptedData, signatures } = await openpgp.decrypt({ + message: await openpgp.readMessage({ armoredMessage }), + decryptionKeys: privateKey, + verificationKeys: senderKey + }); + + expect(decryptedData).to.equal('Hello there'); + expect(signatures).to.have.length(1); + expect(await signatures[0].verified).to.be.true; + }); + + + it('decrypt/verify should succeed using X448/Ed448 (PKESK v6, SEIPD v2, GCM)', async function() { + // data generated by gopenpgp + const armoredMessage = `-----BEGIN PGP MESSAGE----- + +wXUGIQZh4qTsn8glFgGNbIdCTl8gH2OtkI/PAGCQ0gi9s9k/rhrDhXo7kUKDJ39F +fNp3kmAaM24Ce3bcYXwLy0gF2i6rxfL20D+g3cxv0i3CuXQCgcbojTN/8KY8ExiV +Xdfo+OWIZ5XndtyMpJW28BiLHru+n9bSwM8CBwMMENh7cT8lILXteh885FrUUD1Q +JMtD7xJUn2y78cVGgFSIkLbvFPDerB37xuhtMRkykuWgbUoJH/kcgBPdeCoYzJmf +LV9FyATv0/AYq0yWpQ0VUfNLTFyeHIGxz7NHvrzJSrOy1Gm31PXqWvb4sBROjnOX +oAk12JdPudz3l1QZT/DX947f4h6hwkVv7RRT0oOS2pMaz/mekRuD6utUcpsjFQ/M +EDphnhOsB4RH0il8YPVc9DCnf3GhSs66h+Z699MXHBaUmdtiN1IgoEgLfb/900U2 +TfI6dvrvC56WIMA8EA1COvLGc9Ge4owW0UE8jIuqWLzA2nVg5belbzhNnOEh9b1c +OcDUh8CfBuXqHEi/ANMUOMmaIGfcHfQFVu5v/UMcLxcH/fSVF6DvtOxEoUxASWBS +mp6yC4A778BFuDFXb+/T8FjuJBaUj9rCSkYqt1TYVKG1XZPI4OdIvGtneo+vH/Cq +F6bxlLWU6oskZ5SE+xJblmmO01ObM9JRi9D8jZnXedTHExAnXHXIb8I= +=5RQw +-----END PGP MESSAGE-----`; + + const privateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xX0GZRqLYhwAAAA52IEq/TpKiPp6RofQaq4uhCruTtiG+qiVFnwsQgeh0ui34kHD +Y1E04mBai0pCoDiFVokwsKt3F5sAAC8lDYfVP/p3atbXJDTJB2W9WmZxIS7pUGhS +bjlWpZB/OVTBsoIfP/2J+Hi4ESwBRfDUDgwK4aJVKsLAIAYfHAoAAAA/BQJlGoti +IqEGobsxt8WKsMuJWANyTXpWMdC1QN/7EyJClfcs+nBgqdUCGwMCHgkCCwcDFQoI +AhYABScHAwcCAAAAAPiGIG2qmhCULQ/+H4rKV0XEM1x0uVY3l878Pa6ijZLouZU/ +VRd5PnbGyLPL++q3LDViUUdZ1uusRc01f677Q6wpUU90k8MH/oULwI0+KPtqe1N4 +6nr1NTERsAmAaPjUdf4ZUXX/GWiTd/AlsS5JqGnAQxKRJkzCJacOTOElRMjzGUX7 +CGaAnhSC86YRZ68ocTPfZysAzRdVc2VyQiA8VXNlckJAdGVzdC50ZXN0PsLADQYT +HAoAAAAsBQJlGotiIqEGobsxt8WKsMuJWANyTXpWMdC1QN/7EyJClfcs+nBgqdUC +GQEAAAAASKwgVzMoPb2Hbr3lbNI1CRWECokYLokL7F8MbYiMnlg+v6QXLdStvT13 +ZjxdrWQAx3MbihSOUSXbdAys90yMOAdtognj+x418J/TaYFMtIGBHwoHv8gQVnx9 +9ICv8ezx1T5VvGBYNuKZ5Ww0WPEpYMf1VA+Y9JxpohdcRenNBdSug4tLWla2y8NH +aO28Fltpb4AuGQDHewZlGotiGgAAADjdabr1ohAOnbSUUkVhtUM/LVdnYgDLhmaj +YZ1N7TWY0fqEpMk2LLo2165HOmhddRPeTB1TWbuwBwB8lKc3czFUzYcAgvZ08T5S +UUHjfIhjeJeY4yd0OZDfzPw1vbegCc7t94bT+XGoIQbC/Bl7HCyAiMLADQYYHAoA +AAAsBQJlGotiIqEGobsxt8WKsMuJWANyTXpWMdC1QN/7EyJClfcs+nBgqdUCGwwA +AAAAHh0gf2kdqLoXdFX+aNVORr5VCVgcm2gBw8l68lDJ1ftA71bMllFi6Q5sLPUr +6UCpJYYk3o3hYUYzIHmCIZGVYe1pxRgIUNqxydXEfzylJmN5NbiJNkjJizrI7oAR +1mIcEEb/hmRMOUs1V2mcGuoeALBI/r/SyqDE2GRjH6d6g1RS7ZARPPHlZlY4CTqC +4a7L+8odDwA= +=chx0 +-----END PGP PRIVATE KEY BLOCK-----` }); + + const senderKey = await openpgp.readKey({ + armoredKey: `-----BEGIN PGP PUBLIC KEY BLOCK----- + +xkMGZRqLYhwAAAA5U/IaIOge/FoLzCetXKx029bdJHCz2hMFBRMuzq4msjaT+hLe +V6puyC/PeSEfaanqTuo31vvsti2AwsAeBh8cCgAAAD0FAmUai2IioQYDGJ2wdEcO +zIVPDVDs6gYQASdGfG2EozBUGqEgvaj4dQIbAwIeCQILBwMVCggCFgADJwcCAAAA +ADYqIL5j5+FD/jwKRP1atdNf1IKfe8fPjdZv74CSalYvUdCaskTdLiAaW17NkrYT +2i9qDPErFWsvXi4LqGzqQnQkiJZBJ4x57EJPL4Z2vqPTBvgWEU2egi7fK7YAGZmk +Vf/n/X3Vh5ZSvIoUMChRmYqBBNI7MkS/I7QAJHkvi9XcANx44B0bz+yqETz2tNJ6 +8VeeDgkAzRdVc2VyQSA8VXNlckFAdGVzdC50ZXN0PsLADQYTHAoAAAAsBQJlGoti +IqEGAxidsHRHDsyFTw1Q7OoGEAEnRnxthKMwVBqhIL2o+HUCGQEAAAAAXD8gfvYz +WLLMxaFuC3C/RJH9fG84hb9mtPgjH3bfqW+g4Ti1ov8PjoJtk6ObtUB45J9J3G3X +FIqegAtGwI1Dy1U+M9dyXOqvpHwxs8iAFbEpwxLZ5K1ikFsbmoCZz4rmN0DbFyX2 +JbltaV5nUtNqHiUXqoKIPvch98ANe3PDyIAxNf7TAzk3W0lQQa+Cp7TSiFEqJADO +QgZlGotiGgAAADjKb5lwMEt0ubSvwydaAF89wsn6H8NJO7kox5ioWW2Grn88CUZD +YaRBZj3ZH8HMdaih5kN4hJAeCMLADQYYHAoAAAAsBQJlGotiIqEGAxidsHRHDsyF +Tw1Q7OoGEAEnRnxthKMwVBqhIL2o+HUCGwwAAAAAFIQgGYYweuBej4XHAgZrcez8 +8VoTbIZDjMv6Qbj9g6jjW16Fyp10DKda10FFmbY+YjbNvQNYksF9bN/KFSS/PTYt +AVaOZDfW4fiN5s1QaYmA/xCT/zLHEYGryYCJLoLd7KLw28LS1KAWrC9h5cY6+fZE +05cavO/D/WqBLVPuA+5bftXnDvGcVS1p7buaMtQjKz4hAwA= +=GUIG +-----END PGP PUBLIC KEY BLOCK-----` }); + + const { data: decryptedData, signatures } = await openpgp.decrypt({ + message: await openpgp.readMessage({ armoredMessage }), + decryptionKeys: privateKey, + verificationKeys: senderKey + }); + + expect(decryptedData).to.equal('Hello there'); + expect(signatures).to.have.length(1); + expect(await signatures[0].verified).to.be.true; + }); + + it('decrypt/verify should succeed using X448/Ed448 (PKESK v6, SEIPD v2, OCB)', async function() { + // data generated by gopenpgp + const armoredMessage = `-----BEGIN PGP MESSAGE----- + +wXUGIQaYemlYu2ObOZ2IjFbL77NygqexwaCgtb0COZ0EnXfXlBri0wADNxbvwCnJ +GDlRX9VhIy46oPAvVJjm2d7ZC6wqxNfFuzQEB8KzwYBkExmZuAfO5KJ8la6+DRhc +OUH3A9cBGzq0eiKaKRqjHkiLHY5pFNPSwNoCBwIM98RL63I8iMyxcXpXQlBrYlBx +5uegrENlleNg6UJFr7rBT4eJH+Qeksb//V87eZymzqXZBsrTYmUjsFgYd5kL8NlU +wovy+qQnZmEaUKieDx3w+orR8b32ub5CNjHJa5lCdNWsIK825S5JUifZDd3hR6lC +EgtZRwxY/1CyQU94LR9j6w/YVF0W31+LxGGkL+uJEx0khJUzpxUM9QSEREOY7Frs +EegHNwDvxvxEwWpfkJOPIDME6Y7UcpsNp6xgiZ/XF06IRsliCRbeYaH1IWW+y0OS +CmPvvTFUzjwTxWogDccHz8YLHU0y6TKxf14YMvVLg2tf2P/BVVZSg0ejz6pfDKA5 +AP+Q/eXBAH272SpBjKo7YcVpTsz0KpWyhB6Jra4xaUFkt6pg39ydR3RJMvxbQVlR +aZqV/+1rwIiIauyHKiJFdCiXYPDU3xibVkFIFhuk5JwHm29XvOV1r8FFx7d78X5P +yJnXcXsl+GxwOojcLXSL0CEIU/iRqyAIyyhvUyyss3glehhgx0fENV2P/Ygi/naN +nJUJgg== +=m19C +-----END PGP MESSAGE-----`; + + const privateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xX0GZRqLYhwAAAA5U/IaIOge/FoLzCetXKx029bdJHCz2hMFBRMuzq4msjaT+hLe +V6puyC/PeSEfaanqTuo31vvsti2AAIttr4GDGXF4vfPzbzkWV9dT4VVsIU7QqLv1 +hzwZ+k7pHroRyXnUiYxRYHuzlg7Vw4CrAtN/8T65OMLAHgYfHAoAAAA9BQJlGoti +IqEGAxidsHRHDsyFTw1Q7OoGEAEnRnxthKMwVBqhIL2o+HUCGwMCHgkCCwcDFQoI +AhYAAycHAgAAAAA2KiC+Y+fhQ/48CkT9WrXTX9SCn3vHz43Wb++AkmpWL1HQmrJE +3S4gGltezZK2E9ovagzxKxVrL14uC6hs6kJ0JIiWQSeMeexCTy+Gdr6j0wb4FhFN +noIu3yu2ABmZpFX/5/191YeWUryKFDAoUZmKgQTSOzJEvyO0ACR5L4vV3ADceOAd +G8/sqhE89rTSevFXng4JAM0XVXNlckEgPFVzZXJBQHRlc3QudGVzdD7CwA0GExwK +AAAALAUCZRqLYiKhBgMYnbB0Rw7MhU8NUOzqBhABJ0Z8bYSjMFQaoSC9qPh1AhkB +AAAAAFw/IH72M1iyzMWhbgtwv0SR/XxvOIW/ZrT4Ix9236lvoOE4taL/D46CbZOj +m7VAeOSfSdxt1xSKnoALRsCNQ8tVPjPXclzqr6R8MbPIgBWxKcMS2eStYpBbG5qA +mc+K5jdA2xcl9iW5bWleZ1LTah4lF6qCiD73IffADXtzw8iAMTX+0wM5N1tJUEGv +gqe00ohRKiQAx3sGZRqLYhoAAAA4ym+ZcDBLdLm0r8MnWgBfPcLJ+h/DSTu5KMeY +qFlthq5/PAlGQ2GkQWY92R/BzHWooeZDeISQHggAuraV/u+CE642fcbcq90OY+qg +n739wkHcBps/s/MgMI+Q2H13vEsFpYZ/kuBIIYP39xkdU48/1GbCwA0GGBwKAAAA +LAUCZRqLYiKhBgMYnbB0Rw7MhU8NUOzqBhABJ0Z8bYSjMFQaoSC9qPh1AhsMAAAA +ABSEIBmGMHrgXo+FxwIGa3Hs/PFaE2yGQ4zL+kG4/YOo41tehcqddAynWtdBRZm2 +PmI2zb0DWJLBfWzfyhUkvz02LQFWjmQ31uH4jebNUGmJgP8Qk/8yxxGBq8mAiS6C +3eyi8NvC0tSgFqwvYeXGOvn2RNOXGrzvw/1qgS1T7gPuW37V5w7xnFUtae27mjLU +Iys+IQMA +=iwhO +-----END PGP PRIVATE KEY BLOCK-----` }); + + const senderKey = await openpgp.readKey({ + armoredKey: `-----BEGIN PGP PUBLIC KEY BLOCK----- + +xkMGZRqLYhwAAAA52IEq/TpKiPp6RofQaq4uhCruTtiG+qiVFnwsQgeh0ui34kHD +Y1E04mBai0pCoDiFVokwsKt3F5sAwsAgBh8cCgAAAD8FAmUai2IioQahuzG3xYqw +y4lYA3JNelYx0LVA3/sTIkKV9yz6cGCp1QIbAwIeCQILBwMVCggCFgAFJwcDBwIA +AAAA+IYgbaqaEJQtD/4fispXRcQzXHS5VjeXzvw9rqKNkui5lT9VF3k+dsbIs8v7 +6rcsNWJRR1nW66xFzTV/rvtDrClRT3STwwf+hQvAjT4o+2p7U3jqevU1MRGwCYBo ++NR1/hlRdf8ZaJN38CWxLkmoacBDEpEmTMIlpw5M4SVEyPMZRfsIZoCeFILzphFn +ryhxM99nKwDNF1VzZXJCIDxVc2VyQkB0ZXN0LnRlc3Q+wsANBhMcCgAAACwFAmUa +i2IioQahuzG3xYqwy4lYA3JNelYx0LVA3/sTIkKV9yz6cGCp1QIZAQAAAABIrCBX +Myg9vYduveVs0jUJFYQKiRguiQvsXwxtiIyeWD6/pBct1K29PXdmPF2tZADHcxuK +FI5RJdt0DKz3TIw4B22iCeP7HjXwn9NpgUy0gYEfCge/yBBWfH30gK/x7PHVPlW8 +YFg24pnlbDRY8Slgx/VUD5j0nGmiF1xF6c0F1K6Di0taVrbLw0do7bwWW2lvgC4Z +AM5CBmUai2IaAAAAON1puvWiEA6dtJRSRWG1Qz8tV2diAMuGZqNhnU3tNZjR+oSk +yTYsujbXrkc6aF11E95MHVNZu7AHwsANBhgcCgAAACwFAmUai2IioQahuzG3xYqw +y4lYA3JNelYx0LVA3/sTIkKV9yz6cGCp1QIbDAAAAAAeHSB/aR2ouhd0Vf5o1U5G +vlUJWBybaAHDyXryUMnV+0DvVsyWUWLpDmws9SvpQKklhiTejeFhRjMgeYIhkZVh +7WnFGAhQ2rHJ1cR/PKUmY3k1uIk2SMmLOsjugBHWYhwQRv+GZEw5SzVXaZwa6h4A +sEj+v9LKoMTYZGMfp3qDVFLtkBE88eVmVjgJOoLhrsv7yh0PAA== +=2Usy +-----END PGP PUBLIC KEY BLOCK-----` }); + + const { data: decryptedData, signatures } = await openpgp.decrypt({ + message: await openpgp.readMessage({ armoredMessage }), + decryptionKeys: privateKey, + verificationKeys: senderKey + }); + + expect(decryptedData).to.equal('Hello nice to meet you'); + expect(signatures).to.have.length(1); + expect(await signatures[0].verified).to.be.true; + }); }); describe('Sign and verify with each curve', function() { - const curves = ['secp256k1' , 'p256', 'p384', 'p521', 'curve25519', 'brainpoolP256r1', 'brainpoolP384r1', 'brainpoolP512r1']; + const curves = ['secp256k1' , 'nistP256', 'nistP384', 'nistP521', 'curve25519Legacy', 'brainpoolP256r1', 'brainpoolP384r1', 'brainpoolP512r1']; curves.forEach(curve => { it(`sign/verify with ${curve}`, async function() { const config = { rejectCurves: new Set() }; @@ -4135,17 +4975,27 @@ A7sB7uYCTVCLIMfPFwVZH+c29gpCzPxSXQ== }); it('sign/verify with new Ed25519 format', async function () { - // v4 key, which we do not support generating - const privateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- + const userIDs = { name: 'Alice', email: 'info@alice.com' }; + const { privateKey } = await openpgp.generateKey({ type: 'curve25519', userIDs, format: 'object' }); + const plaintext = 'plaintext'; -xUkEZBw5PBscroGar9fsilA0q9AX979pBhTNkGQ69vQGGW7kxRxNuABB+eAw -JrQ9A3o1gUJg28ORTQd72+kFo87184qR97a6rRGFzQR0ZXN0wogEEBsIAD4F -gmQcOTwECwkHCAmQT/m+Rl22Ps8DFQgKBBYAAgECGQECmwMCHgEWIQSUlOfm -G7MWJd2909ZP+b5GXbY+zwAAVs/4pWH4l7pWcTATBavVqSATMKi4A+usp89G -J/qaHc+qmcEpIMmPNvLQ7n4F4kEXk8Zwz+OXovVWLQ+Njl5gzooF -=wYg1 ------END PGP PRIVATE KEY BLOCK----- - ` }); + const signed = await openpgp.sign({ + message: await openpgp.createMessage({ text: plaintext }), + signingKeys: privateKey + }); + + const { signatures, data } = await openpgp.verify({ + message: await openpgp.readMessage({ armoredMessage: signed }), + verificationKeys: privateKey + }); + expect(data).to.equal(plaintext); + expect(signatures).to.have.length(1); + expect(await signatures[0].verified).to.be.true; + }); + + it('sign/verify with Ed448', async function () { + const userIDs = { name: 'Alice', email: 'info@alice.com' }; + const { privateKey } = await openpgp.generateKey({ type: 'curve448', userIDs, format: 'object' }); const plaintext = 'plaintext'; const signed = await openpgp.sign({ diff --git a/test/general/packet.js b/test/general/packet.js index 212e874f..e977ebab 100644 --- a/test/general/packet.js +++ b/test/general/packet.js @@ -1,14 +1,18 @@ /* eslint-disable max-lines */ -const stream = require('@openpgp/web-stream-tools'); -const stub = require('sinon/lib/sinon/stub'); -const { use: chaiUse, expect } = require('chai'); -chaiUse(require('chai-as-promised')); +import * as stream from '@openpgp/web-stream-tools'; +import sinon from 'sinon'; +import { use as chaiUse, expect } from 'chai'; +import chaiAsPromised from 'chai-as-promised'; // eslint-disable-line import/newline-after-import +chaiUse(chaiAsPromised); -const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../..'); -const crypto = require('../../src/crypto'); -const util = require('../../src/util'); +import openpgp from '../initOpenpgp.js'; +import crypto from '../../src/crypto'; +import util from '../../src/util.js'; +import * as packet from '../../src/packet'; +import * as random from '../../src/crypto/random'; -const input = require('./testInputs'); +import * as input from './testInputs.js'; +import { mockCryptoRandomGenerator, restoreCryptoRandomGenerator } from '../mockRandom.ts'; function stringify(array) { if (stream.isStream(array)) { @@ -26,7 +30,7 @@ function stringify(array) { return result.join(''); } -module.exports = () => describe('Packet', function() { +export default () => describe('Packet', function() { const allAllowedPackets = util.constructAllowedPackets([...Object.values(openpgp).filter(packetClass => !!packetClass.tag)]); const armored_key = @@ -136,6 +140,7 @@ module.exports = () => describe('Packet', function() { const literal = new openpgp.LiteralDataPacket(); const enc = new openpgp.SymEncryptedIntegrityProtectedDataPacket(); + enc.version = 1; enc.packets = new openpgp.PacketList(); enc.packets.push(literal); const msg = new openpgp.PacketList(); @@ -151,7 +156,51 @@ module.exports = () => describe('Packet', function() { expect(await stringify(msg2[0].packets[0].data)).to.equal(stringify(literal.data)); }); - it('Sym. encrypted AEAD protected packet', function() { + describe('Sym. encrypted integrity protected packet - throw on unexpected session key size', async () => { + async function testSEIPD(packetOptions) { + const symmetricAlgo = openpgp.enums.symmetric.aes256; + const key = random.getRandomBytes(crypto.getCipherParams(symmetricAlgo).keySize); + const unexpectedSymmetricAlgo = openpgp.enums.symmetric.aes128; + const keyWithUnexpectedSize = random.getRandomBytes(crypto.getCipherParams(unexpectedSymmetricAlgo).keySize); + + const plaintext = 'hello world'; + const literal = new openpgp.LiteralDataPacket(); + literal.setText(plaintext); + const seipd = openpgp.SymEncryptedIntegrityProtectedDataPacket.fromObject(packetOptions); + seipd.packets = new openpgp.PacketList(); + seipd.packets.push(literal); + + await expect( + seipd.encrypt(symmetricAlgo, keyWithUnexpectedSize, undefined, openpgp.config)).to.be.rejectedWith(/Unexpected session key size/); + expect(seipd.encrypted).to.be.null; + await seipd.encrypt(symmetricAlgo, key, undefined, openpgp.config); + expect(seipd.encrypted).to.not.be.null; + + const seipdParsed = new openpgp.SymEncryptedIntegrityProtectedDataPacket(); + await seipdParsed.read(seipd.write()); + await expect( + seipdParsed.decrypt(unexpectedSymmetricAlgo, keyWithUnexpectedSize, undefined, openpgp.config) + ).to.be.rejectedWith( + seipdParsed.version === 1 ? + /Modification detected/ : // SEIPDv1 does not store info about the symmetric algo + /Unexpected session key algo/ + ); + await expect( + seipdParsed.decrypt(unexpectedSymmetricAlgo, key, undefined, openpgp.config) + ).to.be.rejectedWith(/Unexpected session key size/); + await expect( + seipdParsed.decrypt(symmetricAlgo, keyWithUnexpectedSize, undefined, openpgp.config) + ).to.be.rejectedWith(/Unexpected session key size/); + await seipdParsed.decrypt(symmetricAlgo, key, undefined, openpgp.config); + + expect(seipdParsed.packets[0].getText()).to.equal(plaintext); + } + + it('SEIPDv1', () => testSEIPD({ version: 1 })); + it('SEIPDv2', () => testSEIPD({ version: 2, aeadAlgorithm: openpgp.enums.aead.gcm })); + }); + + it('Sym. encrypted AEAD protected packet (AEADP)', function() { const aeadProtectVal = openpgp.config.aeadProtect; openpgp.config.aeadProtect = false; @@ -182,25 +231,26 @@ module.exports = () => describe('Packet', function() { function cryptStub(webCrypto, method) { const crypt = webCrypto[method]; - const cryptStub = stub(webCrypto, method); + const cryptStub = sinon.stub(webCrypto, method); let cryptCallsActive = 0; cryptStub.onCall(0).callsFake(async function() { cryptCallsActive++; try { - return await crypt.apply(this, arguments); // eslint-disable-line no-invalid-this + // eslint-disable-next-line @typescript-eslint/return-await + return await crypt.apply(this, arguments); } finally { cryptCallsActive--; } }); cryptStub.onCall(1).callsFake(function() { expect(cryptCallsActive).to.equal(1); - return crypt.apply(this, arguments); // eslint-disable-line no-invalid-this + return crypt.apply(this, arguments); }); cryptStub.callThrough(); return cryptStub; } - it('Sym. encrypted AEAD protected packet is encrypted in parallel (AEAD, GCM)', async function() { + it('Sym. encrypted AEAD protected packet is encrypted in parallel (AEADP, GCM)', async function() { const webCrypto = util.getWebCrypto(); if (!webCrypto || util.getNodeCrypto()) return; const encryptStub = cryptStub(webCrypto, 'encrypt'); @@ -235,12 +285,9 @@ module.exports = () => describe('Packet', function() { } }); - it('Sym. encrypted AEAD protected packet test vector (AEAD)', async function() { + it('AEAD Encrypted Data packet test vector (AEADP)', async function() { // From https://gitlab.com/openpgp-wg/rfc4880bis/commit/00b20923e6233fb6ff1666ecd5acfefceb32907d - const nodeCrypto = util.getNodeCrypto(); - if (!nodeCrypto) return; - const packetBytes = util.hexToUint8Array(` d4 4a 01 07 01 0e b7 32 37 9f 73 c4 92 8d e2 5f ac fe 65 17 ec 10 5d c1 1a 81 dc 0c b8 a2 f6 f3 @@ -262,12 +309,16 @@ module.exports = () => describe('Packet', function() { const msg = new openpgp.PacketList(); msg.push(enc); - const msg2 = new openpgp.PacketList(); - - const randomBytesStub = stub(nodeCrypto, 'randomBytes'); - randomBytesStub.returns(iv); try { + const msg2 = new openpgp.PacketList(); + + const randomBytesStub = sinon.stub(); + randomBytesStub.onCall(0).returns(iv); + // no more random calls expected, so we throw as a reminder to update behavior if needed in the future + randomBytesStub.throws('random mock behavior not defined'); + mockCryptoRandomGenerator(randomBytesStub); + await enc.encrypt(algo, key, { ...openpgp.config, aeadChunkSizeByte: 14 }); const data = msg.write(); expect(await stream.readToEnd(stream.clone(data))).to.deep.equal(packetBytes); @@ -275,10 +326,107 @@ module.exports = () => describe('Packet', function() { await msg2[0].decrypt(algo, key); expect(await stream.readToEnd(msg2[0].packets[0].data)).to.deep.equal(literal.data); } finally { - randomBytesStub.restore(); + restoreCryptoRandomGenerator(); } }); + + describe('Sym. encrypted integrity protected packet reading/writing test vector (SEIPDv2)', async function () { + const testVectors = [{ + // from https://datatracker.ietf.org/doc/html/rfc9580#appendix-A.9 + algoLabel: 'EAX', + aeadAlgo: openpgp.enums.aead.eax, + padding: util.hexToUint8Array('ae 5b f0 cd 67 05 50 03 55 81 6c b0 c8 ff'.replace(/\s+/g, '')), + sessionKey: util.hexToUint8Array('38 81 ba fe 98 54 12 45 9b 86 c3 6f 98 cb 9a 5e'.replace(/\s+/g, '')), + salt: util.hexToUint8Array('9f f9 0e 3b 32 19 64 f3 a4 29 13 c8 dc c6 61 93 25 01 52 27 ef b7 ea ea a4 9f 04 c2 e6 74 17 5d'.replace(/\s+/g, '')), + encryptedPacketBytes: util.hexToUint8Array(` + d2 69 02 07 01 06 + 9f f9 0e 3b 32 19 64 f3 a4 29 13 c8 dc c6 61 93 + 25 01 52 27 ef b7 ea ea a4 9f 04 c2 e6 74 17 5d + 4a 3d 22 6e d6 af cb 9c a9 ac 12 2c 14 70 e1 1c + 63 d4 c0 ab 24 1c 6a 93 8a d4 8b f9 9a 5a 99 b9 + 0b ba 83 25 de + 61 04 75 40 25 8a b7 95 9a 95 ad 05 1d da 96 eb + 15 43 1d fe f5 f5 e2 25 5c a7 82 61 54 6e 33 9a + `.replace(/\s+/g, '')) + }, + { + // from https://datatracker.ietf.org/doc/html/rfc9580#appendix-A.10 + algoLabel: 'OCB', + aeadAlgo: openpgp.enums.aead.ocb, + padding: util.hexToUint8Array('ae 6a a1 64 9b 56 aa 83 5b 26 13 90 2b d2'.replace(/\s+/g, '')), + sessionKey: util.hexToUint8Array('28 e7 9a b8 23 97 d3 c6 3d e2 4a c2 17 d7 b7 91'.replace(/\s+/g, '')), + salt: util.hexToUint8Array('20 a6 61 f7 31 fc 9a 30 32 b5 62 33 26 02 7e 3a 5d 8d b5 74 8e be ff 0b 0c 59 10 d0 9e cd d6 41'.replace(/\s+/g, '')), + encryptedPacketBytes: util.hexToUint8Array(` + d2 69 02 07 02 06 + 20 a6 61 f7 31 fc 9a 30 32 b5 62 33 26 02 7e 3a + 5d 8d b5 74 8e be ff 0b 0c 59 10 d0 9e cd d6 41 + ff 9f d3 85 62 75 80 35 bc 49 75 4c e1 bf 3f ff + a7 da d0 a3 b8 10 4f 51 33 cf 42 a4 10 0a 83 ee + f4 ca 1b 48 01 + a8 84 6b f4 2b cd a7 c8 ce 9d 65 e2 12 f3 01 cb + cd 98 fd ca de 69 4a 87 7a d4 24 73 23 f6 e8 57 + `.replace(/\s+/g, '')) + }, + { + // from https://datatracker.ietf.org/doc/html/rfc9580#appendix-A.11 + algoLabel: 'GCM', + aeadAlgo: openpgp.enums.aead.gcm, + padding: util.hexToUint8Array('1c e2 26 9a 9e dd ef 81 03 21 72 b7 ed 7c'.replace(/\s+/g, '')), + sessionKey: util.hexToUint8Array('19 36 fc 85 68 98 02 74 bb 90 0d 83 19 36 0c 77'.replace(/\s+/g, '')), + salt: util.hexToUint8Array('fc b9 44 90 bc b9 8b bd c9 d1 06 c6 09 02 66 94 0f 72 e8 9e dc 21 b5 59 6b 15 76 b1 01 ed 0f 9f'.replace(/\s+/g, '')), + encryptedPacketBytes: util.hexToUint8Array(` + d2 69 02 07 03 06 + fc b9 44 90 bc b9 8b bd c9 d1 06 c6 09 02 66 94 + 0f 72 e8 9e dc 21 b5 59 6b 15 76 b1 01 ed 0f 9f + fc 6f c6 d6 5b bf d2 4d cd 07 90 96 6e 6d 1e 85 + a3 00 53 78 4c b1 d8 b6 a0 69 9e f1 21 55 a7 b2 + ad 62 58 53 1b + 57 65 1f d7 77 79 12 fa 95 e3 5d 9b 40 21 6f 69 + a4 c2 48 db 28 ff 43 31 f1 63 29 07 39 9e 6f f9 + `.replace(/\s+/g, '')) + }]; + + testVectors.forEach(testVector => it(testVector.algoLabel, async function () { + const symmetricAlgo = openpgp.enums.symmetric.aes128; + + const { aeadAlgo: expectedAEADAlgo, padding, salt, sessionKey, encryptedPacketBytes: expectedEncryptedPacketBytes } = testVector; + + try { + const randomBytesStub = sinon.stub(); + randomBytesStub.onCall(0).returns(padding); + randomBytesStub.onCall(1).returns(salt); + // no more random calls expected, so we throw as a reminder to update behavior if needed in the future + randomBytesStub.onCall(2).throws('random mock behavior not defined'); + mockCryptoRandomGenerator(randomBytesStub); + + const literal = new openpgp.LiteralDataPacket(0); + literal.setBytes(util.stringToUint8Array('Hello, world!'), openpgp.enums.literal.binary); + literal.filename = ''; + const pad = new openpgp.PaddingPacket(); + await pad.createPadding(14); + const seipdv2 = openpgp.SymEncryptedIntegrityProtectedDataPacket.fromObject({ version: 2, aeadAlgorithm: expectedAEADAlgo }); + seipdv2.packets = new openpgp.PacketList(); + seipdv2.packets.push(literal); + seipdv2.packets.push(pad); + const msg = new openpgp.PacketList(); + msg.push(seipdv2); + + await seipdv2.encrypt(symmetricAlgo, sessionKey, { ...openpgp.config, aeadChunkSizeByte: 6 }); + const data = msg.write(); + expect(await stream.readToEnd(stream.clone(data))).to.deep.equal(expectedEncryptedPacketBytes); + + const msg2 = new openpgp.PacketList(); + await msg2.read(data, allAllowedPackets); + expect(msg2[0].cipherAlgorithm).to.equal(openpgp.enums.symmetric.aes128); + await msg2[0].decrypt(symmetricAlgo, sessionKey); + expect(await stream.readToEnd(msg2[0].packets[0].data)).to.deep.equal(literal.data); + } finally { + restoreCryptoRandomGenerator(); + } + })); + }); + it('Sym. encrypted session key with a compressed packet', async function() { const msg = '-----BEGIN PGP MESSAGE-----\n' + @@ -305,31 +453,64 @@ module.exports = () => describe('Packet', function() { }); }); - it('Public key encrypted symmetric key packet', function() { - const rsa = openpgp.enums.publicKey.rsaEncryptSign; - const keySize = 1024; + describe('Public key encrypted symmetric key packet - roundtrip', () => { + const testData = [{ + algoLabel: 'RSA', + publicKeyAlgorithm: openpgp.enums.publicKey.rsaEncryptSign, + paramsPromise: crypto.generateParams(openpgp.enums.publicKey.rsaEncryptSign, 1024, 65537) + }, + { + algoLabel: 'ECDH NIST P-256', + publicKeyAlgorithm: openpgp.enums.publicKey.ecdh, + paramsPromise: crypto.generateParams(openpgp.enums.publicKey.ecdh, null, openpgp.enums.curve.nistP256) + }, + { + algoLabel: 'ECDH x25519Legacy', + publicKeyAlgorithm: openpgp.enums.publicKey.ecdh, + paramsPromise: crypto.generateParams(openpgp.enums.publicKey.ecdh, null, openpgp.enums.curve.curve25519Legacy) + }, + { + algoLabel: 'x25519', + publicKeyAlgorithm: openpgp.enums.publicKey.x25519, + paramsPromise: crypto.generateParams(openpgp.enums.publicKey.x25519) + }]; - return crypto.generateParams(rsa, keySize, 65537).then(function({ publicParams, privateParams }) { - const enc = new openpgp.PublicKeyEncryptedSessionKeyPacket(); - const msg = new openpgp.PacketList(); - const msg2 = new openpgp.PacketList(); + function testRoundtrip({ v6 }) { + testData.forEach(({ algoLabel, publicKeyAlgorithm, paramsPromise }) => { + it(`${algoLabel} (PKESK ${v6 ? 'v6' : 'v3'})`, async () => { + const { publicParams, privateParams } = await paramsPromise; + // cannot use the `openpgp` exported values, since the different context gives issues when internally + // evaluating the `OID` instanceof of `publicParams.oid`, as part of `pkesk.encrypt` and `decrypt` + const pkesk = new packet.PublicKeyEncryptedSessionKeyPacket(); + pkesk.version = v6 ? 6 : 3; + const msg = new packet.PacketList(); + const msg2 = new packet.PacketList(); - enc.sessionKey = new Uint8Array([1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2]); - enc.publicKeyAlgorithm = openpgp.enums.publicKey.rsaEncryptSign; - enc.sessionKeyAlgorithm = openpgp.enums.symmetric.aes256; - enc.publicKeyID.bytes = '12345678'; - return enc.encrypt({ publicParams, getFingerprintBytes() {} }).then(async () => { + const privateKey = { + algorithm: publicKeyAlgorithm, + publicParams, + privateParams, + getFingerprintBytes: () => new Uint8Array(64) + }; + pkesk.sessionKey = new Uint8Array([1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2]); + pkesk.publicKeyAlgorithm = publicKeyAlgorithm; + pkesk.sessionKeyAlgorithm = openpgp.enums.symmetric.aes256; + pkesk.publicKeyID.bytes = '12345678'; + await pkesk.encrypt({ publicParams: privateKey.publicParams, getFingerprintBytes: privateKey.getFingerprintBytes }); - msg.push(enc); - await msg2.read(msg.write(), allAllowedPackets); + msg.push(pkesk); + const allAllowedPackets = util.constructAllowedPackets([...Object.values(packet).filter(packetClass => !!packetClass.tag)]); + await msg2.read(msg.write(), allAllowedPackets); - const privateKey = { algorithm: openpgp.enums.publicKey.rsaEncryptSign, publicParams, privateParams, getFingerprintBytes() {} }; - return msg2[0].decrypt(privateKey).then(() => { - expect(stringify(msg2[0].sessionKey)).to.equal(stringify(enc.sessionKey)); - expect(msg2[0].sessionKeyAlgorithm).to.equal(enc.sessionKeyAlgorithm); + await msg2[0].decrypt(privateKey); + expect(msg2[0].sessionKey).to.deep.equal(pkesk.sessionKey); + expect(msg2[0].sessionKeyAlgorithm).to.equal(v6 ? null : pkesk.sessionKeyAlgorithm); }); }); - }); + } + + testRoundtrip({ v6: false }); + testRoundtrip({ v6: true }); }); it('Secret key packet (reading, unencrypted)', async function() { @@ -360,6 +541,7 @@ module.exports = () => describe('Packet', function() { key = key[0]; const enc = new openpgp.PublicKeyEncryptedSessionKeyPacket(); + enc.version = 3; const secret = new Uint8Array([1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2]); enc.sessionKey = secret; @@ -451,6 +633,7 @@ module.exports = () => describe('Packet', function() { literal.setText(testText); const skesk = new openpgp.SymEncryptedSessionKeyPacket(); const seip = new openpgp.SymEncryptedIntegrityProtectedDataPacket(); + seip.version = 1; seip.packets = new openpgp.PacketList(); seip.packets.push(literal); const msg = new openpgp.PacketList(); @@ -489,6 +672,7 @@ module.exports = () => describe('Packet', function() { const literal = new openpgp.LiteralDataPacket(); literal.setText(testText); const skesk = new openpgp.SymEncryptedSessionKeyPacket(); + skesk.version = 5; const aeadEnc = new openpgp.AEADEncryptedDataPacket(); aeadEnc.packets = new openpgp.PacketList(); aeadEnc.packets.push(literal); @@ -515,69 +699,33 @@ module.exports = () => describe('Packet', function() { } }); - it('Sym. encrypted session key reading/writing test vector (EAX, AEAD)', async function() { - // From https://gitlab.com/openpgp-wg/rfc4880bis/blob/00b20923/back.mkd#sample-aead-eax-encryption-and-decryption - - const nodeCrypto = util.getNodeCrypto(); - if (!nodeCrypto) return; - + it('Sym. encrypted session key reading/writing (SEIPDv2)', async function() { const aeadProtectVal = openpgp.config.aeadProtect; - const aeadChunkSizeByteVal = openpgp.config.aeadChunkSizeByte; - const s2kIterationCountByteVal = openpgp.config.s2kIterationCountByte; openpgp.config.aeadProtect = true; - openpgp.config.aeadChunkSizeByte = 14; - openpgp.config.s2kIterationCountByte = 0x90; - - const salt = util.hexToUint8Array('cd5a9f70fbe0bc65'); - const sessionKey = util.hexToUint8Array('86 f1 ef b8 69 52 32 9f 24 ac d3 bf d0 e5 34 6d'.replace(/\s+/g, '')); - const sessionIV = util.hexToUint8Array('bc 66 9e 34 e5 00 dc ae dc 5b 32 aa 2d ab 02 35'.replace(/\s+/g, '')); - const dataIV = util.hexToUint8Array('b7 32 37 9f 73 c4 92 8d e2 5f ac fe 65 17 ec 10'.replace(/\s+/g, '')); - - const randomBytesStub = stub(nodeCrypto, 'randomBytes'); - randomBytesStub.onCall(0).returns(salt); - randomBytesStub.onCall(1).returns(sessionKey); - randomBytesStub.onCall(2).returns(sessionIV); - randomBytesStub.onCall(3).returns(dataIV); - - const packetBytes = util.hexToUint8Array(` - c3 3e 05 07 01 03 08 cd 5a 9f 70 fb e0 bc 65 90 - bc 66 9e 34 e5 00 dc ae dc 5b 32 aa 2d ab 02 35 - 9d ee 19 d0 7c 34 46 c4 31 2a 34 ae 19 67 a2 fb - 7e 92 8e a5 b4 fa 80 12 bd 45 6d 17 38 c6 3c 36 - - d4 4a 01 07 01 0e b7 32 37 9f 73 c4 92 8d e2 5f - ac fe 65 17 ec 10 5d c1 1a 81 dc 0c b8 a2 f6 f3 - d9 00 16 38 4a 56 fc 82 1a e1 1a e8 db cb 49 86 - 26 55 de a8 8d 06 a8 14 86 80 1b 0f f3 87 bd 2e - ab 01 3d e1 25 95 86 90 6e ab 24 76 - `.replace(/\s+/g, '')); try { - const passphrase = 'password'; - const algo = openpgp.enums.symmetric.aes128; + const passphrase = 'hello'; + const algo = openpgp.enums.symmetric.aes256; + const testText = input.createSomeMessage(); - const literal = new openpgp.LiteralDataPacket(0); - literal.setBytes(util.stringToUint8Array('Hello, world!\n'), openpgp.enums.literal.binary); - literal.filename = ''; + const literal = new openpgp.LiteralDataPacket(); + literal.setText(testText); const skesk = new openpgp.SymEncryptedSessionKeyPacket(); - skesk.sessionKeyAlgorithm = algo; - const encData = new openpgp.AEADEncryptedDataPacket(); - encData.packets = new openpgp.PacketList(); - encData.packets.push(literal); + const aeadEnc = new openpgp.AEADEncryptedDataPacket(); + aeadEnc.packets = new openpgp.PacketList(); + aeadEnc.packets.push(literal); const msg = new openpgp.PacketList(); msg.push(skesk); - msg.push(encData); + msg.push(aeadEnc); + skesk.sessionKeyAlgorithm = algo; await skesk.encrypt(passphrase, openpgp.config); const key = skesk.sessionKey; - await encData.encrypt(algo, key, undefined, openpgp.config); - - const data = msg.write(); - expect(await stream.readToEnd(stream.clone(data))).to.deep.equal(packetBytes); + await aeadEnc.encrypt(algo, key, undefined, openpgp.config); const msg2 = new openpgp.PacketList(); - await msg2.read(data, allAllowedPackets); + await msg2.read(msg.write(), allAllowedPackets); await msg2[0].decrypt(passphrase); const key2 = msg2[0].sessionKey; @@ -586,88 +734,197 @@ module.exports = () => describe('Packet', function() { expect(await stringify(msg2[1].packets[0].data)).to.equal(stringify(literal.data)); } finally { openpgp.config.aeadProtect = aeadProtectVal; - openpgp.config.aeadChunkSizeByte = aeadChunkSizeByteVal; - openpgp.config.s2kIterationCountByte = s2kIterationCountByteVal; - randomBytesStub.restore(); } }); - it('Sym. encrypted session key reading/writing test vector (AEAD, OCB)', async function() { - // From https://gitlab.com/openpgp-wg/rfc4880bis/blob/00b20923/back.mkd#sample-aead-ocb-encryption-and-decryption + describe('Sym. encrypted session key reading/writing test vector (SKESK with AEADP)', () => { + const testVectors = [{ + // From https://gitlab.com/openpgp-wg/rfc4880bis/blob/00b20923/back.mkd#sample-aead-eax-encryption-and-decryption + algoLabel: 'EAX', + aeadAlgo: openpgp.enums.aead.eax, + salt: util.hexToUint8Array('cd5a9f70fbe0bc65'), + sessionKey: util.hexToUint8Array('86 f1 ef b8 69 52 32 9f 24 ac d3 bf d0 e5 34 6d'.replace(/\s+/g, '')), + sessionIV: util.hexToUint8Array('bc 66 9e 34 e5 00 dc ae dc 5b 32 aa 2d ab 02 35'.replace(/\s+/g, '')), + dataIV: util.hexToUint8Array('b7 32 37 9f 73 c4 92 8d e2 5f ac fe 65 17 ec 10'.replace(/\s+/g, '')), + packetBytes: util.hexToUint8Array(` + c3 3e 05 07 01 03 08 cd 5a 9f 70 fb e0 bc 65 90 + bc 66 9e 34 e5 00 dc ae dc 5b 32 aa 2d ab 02 35 + 9d ee 19 d0 7c 34 46 c4 31 2a 34 ae 19 67 a2 fb + 7e 92 8e a5 b4 fa 80 12 bd 45 6d 17 38 c6 3c 36 - const nodeCrypto = util.getNodeCrypto(); - if (!nodeCrypto) return; + d4 4a 01 07 01 0e b7 32 37 9f 73 c4 92 8d e2 5f + ac fe 65 17 ec 10 5d c1 1a 81 dc 0c b8 a2 f6 f3 + d9 00 16 38 4a 56 fc 82 1a e1 1a e8 db cb 49 86 + 26 55 de a8 8d 06 a8 14 86 80 1b 0f f3 87 bd 2e + ab 01 3d e1 25 95 86 90 6e ab 24 76 + `.replace(/\s+/g, '')) + }, + { + algoLabel: 'OCB', + aeadAlgo: openpgp.enums.aead.ocb, + salt: util.hexToUint8Array('9f0b7da3e5ea6477'), + sessionKey: util.hexToUint8Array('d1 f0 1b a3 0e 13 0a a7 d2 58 2c 16 e0 50 ae 44'.replace(/\s+/g, '')), + sessionIV: util.hexToUint8Array('99 e3 26 e5 40 0a 90 93 6c ef b4 e8 eb a0 8c'.replace(/\s+/g, '')), + dataIV: util.hexToUint8Array('5e d2 bc 1e 47 0a be 8f 1d 64 4c 7a 6c 8a 56'.replace(/\s+/g, '')), + packetBytes: util.hexToUint8Array(` + c3 3d 05 07 02 03 08 9f 0b 7d a3 e5 ea 64 77 90 + 99 e3 26 e5 40 0a 90 93 6c ef b4 e8 eb a0 8c 67 + 73 71 6d 1f 27 14 54 0a 38 fc ac 52 99 49 da c5 + 29 d3 de 31 e1 5b 4a eb 72 9e 33 00 33 db ed + + d4 49 01 07 02 0e 5e d2 bc 1e 47 0a be 8f 1d 64 + 4c 7a 6c 8a 56 7b 0f 77 01 19 66 11 a1 54 ba 9c + 25 74 cd 05 62 84 a8 ef 68 03 5c 62 3d 93 cc 70 + 8a 43 21 1b b6 ea f2 b2 7f 7c 18 d5 71 bc d8 3b + 20 ad d3 a0 8b 73 af 15 b9 a0 98 + `.replace(/\s+/g, '')) + }]; - const aeadProtectVal = openpgp.config.aeadProtect; - const aeadChunkSizeByteVal = openpgp.config.aeadChunkSizeByte; - const s2kIterationCountByteVal = openpgp.config.s2kIterationCountByte; - openpgp.config.aeadProtect = true; - openpgp.config.aeadChunkSizeByte = 14; - openpgp.config.s2kIterationCountByte = 0x90; + testVectors.forEach(testVector => it(testVector.algoLabel, async function () { + const { aeadAlgo, salt, sessionKey, sessionIV, dataIV, packetBytes: expectedEncryptedPacketBytes } = testVector; - const salt = util.hexToUint8Array('9f0b7da3e5ea6477'); - const sessionKey = util.hexToUint8Array('d1 f0 1b a3 0e 13 0a a7 d2 58 2c 16 e0 50 ae 44'.replace(/\s+/g, '')); - const sessionIV = util.hexToUint8Array('99 e3 26 e5 40 0a 90 93 6c ef b4 e8 eb a0 8c'.replace(/\s+/g, '')); - const dataIV = util.hexToUint8Array('5e d2 bc 1e 47 0a be 8f 1d 64 4c 7a 6c 8a 56'.replace(/\s+/g, '')); + try { + const passphrase = 'password'; + const algo = openpgp.enums.symmetric.aes128; - const randomBytesStub = stub(nodeCrypto, 'randomBytes'); - randomBytesStub.onCall(0).returns(salt); - randomBytesStub.onCall(1).returns(sessionKey); - randomBytesStub.onCall(2).returns(sessionIV); - randomBytesStub.onCall(3).returns(dataIV); + const randomBytesStub = sinon.stub(); + randomBytesStub.onCall(0).returns(salt); + randomBytesStub.onCall(1).returns(sessionKey); + randomBytesStub.onCall(2).returns(sessionIV); + randomBytesStub.onCall(3).returns(dataIV); + // no more random calls expected, so we throw as a reminder to update behavior if needed in the future + randomBytesStub.onCall(4).throws('random mock behavior not defined'); + mockCryptoRandomGenerator(randomBytesStub); - const packetBytes = util.hexToUint8Array(` - c3 3d 05 07 02 03 08 9f 0b 7d a3 e5 ea 64 77 90 - 99 e3 26 e5 40 0a 90 93 6c ef b4 e8 eb a0 8c 67 - 73 71 6d 1f 27 14 54 0a 38 fc ac 52 99 49 da c5 - 29 d3 de 31 e1 5b 4a eb 72 9e 33 00 33 db ed + const literal = new openpgp.LiteralDataPacket(0); + literal.setBytes(util.stringToUint8Array('Hello, world!\n'), openpgp.enums.literal.binary); + literal.filename = ''; + const skesk = new openpgp.SymEncryptedSessionKeyPacket(); + skesk.version = 5; + skesk.sessionKeyAlgorithm = algo; + const encData = new openpgp.AEADEncryptedDataPacket(); + encData.packets = new openpgp.PacketList(); + encData.packets.push(literal); + encData.aeadAlgorithm = skesk.aeadAlgorithm = aeadAlgo; + const msg = new openpgp.PacketList(); + msg.push(skesk); + msg.push(encData); - d4 49 01 07 02 0e 5e d2 bc 1e 47 0a be 8f 1d 64 - 4c 7a 6c 8a 56 7b 0f 77 01 19 66 11 a1 54 ba 9c - 25 74 cd 05 62 84 a8 ef 68 03 5c 62 3d 93 cc 70 - 8a 43 21 1b b6 ea f2 b2 7f 7c 18 d5 71 bc d8 3b - 20 ad d3 a0 8b 73 af 15 b9 a0 98 - `.replace(/\s+/g, '')); + await skesk.encrypt(passphrase, { ...openpgp.config, s2kIterationCountByte: 0x90 }); + + const key = skesk.sessionKey; + await encData.encrypt(algo, key, { ...openpgp.config, aeadChunkSizeByte: 14 }); + + const data = msg.write(); + expect(await stream.readToEnd(stream.clone(data))).to.deep.equal(expectedEncryptedPacketBytes); + + const msg2 = new openpgp.PacketList(); + await msg2.read(data, allAllowedPackets); + + await msg2[0].decrypt(passphrase); + const key2 = msg2[0].sessionKey; + await msg2[1].decrypt(msg2[0].sessionKeyAlgorithm, key2); + + expect(await stringify(msg2[1].packets[0].data)).to.equal(stringify(literal.data)); + } finally { + restoreCryptoRandomGenerator(); + } + })); + }); + + describe('Sym. encrypted session key reading/writing test vector (SKESK v6)', async function () { + const testVectors = [{ + // from https://datatracker.ietf.org/doc/html/rfc9580#appendix-A.9.1 + algoLabel: 'EAX', + aeadAlgo: openpgp.enums.aead.eax, + s2kSalt: util.hexToUint8Array('a5 ae 57 9d 1f c5 d8 2b'.replace(/\s+/g, '')), + sessionKey: util.hexToUint8Array('38 81 ba fe 98 54 12 45 9b 86 c3 6f 98 cb 9a 5e'.replace(/\s+/g, '')), + sessionIV: util.hexToUint8Array('69 22 4f 91 99 93 b3 50 6f a3 b5 9a 6a 73 cf f8'.replace(/\s+/g, '')), + encryptedPacketBytes: util.hexToUint8Array(` + c3 40 06 1e 07 01 0b 03 + 08 a5 ae 57 9d 1f c5 d8 + 2b ff 69 22 4f 91 99 93 + b3 50 6f a3 b5 9a 6a 73 + cf f8 c5 ef c5 f4 1c 57 + fb 54 e1 c2 26 81 5d 78 + 28 f5 f9 2c 45 4e b6 5e + be 00 ab 59 86 c6 8e 6e + 7c 55`.replace(/\s+/g, '')) + }, + { + algoLabel: 'OCB', + aeadAlgo: openpgp.enums.aead.ocb, + padding: util.hexToUint8Array('ae 6a a1 64 9b 56 aa 83 5b 26 13 90 2b d2'.replace(/\s+/g, '')), + s2kSalt: util.hexToUint8Array('56 a2 98 d2 f5 e3 64 53'.replace(/\s+/g, '')), + sessionKey: util.hexToUint8Array('28 e7 9a b8 23 97 d3 c6 3d e2 4a c2 17 d7 b7 91'.replace(/\s+/g, '')), + sessionIV: util.hexToUint8Array('cf cc 5c 11 66 4e db 9d b4 25 90 d7 dc 46 b0'.replace(/\s+/g, '')), + encryptedPacketBytes: util.hexToUint8Array(` + c3 3f 06 1d 07 02 0b 03 + 08 56 a2 98 d2 f5 e3 64 + 53 ff cf cc 5c 11 66 4e + db 9d b4 25 90 d7 dc 46 + b0 72 41 b6 12 c3 81 2c + ff fb ea 00 f2 34 7b 25 + 64 11 23 f8 87 ae 60 d4 + fd 61 4e 08 37 d8 19 d3 + 6c`.replace(/\s+/g, '')) + }, + { + algoLabel: 'GCM', + aeadAlgo: openpgp.enums.aead.gcm, + s2kSalt: util.hexToUint8Array('e9 d3 97 85 b2 07 00 08'.replace(/\s+/g, '')), + sessionKey: util.hexToUint8Array('19 36 fc 85 68 98 02 74 bb 90 0d 83 19 36 0c 77'.replace(/\s+/g, '')), + sessionIV: util.hexToUint8Array('b4 2e 7c 48 3e f4 88 44 57 cb 37 26'.replace(/\s+/g, '')), + encryptedPacketBytes: util.hexToUint8Array(` + c3 3c 06 1a 07 03 0b 03 + 08 e9 d3 97 85 b2 07 00 + 08 ff b4 2e 7c 48 3e f4 + 88 44 57 cb 37 26 b9 b3 + db 9f f7 76 e5 f4 d9 a4 + 09 52 e2 44 72 98 85 1a + bf ff 75 26 df 2d d5 54 + 41 75 79 a7 79 9f`.replace(/\s+/g, '')) + }]; + + testVectors.forEach(testVector => it(testVector.algoLabel, async function () { + const { aeadAlgo: expectedAEADAlgo, s2kSalt, sessionKey, sessionIV, encryptedPacketBytes: expectedEncryptedPacketBytes } = testVector; - try { const passphrase = 'password'; - const algo = openpgp.enums.symmetric.aes128; + const symmetricAlgo = openpgp.enums.symmetric.aes128; - const literal = new openpgp.LiteralDataPacket(0); - literal.setBytes(util.stringToUint8Array('Hello, world!\n'), openpgp.enums.literal.binary); - literal.filename = ''; - const skesk = new openpgp.SymEncryptedSessionKeyPacket(); - skesk.sessionKeyAlgorithm = algo; - const enc = new openpgp.AEADEncryptedDataPacket(); - enc.packets = new openpgp.PacketList(); - enc.packets.push(literal); - enc.aeadAlgorithm = skesk.aeadAlgorithm = openpgp.enums.aead.ocb; - const msg = new openpgp.PacketList(); - msg.push(skesk); - msg.push(enc); + try { + const randomBytesStub = sinon.stub(); + randomBytesStub.onCall(0).returns(s2kSalt); + randomBytesStub.onCall(1).returns(sessionKey); + randomBytesStub.onCall(2).returns(sessionIV); + // no more random calls expected, so we throw as a reminder to update behavior if needed in the future + randomBytesStub.onCall(3).throws('random mock behavior not defined'); + mockCryptoRandomGenerator(randomBytesStub); - await skesk.encrypt(passphrase, openpgp.config); + const skesk = new openpgp.SymEncryptedSessionKeyPacket(); + skesk.version = 6; + skesk.sessionKeyAlgorithm = symmetricAlgo; + skesk.aeadAlgorithm = expectedAEADAlgo; + const msg = new openpgp.PacketList(); + msg.push(skesk); - const key = skesk.sessionKey; - await enc.encrypt(algo, key, undefined, openpgp.config); + await skesk.encrypt(passphrase, { ...openpgp.config, aeadChunkSizeByte: 6, s2kIterationCountByte: 255 }); - const data = msg.write(); - expect(await stream.readToEnd(stream.clone(data))).to.deep.equal(packetBytes); + const serialized = msg.write(); + expect(await stream.readToEnd(stream.clone(serialized))).to.deep.equal(expectedEncryptedPacketBytes); - const msg2 = new openpgp.PacketList(); - await msg2.read(data, allAllowedPackets); + const msg2 = new openpgp.PacketList(); + await msg2.read(serialized, allAllowedPackets); - await msg2[0].decrypt(passphrase); - const key2 = msg2[0].sessionKey; - await msg2[1].decrypt(msg2[0].sessionKeyAlgorithm, key2); - - expect(await stringify(msg2[1].packets[0].data)).to.equal(stringify(literal.data)); - } finally { - openpgp.config.aeadProtect = aeadProtectVal; - openpgp.config.aeadChunkSizeByte = aeadChunkSizeByteVal; - openpgp.config.s2kIterationCountByte = s2kIterationCountByteVal; - randomBytesStub.restore(); - } + await msg2[0].decrypt(passphrase); + const decryptedSessionKey = msg2[0].sessionKey; + const decryptedSessionKeyAlgo = msg2[0].sessionKeyAlgorithm; + expect(decryptedSessionKey).to.deep.equal(sessionKey); + expect(decryptedSessionKeyAlgo).to.be.null; + } finally { + restoreCryptoRandomGenerator(); + } + })); }); it('Secret key encryption/decryption test', async function() { @@ -853,8 +1110,36 @@ V+HOQJQxXJkVRYa3QrFUehiMzTeqqMdgC6ZqJy7+ }); it('Writing of unencrypted v5 secret key packet', async function() { - const originalV5KeysSetting = openpgp.config.v5Keys; - openpgp.config.v5Keys = true; + const packet = new openpgp.SecretKeyPacket(); + packet.version = 5; + packet.privateParams = { key: new Uint8Array([1, 2, 3]) }; + packet.publicParams = { pubKey: new Uint8Array([4, 5, 6]) }; + packet.algorithm = openpgp.enums.publicKey.rsaSign; + packet.isEncrypted = false; + packet.s2kUsage = 0; + + const written = packet.write(); + expect(written.length).to.equal(28); + + /* The serialized length of private data */ + expect(written[17]).to.equal(0); + expect(written[18]).to.equal(0); + expect(written[19]).to.equal(0); + expect(written[20]).to.equal(5); + + /** + * The private data + * + * The 2 bytes missing here are the length prefix of the MPI + */ + expect(written[23]).to.equal(1); + expect(written[24]).to.equal(2); + expect(written[25]).to.equal(3); + }); + + it('Writing of unencrypted v6 secret key packet', async function() { + const originalv6KeysSetting = openpgp.config.v6Keys; + openpgp.config.v6Keys = true; try { const packet = new openpgp.SecretKeyPacket(); @@ -866,24 +1151,18 @@ V+HOQJQxXJkVRYa3QrFUehiMzTeqqMdgC6ZqJy7+ packet.s2kUsage = 0; const written = packet.write(); - expect(written.length).to.equal(28); - - /* The serialized length of private data */ - expect(written[17]).to.equal(0); - expect(written[18]).to.equal(0); - expect(written[19]).to.equal(0); - expect(written[20]).to.equal(5); + expect(written.length).to.equal(21); /** * The private data * * The 2 bytes missing here are the length prefix of the MPI */ - expect(written[23]).to.equal(1); - expect(written[24]).to.equal(2); - expect(written[25]).to.equal(3); + expect(written[18]).to.equal(1); + expect(written[19]).to.equal(2); + expect(written[20]).to.equal(3); } finally { - openpgp.config.v5Keys = originalV5KeysSetting; + openpgp.config.v6Keys = originalv6KeysSetting; } }); @@ -927,7 +1206,7 @@ V+HOQJQxXJkVRYa3QrFUehiMzTeqqMdgC6ZqJy7+ signature.publicKeyAlgorithm = openpgp.enums.publicKey.rsaSign; signature.signatureType = openpgp.enums.signature.text; - return signature.sign(key, literal).then(async () => { + return signature.sign(key, literal, undefined, undefined, openpgp.config).then(async () => { signed.push(literal); signed.push(signature); @@ -1042,7 +1321,21 @@ kePFjAnu9cpynKXu3usf8+FuBw2zLsg1Id1n7ttxoAte416KjBN9lFBt8mcu await expect( openpgp.PacketList.fromBinary(binaryMessage, allAllowedPackets, { ...openpgp.config, ignoreUnsupportedPackets: false }) - ).to.be.rejectedWith(/Unknown S2K type/); + ).to.be.rejectedWith(/Unsupported S2K type/); + }); + + it('Throws on critical packet even with tolerant mode enabled', async function() { + const unknownPacketTag39 = util.hexToUint8Array('e70a750064bf943d6e756c6c'); // critical tag + + await expect(openpgp.PacketList.fromBinary(unknownPacketTag39, allAllowedPackets, { ...openpgp.config, ignoreUnsupportedPackets: false, ignoreMalformedPackets: false })).to.be.rejectedWith(/Unknown packet type/); + await expect(openpgp.PacketList.fromBinary(unknownPacketTag39, allAllowedPackets, { ...openpgp.config, ignoreUnsupportedPackets: true, ignoreMalformedPackets: true })).to.be.rejectedWith(/Unknown packet type/); + }); + + it('Ignores non-critical packet even with tolerant mode disabled', async function() { + const unknownPacketTag63 = util.hexToUint8Array('ff0a750064bf943d6e756c6c'); // non-critical tag + + await expect(openpgp.PacketList.fromBinary(unknownPacketTag63, allAllowedPackets, { ...openpgp.config, ignoreUnsupportedPackets: false, ignoreMalformedPackets: false })).to.eventually.have.length(0); + await expect(openpgp.PacketList.fromBinary(unknownPacketTag63, allAllowedPackets, { ...openpgp.config, ignoreUnsupportedPackets: true, ignoreMalformedPackets: true })).to.eventually.have.length(0); }); it('Throws on disallowed packet even with tolerant mode enabled', async function() { @@ -1080,4 +1373,60 @@ kePFjAnu9cpynKXu3usf8+FuBw2zLsg1Id1n7ttxoAte416KjBN9lFBt8mcu expect(otherPackets[0].constructor.tag).to.equal(openpgp.enums.packet.userID); }); }); + + describe('UserID', () => { + it('parse conventional userID', () => { + const userID = 'Mr. Ed, the Talking Horse '; + + const packet = new openpgp.UserIDPacket(); + packet.read(new TextEncoder().encode(userID)); + expect(packet.comment).to.equal(''); + expect(packet.email).to.equal('ed@example.org'); + expect(packet.userID).to.equal(userID); + }); + + it('parse userID with comment', () => { + const userID = 'Alice Jones (the Great) '; + + const packet = new openpgp.UserIDPacket(); + packet.read(new TextEncoder().encode(userID)); + expect(packet.name).to.equal('Alice Jones'); + expect(packet.comment).to.equal('the Great'); + expect(packet.email).to.equal('alice@example.org'); + expect(packet.userID).to.equal(userID); + }); + + it('parse userID with unbracketed email address', () => { + const userID = 'alice@example.org'; + + const packet = new openpgp.UserIDPacket(); + packet.read(new TextEncoder().encode(userID)); + expect(packet.name).to.equal(''); + expect(packet.comment).to.equal(''); + expect(packet.email).to.equal('alice@example.org'); + expect(packet.userID).to.equal(userID); + }); + + it('parse userID with whitespace between parts', () => { + const userID = ' A name surrounded by whitespace ( a comment surrounded too ) '; + + const packet = new openpgp.UserIDPacket(); + packet.read(new TextEncoder().encode(userID)); + expect(packet.name).to.equal('A name surrounded by whitespace'); + expect(packet.comment).to.equal('a comment surrounded too'); + expect(packet.email).to.equal('ed@example.org'); + expect(packet.userID).to.equal(userID); + }); + + it('store userID without parts if parsing fails', () => { + const userID = 'Name only'; + + const packet = new openpgp.UserIDPacket(); + packet.read(new TextEncoder().encode(userID)); + expect(packet.name).to.equal(''); + expect(packet.comment).to.equal(''); + expect(packet.email).to.equal(''); + expect(packet.userID).to.equal(userID); + }); + }); }); diff --git a/test/general/signature.js b/test/general/signature.js index 25fc119d..648dd890 100644 --- a/test/general/signature.js +++ b/test/general/signature.js @@ -1,14 +1,15 @@ /* eslint-disable max-lines */ -/* globals tryTests: true */ -const stream = require('@openpgp/web-stream-tools'); -const { use: chaiUse, expect } = require('chai'); -chaiUse(require('chai-as-promised')); +/* globals tryTests, loadStreamsPolyfill */ +import * as stream from '@openpgp/web-stream-tools'; +import { use as chaiUse, expect } from 'chai'; +import chaiAsPromised from 'chai-as-promised'; // eslint-disable-line import/newline-after-import +chaiUse(chaiAsPromised); -const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../..'); +import openpgp from '../initOpenpgp.js'; -const util = require('../../src/util'); +import util from '../../src/util.js'; -module.exports = () => describe('Signature', function() { +export default () => describe('Signature', function() { const priv_key_arm1 = ['-----BEGIN PGP PRIVATE KEY BLOCK-----', 'Version: GnuPG v1.4.11 (GNU/Linux)', @@ -709,6 +710,68 @@ hUhMKMuiM3pRwdIyDOItkUWQmjEEw7/XmhgInkXsCw== expect(signature.getSigningKeyIDs().map(x => x.toHex())).to.include(publicKey.getKeyID().toHex()); }); + it('Generates valid one-pass signature packets (v6 keys)', async function () { + const v6PrivateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMAGXKB +exK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJjh3/jAwsJ +BwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6 +2azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2kCcUmKfvBXbAf6rh +RYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaEQsiPlR4zxP/TP7mhfVEe +7XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/ +LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u/tVY6a//1q0NWC1X+yui3O24wpsG +GBsKAAAALAWCY4d/4wKbDCIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6 +2azJAAAAAAQBIKbpGG2dWTX8j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDE +M0g12vYxoWM8Y81W+bHBw805I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUr +k0mXubZvyl4GBg== +-----END PGP PRIVATE KEY BLOCK-----` }); + const v4PrivateKey = await openpgp.readKey({ + armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xVgEZOjwQBYJKwYBBAHaRw8BAQdAIzsYHb7T7NhSFmkWKSk5ItaBYv2HET7u +IFXhGdvOpogAAQDMk8SQlysNkLe7VRwXedX63St1a2V6/dzDG926oPyW3hJ6 +zQ48dGVzdEB0ZXN0Lml0PsKMBBAWCgA+BYJk6PBABAsJBwgJkFMSwQwprjrH +AxUICgQWAAIBAhkBApsDAh4BFiEEwKKS1V/wldBNfwpjUxLBDCmuOscAAONc +AQDU1gi9moPtQzh6rrpKPWZ8nMSiZzLb3LYmpqz9Tn9YFAEAmEC2uHET/Oj6 +VVxvMwvGytr5y0nuc6ZV5D0I2qoMcAHHXQRk6PBAEgorBgEEAZdVAQUBAQdA +xeBFSxzKOtLEQ6mVO2mnxvDiiMKmq7NbOezoWkbeSjgDAQgHAAD/YMvH+eDP +h1RplHOiaAMwhpTRGSGOaC6+pu6AMWiRLWAQ88J4BBgWCAAqBYJk6PBACZBT +EsEMKa46xwKbDBYhBMCiktVf8JXQTX8KY1MSwQwprjrHAAD4igEAzoExR6VX +EY9xxFKtAVdtYOT61d0FZibs0TD0VjbqzdkBAK60jT9jKefpnZ9sGv7bhSRX +2hrIPyCF2f0R5Js3LCML +=xyQ6 +-----END PGP PRIVATE KEY BLOCK-----` + }); + const armoredSignedMessage = await openpgp.sign({ + message: await openpgp.createMessage({ text: 'test' }), + signingKeys: [v6PrivateKey, v4PrivateKey, v6PrivateKey] // get multiple signature packets + }); + const signedMessage = await openpgp.readMessage({ armoredMessage: armoredSignedMessage }); + // read signature packet stream + signedMessage.packets.push(...await stream.readToEnd(signedMessage.packets.stream, _ => _)); + const signature1 = signedMessage.packets[4]; + const signature2 = signedMessage.packets[5]; // v4 sig + const signature3 = signedMessage.packets[6]; + const opsSignature1 = signedMessage.packets[2]; + const opsSignature2 = signedMessage.packets[1]; + const opsSignature3 = signedMessage.packets[0]; + expect(opsSignature1).to.be.instanceOf(openpgp.OnePassSignaturePacket); + expect(signature1).to.be.instanceOf(openpgp.SignaturePacket); + expect(opsSignature2).to.be.instanceOf(openpgp.OnePassSignaturePacket); + expect(signature2).to.be.instanceOf(openpgp.SignaturePacket); + expect(opsSignature3).to.be.instanceOf(openpgp.OnePassSignaturePacket); + expect(signature3).to.be.instanceOf(openpgp.SignaturePacket); + expect(opsSignature1.version).to.equal(6); + expect(opsSignature2.version).to.equal(3); + expect(opsSignature3.version).to.equal(6); + expect(util.uint8ArrayToHex(opsSignature1.issuerFingerprint)).to.equal(v6PrivateKey.getFingerprint()); + expect(util.uint8ArrayToHex(opsSignature3.issuerFingerprint)).to.equal(v6PrivateKey.getFingerprint()); + expect(opsSignature1.salt).to.deep.equal(signature1.salt); + expect(opsSignature2.salt).to.be.null; + expect(opsSignature3.salt).to.deep.equal(signature3.salt); + expect(opsSignature1.salt).to.not.deep.equal(opsSignature3.salt); // sanity check + }); + it('Throws when reading a signature missing the creation time', async function () { const armoredSignature = `-----BEGIN PGP SIGNATURE----- @@ -727,6 +790,31 @@ kCNcH9WI6idSzFjuYegECf+ZA1xOCjS9oLTGbSeT7jNfC8dH5+E92qlBLq4Ctt7k await expect(openpgp.readSignature({ armoredSignature })).to.be.rejectedWith(/Missing signature creation time/); }); + it('Supports parsing v6 signature with unknown hash algo', async function () { + // v6 signatures must check that the salt size is correct. + // but we want to support parsing signatures with unknown hash algos. + // this test checks that the parsing succeeds, while signing/verification fails. + + // key with direct signature of unknown hash algo 99 + const armoredKey = `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xUsGZN8edBsAAAAgdUMlFMFCVKNo7sdUd6FVBos6NNjpUpSdrodk6BfPb/kA ++3buA2+WY2LwyxlX5o07WR2VSn+wuegC3v28yO0tClHCtwYfG2MAAABIBYJk +3x50BAsJCAcHFQ4MCgkICwIWAAIXgAKbAwIeCSIhBpbSe0QWuaCNSSLaePhX +EP3BxQ2VHX3WpW1U6svHvCUiBScJAgcCAAAAACMZIP8aHixoyC9wS3q/TNV/ +IfOQa81f+U5Ucz6H4I+c5bWRYUzH/piBB4n5FoYlld+/SViCQIBCQ+fynLma +j5wlf22+mISTt/9je1ZfYWlJ+WSJyi5gY5EH9DubfuIU3VaqCM0aQmVybmFk +ZXR0ZSA8YkBleGFtcGxlLm9yZz7CugYTGw4AAABLBYJk3x50BAsJCAcHFQ4M +CgkICwIWAAIXgAIZAQKbAwIeCSIhBpbSe0QWuaCNSSLaePhXEP3BxQ2VHX3W +pW1U6svHvCUiBScJAgcCAAAAAMMGIJEi9+yqkFKsNwX1H5II0riPudFpwBx2 +ypVjNk4aNb7Exl56Aac4tXEhz4fH41q0dAzFww2erZaiUqmohQ4AFSw1jN/W +OiDfb1DkjT/HJ8vXMGpwWdgFPoqsWzTNhd5VCQ== +-----END PGP PRIVATE KEY BLOCK-----`; + + const key = await openpgp.readKey({ armoredKey }); + await expect(key.getSigningKey()).to.be.rejectedWith(/Unsupported hash function/); + }); + it('Ignores marker packets when verifying signatures', async function () { const signatureWithMarkerPacket = `-----BEGIN PGP SIGNATURE----- @@ -807,7 +895,8 @@ DQmhI0SZoTKy4EGhS0bNJ+g2+dJ8Y22fKzLWXwo= await expect(openpgp.sign({ signingKeys: key, date: new Date(key.keyPacket.created - 3600), - message: await openpgp.createMessage({ text: 'Hello World' }) + message: await openpgp.createMessage({ text: 'Hello World' }), + config: { minRSABits: 1024 } })).to.be.rejectedWith(/Signature creation time is in the future/); }); @@ -851,9 +940,7 @@ SlcdMBDgwngEGBYIAAkFAmFppjQCGwwAIQkQDmTSjoPv10MWIQRqj/4SGmAk ibGeE60OZNKOg+/XQx/EAQCM0UYrObp60YbOCxu07Dm6XjCVylbOcsaxCnE7 2eMU4AD+OkgajZgbqSIdAR1ud76FW+W+3xlDi/SMFdU7D49SbQI= =ASQu ------END PGP PRIVATE KEY BLOCK----- - -`; +-----END PGP PRIVATE KEY BLOCK-----`; const armoredMessage = `-----BEGIN PGP MESSAGE----- xA0DAQoWDmTSjoPv10MByw91AGFpplFwbGFpbnRleHTCdQQBFgoABgUCYWml @@ -900,7 +987,7 @@ AkLaG/AkATpuH+DMkYDmKbDLGgD+N4yuxXBJmBfC2IBe4J1S2Gg= date: key.keyPacket.created, format: 'object' }); - await stream.loadStreamsPonyfill(); + await loadStreamsPolyfill(); const { signatures: [sigInfo] } = await openpgp.verify({ verificationKeys: expiredKey, message: await openpgp.readMessage({ armoredMessage: stream.toStream(armoredMessage) }), @@ -931,7 +1018,7 @@ aMsUdQBgnPAcSGVsbG8gV29ybGQgOik= date: key.keyPacket.created, format: 'object' }); - await stream.loadStreamsPonyfill(); + await loadStreamsPolyfill(); const { signatures: [sigInfo] } = await openpgp.verify({ verificationKeys: expiredKey, message: await openpgp.readMessage({ armoredMessage: stream.toStream(armoredMessage) }), @@ -961,7 +1048,7 @@ eSvSZutLuKKbidSYMLhWROPlwKc2GU2ws6PrLZAyCAel/lU= date: key.keyPacket.created, format: 'object' }); - await stream.loadStreamsPonyfill(); + await loadStreamsPolyfill(); const { signatures: [sigInfo] } = await openpgp.verify({ verificationKeys: expiredKey, message: await openpgp.readMessage({ armoredMessage: stream.toStream(armoredMessage) }), @@ -999,6 +1086,37 @@ eSvSZutLuKKbidSYMLhWROPlwKc2GU2ws6PrLZAyCAel/lU= expect(await sigInfo.verified).to.be.true; }); + it('Verification fails if v6 signature has unexpected salt length', async function() { + // signature with salt shorter than expected + const armoredMessage = `-----BEGIN PGP MESSAGE----- + +xDQGAQgbDpdDiCIrq6YZAf5vD3wFIucHRyMNlExatdj6sQcW2FA/vV5eZGCv +mBUS4Mqqki4ByxR1AGUddyNUaGlzIGlzIHNpZ25lZMKGBgEbCAAAACkFgmUd +dyMioQYi5wdHIw2UTFq12PqxBxbYUD+9Xl5kYK+YFRLgyqqSLgAAAADZ9w6X +Q4giK6umGQH+bw98BS96KSXxW39Ue6hNxbSoc5xOqYnTsD+75FYdR1U9fco/ +HDpH7axPa2euIDpwT60NedSjcTx7C9Sots4mTvjMwQQ= +-----END PGP MESSAGE-----`; + const key = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xUsGZR13GRsAAAAgcCI5M7vPn+9uD1ii8nnT/schP5BjXXTyr+q7EmSlcaoA +/OkLygFTbUdwt6hMlfcNyUmS058WSIHxaVtG4uSfyjbCmQYfGwgAAAA6BYJl +HXcZAwsJBwMVCAoCFgACmwMCHgEioQYi5wdHIw2UTFq12PqxBxbYUD+9Xl5k +YK+YFRLgyqqSLgAAAABCZxAAxl8ycoAAY74DEPZDnfSYLP+dqdM8QZ3b/Mp4 +fnzOcVI4RvaxAjp3GZVXxisSS36A2fUx2lpj38y1tIvnnlShfpuylTp73foT +DVf/bROnAM0AwosGEBsIAAAALAWCZR13GQIZASKhBiLnB0cjDZRMWrXY+rEH +FthQP71eXmRgr5gVEuDKqpIuAAAAAFEEEFrhrlN40SgxwpL3UaSWs6F5pD83 +AOtaXLA/e9gFPNgiLnuid3AqUaFa6JlhWf4N/Md6SMQJ5cC7ATxTJ2a3xAMY +5UsE6+HN099QVLx95CMP +-----END PGP PRIVATE KEY BLOCK-----` }); + + const { signatures } = await openpgp.verify({ + message: await openpgp.readMessage({ armoredMessage }), + verificationKeys: key + }); + expect(signatures).to.have.length(1); + await expect(signatures[0].verified).to.be.rejectedWith(/Signature salt does not have the expected length/); + }); + it('Reject cleartext message with arbitrary text added around hash headers (spoofed cleartext message)', async function() { await expect(openpgp.readCleartextMessage({ cleartextMessage: `-----BEGIN PGP SIGNED MESSAGE----- This is not signed but you might think it is Hash: SHA512 @@ -1108,7 +1226,92 @@ Fk7EflUZzngwY4lBzYAfnNBjEjc30xD/ddo+rwE= }); }); - it('Verify signed message with two one pass signatures', async function() { + + it('Verify signed message with a v6 one pass signature', async function() { + // test vector from https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#appendix-A.7 + const message = await openpgp.readMessage({ armoredMessage: `-----BEGIN PGP MESSAGE----- + +xEYGAQobIHZJX1AhiJD39eLuPBgiUU9wUA9VHYblySHkBONKU/usyxhsTwYJppfk +1S36bHIrDB8eJ8GKVnCPZSXsJ7rZrMkBy0p1AAAAAABXaGF0IHdlIG5lZWQgZnJv +bSB0aGUgZ3JvY2VyeSBzdG9yZToKCi0gdG9mdQotIHZlZ2V0YWJsZXMKLSBub29k +bGVzCsKYBgEbCgAAACkFgmOYo2MiIQbLGGxPBgmml+TVLfpscisMHx4nwYpWcI9l +JewnutmsyQAAAABpNiB2SV9QIYiQ9/Xi7jwYIlFPcFAPVR2G5ckh5ATjSlP7rCfQ +b7gKqPxbyxbhljGygHQPnqau1eBzrQD5QVplPEDnemrnfmkrpx0GmhCfokxYz9jj +FtCgazStmsuOXF9SFQE= +-----END PGP MESSAGE-----` }); + const key = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PUBLIC KEY BLOCK----- + +xioGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laPCsQYf +GwoAAABCBYJjh3/jAwsJBwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxy +KwwfHifBilZwj2Ul7Ce62azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lw +gyU2kCcUmKfvBXbAf6rhRYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaE +QsiPlR4zxP/TP7mhfVEe7XWPxtnMUMtf15OyA51YBM4qBmOHf+MZAAAAIIaTJINn ++eUBXbki+PSAld2nhJh/LVmFsS+60WyvXkQ1wpsGGBsKAAAALAWCY4d/4wKbDCIh +BssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce62azJAAAAAAQBIKbpGG2dWTX8 +j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDEM0g12vYxoWM8Y81W+bHBw805 +I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg== +-----END PGP PUBLIC KEY BLOCK-----` }); + + const signingKeyIDs = message.getSigningKeyIDs(); + expect(key.getKeys(signingKeyIDs[0])).to.not.be.empty; + + const { data, signatures } = await openpgp.verify({ message, verificationKeys: key }); + expect(signatures).to.have.length(1); + expect(await signatures[0].verified).to.be.true; + expect((await signatures[0].signature).packets.length).to.equal(1); + expect(data.includes('What we need from the grocery store')).to.be.true; + }); + + it('Verify signed message with two one pass signatures (v3 and v6)', async function() { + // test vector from gopenpgp + const message = await openpgp.readMessage({ armoredMessage: `-----BEGIN PGP MESSAGE----- + +xEYGAAobIBEtnAy3EeX8aDFd2bf/A1Xfi7C/D3mLIkGEwVmD0fecyxhsTwYJppfk +1S36bHIrDB8eJ8GKVnCPZSXsJ7rZrMkAxA0DAAoW8jFVDE9H444ByxRiAAAAAABI +ZWxsbyBXb3JsZCA6KcJ1BAAWCgAnBQJk52D2CRDyMVUMT0fjjhYhBOuFu1+jOnXh +XpROY/IxVQxPR+OOAABOSwEA2nLYxa9ELDxwuPskKCUVs8noyT4fVEScWlWZAKqP +ykIBAJEhqqNHFP4Gok1Ss9mvf6fE25tJkdJKD+7PIH0mCJgAwpgGABsKAAAAKQUC +ZOdg9iKhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce62azJAAAAAA2MIBEt +nAy3EeX8aDFd2bf/A1Xfi7C/D3mLIkGEwVmD0fecr0oTPTcMNupiH7SCY4THe3ue +7PlRvF2WoeWKkNZT1uwcb+ilW5h9eBpp0I4Xj98C0hMA5+rHsTsME2EZM8HADA== +-----END PGP MESSAGE-----` }); + const v6Key = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PUBLIC KEY BLOCK----- + +xioGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laPCsQYf +GwoAAABCBYJjh3/jAwsJBwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxy +KwwfHifBilZwj2Ul7Ce62azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lw +gyU2kCcUmKfvBXbAf6rhRYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaE +QsiPlR4zxP/TP7mhfVEe7XWPxtnMUMtf15OyA51YBM4qBmOHf+MZAAAAIIaTJINn ++eUBXbki+PSAld2nhJh/LVmFsS+60WyvXkQ1wpsGGBsKAAAALAWCY4d/4wKbDCIh +BssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce62azJAAAAAAQBIKbpGG2dWTX8 +j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDEM0g12vYxoWM8Y81W+bHBw805 +I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg== +-----END PGP PUBLIC KEY BLOCK-----` }); + const v4Key = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PUBLIC KEY BLOCK----- +Comment: Alice's OpenPGP certificate + +mDMEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U +b7O1u120JkFsaWNlIExvdmVsYWNlIDxhbGljZUBvcGVucGdwLmV4YW1wbGU+iJAE +ExYIADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQTrhbtfozp14V6UTmPy +MVUMT0fjjgUCXaWfOgAKCRDyMVUMT0fjjukrAPoDnHBSogOmsHOsd9qGsiZpgRnO +dypvbm+QtXZqth9rvwD9HcDC0tC+PHAsO7OTh1S1TC9RiJsvawAfCPaQZoed8gK4 +OARcRwTpEgorBgEEAZdVAQUBAQdAQv8GIa2rSTzgqbXCpDDYMiKRVitCsy203x3s +E9+eviIDAQgHiHgEGBYIACAWIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCXEcE6QIb +DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn +0QEA22Kr7VkCjeAEC08VSTeV+QFsmz55/lntWkwYWhmvOgE= +=iIGO +-----END PGP PUBLIC KEY BLOCK-----` }); + + const { data, signatures } = await openpgp.verify({ message, verificationKeys: [v4Key, v6Key] }); + expect(signatures).to.have.length(2); + expect(await signatures[0].verified).to.be.true; + expect((await signatures[0].signature).packets.length).to.equal(1); + expect(await signatures[1].verified).to.be.true; + expect((await signatures[1].signature).packets.length).to.equal(1); + expect(data).to.equal('Hello World :)'); + }); + + it('Verify signed message with two one pass signatures (both v3)', async function() { const msg_armor = ['-----BEGIN PGP MESSAGE-----', 'Version: GnuPG v2.0.19 (GNU/Linux)', @@ -1206,7 +1409,7 @@ Fk7EflUZzngwY4lBzYAfnNBjEjc30xD/ddo+rwE= }); expect(await sig.verified).to.be.true; const { packets: [{ rawNotations: notations }] } = await sig.signature; - expect(notations).to.have.length(2); + expect(notations).to.have.length(3); expect(notations[0].name).to.equal('test@example.com'); expect(notations[0].value).to.deep.equal(new Uint8Array([116, 101, 115, 116])); expect(notations[0].humanReadable).to.be.true; @@ -1215,6 +1418,112 @@ Fk7EflUZzngwY4lBzYAfnNBjEjc30xD/ddo+rwE= expect(notations[1].value).to.deep.equal(new Uint8Array([0, 1, 2, 3])); expect(notations[1].humanReadable).to.be.false; expect(notations[1].critical).to.be.false; + expect(notations[2].name).to.equal('salt@notations.openpgpjs.org'); + expect(notations[2].humanReadable).to.be.false; + expect(notations[2].critical).to.be.false; + }); + + it('v4 signatures are randomized via salt notation (`config.nonDeterministicSignaturesViaNotation`)', async function() { + const v4SigningKey = await openpgp.readKey({ + armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xVgEX8+jfBYJKwYBBAHaRw8BAQdA9GbdDjprR0sWf0R5a5IpulUauc0FsmzJ +mOYCfoowt8EAAP9UwaqC0LWWQ5RlX7mps3728vFa/If1KBVwAjk7Uqhi2BKL +zQ90ZXN0MiA8YkBhLmNvbT7CjAQQFgoAHQUCX8+jfAQLCQcIAxUICgQWAgEA +AhkBAhsDAh4BACEJEG464aV2od77FiEEIcg441MtKnyJnPDRbjrhpXah3vuR +gQD+Il6Gw2oIok4/ANyDDLBYZtKqRrMv4NcfF9DHYuAFcP4BAPhFOffyP3qU +AEZb7QPrWdLfhn8/FeSFZxJvnmupQ9sDx10EX8+jfBIKKwYBBAGXVQEFAQEH +QOSzo9cX1U2esGFClprOt0QWXNJ97228R5tKFxo6/0NoAwEIBwAA/0n4sq2i +N6/jE+6rVO4o/7LW0xahxpV1tTA6qv1Op9TwFIDCeAQYFggACQUCX8+jfAIb +DAAhCRBuOuGldqHe+xYhBCHIOONTLSp8iZzw0W464aV2od773XcA/jlmX8/c +1/zIotEkyMZB4mI+GAg3FQ6bIACFBH1sz0MzAP9Snri0P4FRZ8D5THRCJoUm +GBgpBmrf6IVv484jBswGDA== +=8rBO +-----END PGP PRIVATE KEY BLOCK-----` + }); + + const date = new Date('Tue, 25 Dec 2023 00:00:00 GMT'); + const text = 'test'; + const armoredRandomSignature1 = await openpgp.sign({ + message: await openpgp.createMessage({ text }), + signingKeys: v4SigningKey, + date, + detached: true + }); + const armoredRandomSignature2 = await openpgp.sign({ + message: await openpgp.createMessage({ text }), + signingKeys: v4SigningKey, + date, + detached: true + }); + const randomSignature1 = await openpgp.readSignature({ armoredSignature: armoredRandomSignature1 }); + const randomSignature2 = await openpgp.readSignature({ armoredSignature: armoredRandomSignature2 }); + expect(randomSignature1.packets[0].signedHashValue).to.not.deep.equal(randomSignature2.packets[0].signedHashValue); + + // ensure the signatures are verifiable, as a sanity check + const verification1 = await openpgp.verify({ message: await openpgp.createMessage({ text }), signature: randomSignature1, verificationKeys: v4SigningKey, expectSigned: true }); + expect(verification1.data).to.equal(text); + + const armoredDeterministicSignature1 = await openpgp.sign({ + message: await openpgp.createMessage({ text }), + signingKeys: v4SigningKey, + date, + detached: true, + config: { nonDeterministicSignaturesViaNotation: false } + }); + const armoredDeterministicSignature2 = await openpgp.sign({ + message: await openpgp.createMessage({ text }), + signingKeys: v4SigningKey, + date, + detached: true, + config: { nonDeterministicSignaturesViaNotation: false } + }); + const deterministicSignature1 = await openpgp.readSignature({ armoredSignature: armoredDeterministicSignature1 }); + const deterministicSignature2 = await openpgp.readSignature({ armoredSignature: armoredDeterministicSignature2 }); + expect(deterministicSignature1.packets[0].signedHashValue).to.deep.equal(deterministicSignature2.packets[0].signedHashValue); + const verification2 = await openpgp.verify({ message: await openpgp.createMessage({ text }), signature: deterministicSignature1, verificationKeys: v4SigningKey, expectSigned: true }); + expect(verification2.data).to.equal(text); + }); + + it('Verify v6 cleartext signed message with openpgp.verify', async function() { + // test vector from https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-08.html#name-sample-v6-certificate-trans + const cleartextMessage = `-----BEGIN PGP SIGNED MESSAGE----- + +What we need from the grocery store: + +- - tofu +- - vegetables +- - noodles + +-----BEGIN PGP SIGNATURE----- + +wpgGARsKAAAAKQWCY5ijYyIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6 +2azJAAAAAGk2IHZJX1AhiJD39eLuPBgiUU9wUA9VHYblySHkBONKU/usJ9BvuAqo +/FvLFuGWMbKAdA+epq7V4HOtAPlBWmU8QOd6aud+aSunHQaaEJ+iTFjP2OMW0KBr +NK2ay45cX1IVAQ== +-----END PGP SIGNATURE-----`; + + const publicKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PUBLIC KEY BLOCK----- + +xioGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laPCsQYf +GwoAAABCBYJjh3/jAwsJBwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxy +KwwfHifBilZwj2Ul7Ce62azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lw +gyU2kCcUmKfvBXbAf6rhRYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaE +QsiPlR4zxP/TP7mhfVEe7XWPxtnMUMtf15OyA51YBM4qBmOHf+MZAAAAIIaTJINn ++eUBXbki+PSAld2nhJh/LVmFsS+60WyvXkQ1wpsGGBsKAAAALAWCY4d/4wKbDCIh +BssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce62azJAAAAAAQBIKbpGG2dWTX8 +j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDEM0g12vYxoWM8Y81W+bHBw805 +I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg== +-----END PGP PUBLIC KEY BLOCK-----` }); + + const plaintext = 'What we need from the grocery store:\n\n- tofu\n- vegetables\n- noodles\n'; + const message = await openpgp.readCleartextMessage({ cleartextMessage }); + + const { signatures, data } = await openpgp.verify({ message, verificationKeys: publicKey }); + expect(data).to.equal(plaintext); + expect(signatures).to.have.length(1); + expect(await signatures[0].verified).to.be.true; + expect((await signatures[0].signature).packets.length).to.equal(1); }); it('Verify cleartext signed message with two signatures with openpgp.verify', async function() { @@ -1405,6 +1714,38 @@ ${armoredSignature} expect(await signatures[0].verified).to.be.true; }); + it('Does not emit headers for v6 cleartext message', async function() { + const v6PrivateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMAGXKB +exK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJjh3/jAwsJ +BwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6 +2azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2kCcUmKfvBXbAf6rh +RYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaEQsiPlR4zxP/TP7mhfVEe +7XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/ +LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u/tVY6a//1q0NWC1X+yui3O24wpsG +GBsKAAAALAWCY4d/4wKbDCIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6 +2azJAAAAAAQBIKbpGG2dWTX8j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDE +M0g12vYxoWM8Y81W+bHBw805I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUr +k0mXubZvyl4GBg== +-----END PGP PRIVATE KEY BLOCK-----` }); + const v4PrivateKey = await openpgp.decryptKey({ + privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }), + passphrase: 'hello world' + }); + + const message = await openpgp.createCleartextMessage({ text: 'check header message' }); + + const cleartextMessageV6 = await openpgp.sign({ message, signingKeys: v6PrivateKey }); + const cleartextMessageV4 = await openpgp.sign({ + message, + signingKeys: [v4PrivateKey, v6PrivateKey], + config: { minRSABits: 1024 } + }); + expect(cleartextMessageV6).to.not.contain('Hash:'); + expect(cleartextMessageV4).to.contain('Hash:'); + }); + function tests() { it('Verify signed message with trailing spaces from GPG', async function() { const armoredMessage = @@ -1454,9 +1795,9 @@ yYDnCgA= -----END PGP MESSAGE-----`.split(''); const plaintext = 'space: \nspace and tab: \t\nno trailing space\n \ntab:\t\ntab and space:\t '; - await stream.loadStreamsPonyfill(); + await loadStreamsPolyfill(); const message = await openpgp.readMessage({ - armoredMessage: new stream.ReadableStream({ + armoredMessage: new ReadableStream({ async pull(controller) { await new Promise(setTimeout); controller.enqueue(armoredMessage.shift()); @@ -1520,9 +1861,9 @@ hkJiXopCSWKSlQInL1devkJJUWJmTmZeugJYlpdLAagQJM0JpsCqIQZwKgAA -----END PGP MESSAGE-----`.split(''); const plaintext = 'space: \nspace and tab: \t\nno trailing space\n \ntab:\t\ntab and space:\t '; - await stream.loadStreamsPonyfill(); + await loadStreamsPolyfill(); const message = await openpgp.readMessage({ - armoredMessage: new stream.ReadableStream({ + armoredMessage: new ReadableStream({ async pull(controller) { await new Promise(setTimeout); controller.enqueue(armoredMessage.shift()); @@ -1913,7 +2254,7 @@ uDvEBgD+LCEUOPejUTCMqPyd04ssdOq1AlMJOmUGUwLk7kFP7Aw= const message = await openpgp.createMessage({ text: content }); await message.appendSignature(detachedSig); - const { data, signatures } = await openpgp.verify({ verificationKeys:[publicKey], message, config: { minRSABits: 1024 } }); + const { data, signatures } = await openpgp.verify({ verificationKeys:[publicKey], message, config: { minRSABits: 1024, allowMissingKeyFlags: true } }); expect(data).to.equal(content); expect(signatures).to.have.length(1); expect(await signatures[0].verified).to.be.true; @@ -2076,7 +2417,7 @@ oaBUyhCKt8tz6Q== -----END PGP PRIVATE KEY BLOCK-----`; const key = await openpgp.readKey({ armoredKey }); const decrypted = await openpgp.decrypt({ - message: await openpgp.readMessage({ armoredMessage: encrypted }), + message: await openpgp.readMessage({ armoredMessage: encrypted, config: { enableParsingV5Entities: true } }), verificationKeys: key, decryptionKeys: key, config: { minRSABits: 1024 } @@ -2135,7 +2476,8 @@ EYaN9YdDOU2jF+HOaSNaJAsPF8J6BRgTCAAJBQJf0mstAhsMACMiIQUee6Tb AcvDfr9a0Cp4WAVzKDKLUzrRMgEAozi0VyjiBo1U2LcwTPJkA4PEQqQRVW1D KZTMSAH7JEo= =tqWy ------END PGP PRIVATE KEY BLOCK-----` +-----END PGP PRIVATE KEY BLOCK-----`, + config: { enableParsingV5Entities: true } }); const signed = `-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256 @@ -2148,8 +2490,30 @@ A3X6psihFkcA+Nuog2qpAq20Zc2lzVjDZzQosb8MLvKMg3UFCX12Oc0BAJwd JImeZLY02MctIpGZULbqgcUGK0P/yqrPL8Pe4lQM =Pacb -----END PGP SIGNATURE-----`; - const message = await openpgp.readCleartextMessage({ cleartextMessage: signed }); + const message = await openpgp.readCleartextMessage({ cleartextMessage: signed, config: { enableParsingV5Entities: true } }); const verified = await openpgp.verify({ verificationKeys: key, message }); expect(await verified.signatures[0].verified).to.be.true; }); + + it('Should parse a signature with a critical unknown subpacket, but not verify it', async function() { + const key = await openpgp.readKey({ + armoredKey: `-----BEGIN PGP PUBLIC KEY BLOCK----- + +xjMEZmsxYRYJKwYBBAHaRw8BAQdAgPH3tbfVO4CNqRQevvYW6kYY0qpNQltw +CegLonECw/vNBFRlc3TCwBgEEBYKAIoFgmZrMWEDCwkHCZAFbxb2+9/G3UUU +AAAAAAAcACBzYWx0QG5vdGF0aW9ucy5vcGVucGdwanMub3Jn1Bg/fpBZjM6n +CMTgcCh7+NHCoTmgpPef1+7CO792jL4FFQgKDA4EFgACAQIZAQKbAwIeARYh +BL/u0Jl6QJQVEZ0grQVvFvb738bdBOMBAgMAAMAYAQD25k4by+9P5WuOvirp +MhKE441PBb1n3fhaVpLogoVgZwD/ST2+Y5G6NdJM+U45iwfZDfa3ix1/zUSf +DF+cVdXVOwrOOARmazFhEgorBgEEAZdVAQUBAQdAGVw9vpajNPafAzshTmok +O1ZCDuQN9KkV+qTxZ7JGoEIDAQgHwsADBBgWCgB1BYJmazFhCZAFbxb2+9/G +3UUUAAAAAAAcACBzYWx0QG5vdGF0aW9ucy5vcGVucGdwanMub3JnRIP2KWB1 +C8+8vpmscsPPBl+KYeNcCbCOJqo7G3A5ES0CmwwWIQS/7tCZekCUFRGdIK0F +bxb2+9/G3QTjAQIDAABj9wEA2E/C98UXszf4TWH7/xBGICoDDNxceMhSDvtt +nYhoNlUA/Ar+Ofx+vMf9oYcNjPEbYu/yu1AtKY44aZvDBLK2+OAI +=YrJy +-----END PGP PUBLIC KEY BLOCK-----` + }); + await expect(key.verifyPrimaryKey()).to.be.rejectedWith(/Unknown critical signature subpacket type 99/); + }); }); diff --git a/test/general/streaming.js b/test/general/streaming.js index aa5cfa2b..a0123840 100644 --- a/test/general/streaming.js +++ b/test/general/streaming.js @@ -1,17 +1,16 @@ /* eslint-disable max-lines */ -const stream = require('@openpgp/web-stream-tools'); -const stub = require('sinon/lib/sinon/stub'); -const { use: chaiUse, expect } = require('chai'); -chaiUse(require('chai-as-promised')); +/* globals loadStreamsPolyfill */ +import * as stream from '@openpgp/web-stream-tools'; +import sinon from 'sinon'; +import { use as chaiUse, expect } from 'chai'; +import chaiAsPromised from 'chai-as-promised'; // eslint-disable-line import/newline-after-import +chaiUse(chaiAsPromised); -const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../..'); -const random = require('../../src/crypto/random'); -const util = require('../../src/util'); +import openpgp from '../initOpenpgp.js'; +import * as random from '../../src/crypto/random.js'; +import util from '../../src/util.js'; -const input = require('./testInputs'); - -const useNativeStream = (() => { try { new global.ReadableStream(); return true; } catch (e) { return false; } })(); // eslint-disable-line no-new -const NodeReadableStream = useNativeStream ? undefined : require('stream').Readable; +import * as input from './testInputs.js'; const detectNode = () => typeof globalThis.process === 'object' && typeof globalThis.process.versions === 'object'; @@ -23,8 +22,8 @@ function getLargeDataStream() { const dataArrivedPromise = new Promise(resolve => { dataArrived = resolve; }); - const expectedType = global.ReadableStream ? 'web' : 'node'; - const dataStream = global.ReadableStream ? new global.ReadableStream({ + const expectedType = 'web'; + const dataStream = new globalThis.ReadableStream({ async pull(controller) { await new Promise(setTimeout); if (i < (expectedType === 'web' ? 100 : 500)) { @@ -42,26 +41,7 @@ function getLargeDataStream() { } }, new ByteLengthQueuingStrategy({ highWaterMark: 1024 - })) : new NodeReadableStream({ - highWaterMark: 1024, - async read() { - while (true) { - await new Promise(setTimeout); - if (i < (expectedType === 'web' ? 100 : 500)) { - i++; - if (i === 4) await dataArrivedPromise; - const randomBytes = random.getRandomBytes(1024); - dataChunks.push(randomBytes); - if (!this.push(randomBytes)) break; - } else { - return this.push(null); - } - } - }, - destroy() { - canceled = true; - } - }); + })); return { dataStream, dataChunks, dataArrived, isCanceled: () => canceled, expectedType }; } @@ -221,18 +201,12 @@ let pubKey; function tests() { it('Encrypt small message', async function() { - const data = global.ReadableStream ? new global.ReadableStream({ + const data = new globalThis.ReadableStream({ start(controller) { controller.enqueue(util.stringToUint8Array('hello ')); controller.enqueue(util.stringToUint8Array('world')); controller.close(); } - }) : new NodeReadableStream({ - read() { - this.push(util.stringToUint8Array('hello ')); - this.push(util.stringToUint8Array('world')); - this.push(null); - } }); const encrypted = await openpgp.encrypt({ message: await openpgp.createMessage({ binary: data }), @@ -310,7 +284,7 @@ function tests() { expect(stream.isStream(encrypted)).to.equal(expectedType); const message = await openpgp.readMessage({ binaryMessage: encrypted }); - setTimeout(dataArrived, 3000); // Do not wait until data arrived, but wait a bit to check that it doesn't arrive early. + setTimeout(() => dataArrived(), 3000); // Do not wait until data arrived, but wait a bit to check that it doesn't arrive early. const decrypted = await openpgp.decrypt({ passwords: ['test'], message, @@ -467,7 +441,7 @@ function tests() { } }); - it('Detect MDC modifications (allowUnauthenticatedStream=true)', async function() { + it('Detect modification (allowUnauthenticatedStream=true)', async function() { const { dataStream, expectedType, dataChunks, dataArrived } = getLargeDataStream(); const aeadProtectValue = openpgp.config.aeadProtect; @@ -482,9 +456,8 @@ function tests() { expect(stream.isStream(encrypted)).to.equal(expectedType); const message = await openpgp.readMessage({ - armoredMessage: stream[expectedType === 'node' ? 'webToNode' : global.ReadableStream === stream.ReadableStream ? 'toStream' : 'toNativeReadable'](stream.transform(encrypted, value => { + armoredMessage: stream.toStream(stream.transform(encrypted, value => { value += ''; - if (value === '=' || value.length === 5) return; // Remove checksum const newlineIndex = value.indexOf('\n', 500); if (value.length > 1000) return value.slice(0, newlineIndex - 1) + (value[newlineIndex - 1] === 'a' ? 'b' : 'a') + value.slice(newlineIndex); return value; @@ -507,7 +480,7 @@ function tests() { } }); - it('Detect armor checksum error (allowUnauthenticatedStream=true)', async function() { + it('Detect modification when not passing public keys (allowUnauthenticatedStream=true)', async function() { const { dataStream, expectedType, dataChunks, dataArrived } = getLargeDataStream(); const allowUnauthenticatedStreamValue = openpgp.config.allowUnauthenticatedStream; @@ -522,46 +495,7 @@ function tests() { expect(stream.isStream(encrypted)).to.equal(expectedType); const message = await openpgp.readMessage({ - armoredMessage: stream[expectedType === 'node' ? 'webToNode' : global.ReadableStream === stream.ReadableStream ? 'toStream' : 'toNativeReadable'](stream.transform(encrypted, value => { - value += ''; - const newlineIndex = value.indexOf('\n', 500); - if (value.length > 1000) return value.slice(0, newlineIndex - 1) + (value[newlineIndex - 1] === 'a' ? 'b' : 'a') + value.slice(newlineIndex); - return value; - }), { encoding: 'utf8' }) - }); - const decrypted = await openpgp.decrypt({ - verificationKeys: pubKey, - decryptionKeys: privKey, - message, - format: 'binary' - }); - expect(stream.isStream(decrypted.data)).to.equal(expectedType); - const reader = stream.getReader(decrypted.data); - expect(await reader.peekBytes(1024)).not.to.deep.equal(dataChunks[0]); - dataArrived(); - await expect(reader.readToEnd()).to.be.rejectedWith('Ascii armor integrity check failed'); - expect(decrypted.signatures).to.exist.and.have.length(1); - } finally { - openpgp.config.allowUnauthenticatedStream = allowUnauthenticatedStreamValue; - } - }); - - it('Detect armor checksum error when not passing public keys (allowUnauthenticatedStream=true)', async function() { - const { dataStream, expectedType, dataChunks, dataArrived } = getLargeDataStream(); - - const allowUnauthenticatedStreamValue = openpgp.config.allowUnauthenticatedStream; - openpgp.config.allowUnauthenticatedStream = true; - try { - const encrypted = await openpgp.encrypt({ - message: await openpgp.createMessage({ binary: dataStream }), - encryptionKeys: pubKey, - signingKeys: privKey, - config: { minRSABits: 1024 } - }); - expect(stream.isStream(encrypted)).to.equal(expectedType); - - const message = await openpgp.readMessage({ - armoredMessage: stream[expectedType === 'node' ? 'webToNode' : global.ReadableStream === stream.ReadableStream ? 'toStream' : 'toNativeReadable'](stream.transform(encrypted, value => { + armoredMessage: stream.toStream(stream.transform(encrypted, value => { value += ''; const newlineIndex = value.indexOf('\n', 500); if (value.length > 1000) return value.slice(0, newlineIndex - 1) + (value[newlineIndex - 1] === 'a' ? 'b' : 'a') + value.slice(newlineIndex); @@ -577,7 +511,7 @@ function tests() { const reader = stream.getReader(decrypted.data); expect(await reader.peekBytes(1024)).not.to.deep.equal(dataChunks[0]); dataArrived(); - await expect(reader.readToEnd()).to.be.rejectedWith('Ascii armor integrity check failed'); + await expect(reader.readToEnd()).to.be.rejectedWith('Modification detected.'); expect(decrypted.signatures).to.exist.and.have.length(1); await expect(decrypted.signatures[0].verified).to.be.eventually.rejectedWith(/Could not find signing key/); } finally { @@ -585,7 +519,7 @@ function tests() { } }); - it('Sign/verify: Detect armor checksum error', async function() { + it('Sign/verify: Detect modification', async function() { const { dataStream, expectedType, dataChunks, dataArrived } = getLargeDataStream(); const signed = await openpgp.sign({ @@ -596,7 +530,7 @@ function tests() { expect(stream.isStream(signed)).to.equal(expectedType); const message = await openpgp.readMessage({ - armoredMessage: stream[expectedType === 'node' ? 'webToNode' : global.ReadableStream === stream.ReadableStream ? 'toStream' : 'toNativeReadable'](stream.transform(signed, value => { + armoredMessage: stream.toStream(stream.transform(signed, value => { value += ''; const newlineIndex = value.indexOf('\n', 500); if (value.length > 1000) return value.slice(0, newlineIndex - 1) + (value[newlineIndex - 1] === 'a' ? 'b' : 'a') + value.slice(newlineIndex); @@ -613,8 +547,9 @@ function tests() { const reader = stream.getReader(verified.data); expect(await reader.peekBytes(1024)).not.to.deep.equal(dataChunks[0]); dataArrived(); - await expect(reader.readToEnd()).to.be.rejectedWith('Ascii armor integrity check failed'); expect(verified.signatures).to.exist.and.have.length(1); + await reader.readToEnd(); + await expect(verified.signatures[0].verified).to.be.rejectedWith('Signed digest did not match'); }); it('stream.transformPair()', async function() { @@ -729,19 +664,13 @@ function tests() { }); it('Detached sign/verify: support streamed input', async function() { - const getDataStream = () => (global.ReadableStream ? new global.ReadableStream({ + const getDataStream = () => new globalThis.ReadableStream({ start(controller) { controller.enqueue(util.stringToUint8Array('hello ')); controller.enqueue(util.stringToUint8Array('world')); controller.close(); } - }) : new NodeReadableStream({ - read() { - this.push(util.stringToUint8Array('hello ')); - this.push(util.stringToUint8Array('world')); - this.push(null); - } - })); + }); const signed = await openpgp.sign({ message: await openpgp.createMessage({ binary: getDataStream() }), @@ -794,20 +723,13 @@ function tests() { it('Detached sign small message', async function() { - const data = global.ReadableStream ? new global.ReadableStream({ + const data = new globalThis.ReadableStream({ start(controller) { controller.enqueue(util.stringToUint8Array('hello ')); controller.enqueue(util.stringToUint8Array('world')); controller.close(); } - }) : new NodeReadableStream({ - read() { - this.push(util.stringToUint8Array('hello ')); - this.push(util.stringToUint8Array('world')); - this.push(null); - } }); - const expectedType = global.ReadableStream ? 'web' : 'node'; const signed = await openpgp.sign({ message: await openpgp.createMessage({ binary: data }), @@ -815,7 +737,7 @@ function tests() { detached: true, config: { minRSABits: 1024 } }); - expect(stream.isStream(signed)).to.equal(expectedType); + expect(stream.isStream(signed)).to.equal('web'); const armoredSignature = await stream.readToEnd(signed); const signature = await openpgp.readSignature({ armoredSignature }); const verified = await openpgp.verify({ @@ -830,20 +752,13 @@ function tests() { }); it('Detached sign small message using brainpool curve keys', async function() { - const data = global.ReadableStream ? new global.ReadableStream({ + const data = new globalThis.ReadableStream({ start(controller) { controller.enqueue(util.stringToUint8Array('hello ')); controller.enqueue(util.stringToUint8Array('world')); controller.close(); } - }) : new NodeReadableStream({ - read() { - this.push(util.stringToUint8Array('hello ')); - this.push(util.stringToUint8Array('world')); - this.push(null); - } }); - const expectedType = global.ReadableStream ? 'web' : 'node'; const pub = await openpgp.readKey({ armoredKey: brainpoolPub }); const priv = await openpgp.decryptKey({ @@ -858,7 +773,7 @@ function tests() { detached: true, config }); - expect(stream.isStream(signed)).to.equal(expectedType); + expect(stream.isStream(signed)).to.equal('web'); const armoredSignature = await stream.readToEnd(signed); const signature = await openpgp.readSignature({ armoredSignature }); const verified = await openpgp.verify({ @@ -873,20 +788,13 @@ function tests() { }); it('Detached sign small message using curve25519 keys (legacy format)', async function() { - const data = global.ReadableStream ? new global.ReadableStream({ + const data = new globalThis.ReadableStream({ async start(controller) { controller.enqueue(util.stringToUint8Array('hello ')); controller.enqueue(util.stringToUint8Array('world')); controller.close(); } - }) : new NodeReadableStream({ - read() { - this.push(util.stringToUint8Array('hello ')); - this.push(util.stringToUint8Array('world')); - this.push(null); - } }); - const expectedType = global.ReadableStream ? 'web' : 'node'; const pub = await openpgp.readKey({ armoredKey: xPub }); const priv = await openpgp.decryptKey({ @@ -899,7 +807,7 @@ function tests() { signingKeys: priv, detached: true }); - expect(stream.isStream(signed)).to.equal(expectedType); + expect(stream.isStream(signed)).to.equal('web'); const armoredSignature = await stream.readToEnd(signed); const signature = await openpgp.readSignature({ armoredSignature }); const verified = await openpgp.verify({ @@ -990,7 +898,7 @@ function tests() { const plaintext = []; let i = 0; - const data = global.ReadableStream ? new global.ReadableStream({ + const data = new globalThis.ReadableStream({ async pull(controller) { await new Promise(resolve => { setTimeout(resolve, 10); }); if (i++ < 10) { @@ -1001,35 +909,20 @@ function tests() { controller.close(); } } - }) : new NodeReadableStream({ - encoding: 'utf8', - async read() { - while (true) { - await new Promise(resolve => { setTimeout(resolve, 10); }); - if (i++ < 10) { - const randomData = input.createSomeMessage(); - plaintext.push(randomData); - if (!this.push(randomData)) break; - } else { - return this.push(null); - } - } - } }); - const expectedType = global.ReadableStream ? 'web' : 'node'; const encrypted = await openpgp.encrypt({ message: await openpgp.createMessage({ text: data }), passwords: ['test'] }); - expect(stream.isStream(encrypted)).to.equal(expectedType); + expect(stream.isStream(encrypted)).to.equal('web'); const message = await openpgp.readMessage({ armoredMessage: encrypted }); const decrypted = await openpgp.decrypt({ passwords: ['test'], message }); - expect(stream.isStream(decrypted.data)).to.equal(expectedType); + expect(stream.isStream(decrypted.data)).to.equal('web'); const reader = stream.getReader(decrypted.data); expect((await reader.peekBytes(plaintext[0].length * 4)).toString('utf8').substr(0, plaintext[0].length)).to.equal(plaintext[0]); expect((await reader.readToEnd()).toString('utf8')).to.equal(util.concat(plaintext)); @@ -1039,7 +932,7 @@ function tests() { it("Don't pull entire input stream when we're not pulling decrypted stream (AEAD)", async function() { let coresStub; if (detectNode()) { - coresStub = stub(require('os'), 'cpus'); + coresStub = sinon.stub(util.nodeRequire('os'), 'cpus'); coresStub.returns(new Array(2)); // Object.defineProperty(require('os'), 'cpus', { value: () => [,], configurable: true }); } else { @@ -1101,7 +994,9 @@ function tests() { }); } -module.exports = () => describe('Streaming', function() { +export default () => describe('Streaming', function() { + const needsStreamPolyfills = !globalThis.ReadableStream; + before(async function() { pubKey = await openpgp.readKey({ armoredKey: pub_key }); privKey = await openpgp.decryptKey({ @@ -1109,41 +1004,44 @@ module.exports = () => describe('Streaming', function() { passphrase: 'hello world' }); - await stream.loadStreamsPonyfill(); + await loadStreamsPolyfill(); }); tests(); - if (detectNode()) { - const fs = require('fs'); + if (detectNode() && !needsStreamPolyfills) { // ReadableStream polyfills interfere with these tests + const fs = util.nodeRequire('fs'); + const { Readable: NodeReadableStream } = util.nodeRequire('stream'); + const { fileURLToPath } = util.nodeRequire('url'); + const __filename = fileURLToPath(import.meta.url); it('Node: Encrypt and decrypt text message roundtrip', async function() { const plaintext = fs.readFileSync(__filename.replace('streaming.js', 'openpgp.js'), 'utf8'); // eslint-disable-line no-sync - const data = fs.createReadStream(__filename.replace('streaming.js', 'openpgp.js'), { encoding: 'utf8' }); + const data = NodeReadableStream.toWeb(fs.createReadStream(__filename.replace('streaming.js', 'openpgp.js'), { encoding: 'utf8' })); const encrypted = await openpgp.encrypt({ message: await openpgp.createMessage({ text: data }), passwords: ['test'] }); - expect(stream.isStream(encrypted)).to.equal('node'); + expect(stream.isStream(encrypted)).to.equal('web'); const message = await openpgp.readMessage({ armoredMessage: encrypted }); const decrypted = await openpgp.decrypt({ passwords: ['test'], message }); - expect(stream.isStream(decrypted.data)).to.equal('node'); + expect(stream.isStream(decrypted.data)).to.equal('web'); expect(await stream.readToEnd(decrypted.data)).to.equal(plaintext); }); it('Node: Encrypt and decrypt binary message roundtrip', async function() { const plaintext = fs.readFileSync(__filename.replace('streaming.js', 'openpgp.js')); // eslint-disable-line no-sync - const data = fs.createReadStream(__filename.replace('streaming.js', 'openpgp.js')); + const data = NodeReadableStream.toWeb(fs.createReadStream(__filename.replace('streaming.js', 'openpgp.js'))); const encrypted = await openpgp.encrypt({ message: await openpgp.createMessage({ binary: data }), passwords: ['test'], format: 'binary' }); - expect(stream.isStream(encrypted)).to.equal('node'); + expect(stream.isStream(encrypted)).to.equal('web'); const message = await openpgp.readMessage({ binaryMessage: encrypted }); const decrypted = await openpgp.decrypt({ @@ -1151,7 +1049,7 @@ module.exports = () => describe('Streaming', function() { message, format: 'binary' }); - expect(stream.isStream(decrypted.data)).to.equal('node'); + expect(stream.isStream(decrypted.data)).to.equal('web'); expect(await stream.readToEnd(decrypted.data)).to.deep.equal(plaintext); }); } diff --git a/test/general/testInputs.js b/test/general/testInputs.js index 5879321b..44d59d32 100644 --- a/test/general/testInputs.js +++ b/test/general/testInputs.js @@ -12,6 +12,6 @@ function createSomeMessage() { return '  \t' + String.fromCodePoint(...arr).replace(/[\r\u2028\u2029]/g, '\n') + '  \t\n한국어/조선말'; } -module.exports = { - createSomeMessage: createSomeMessage +export { + createSomeMessage }; diff --git a/test/general/util.js b/test/general/util.js index d4833af2..5a7d87f4 100644 --- a/test/general/util.js +++ b/test/general/util.js @@ -1,8 +1,7 @@ -const { expect } = require('chai'); -const util = require('../../src/util'); +import { expect } from 'chai'; +import util from '../../src/util'; - -module.exports = () => describe('Util unit tests', function() { +export default () => describe('Util unit tests', function() { describe('isString', function() { it('should return true for type "string"', function() { @@ -39,7 +38,7 @@ module.exports = () => describe('Util unit tests', function() { expect(util.isArray(data)).to.be.true; }); it('should return true for type Array', function() { - const data = Array(); // eslint-disable-line no-array-constructor + const data = Array(); // eslint-disable-line @typescript-eslint/no-array-constructor expect(util.isArray(data)).to.be.true; }); it('should return true for inherited type of Array', function() { @@ -109,20 +108,40 @@ module.exports = () => describe('Util unit tests', function() { const data = 'test@example.com'; expect(util.isEmailAddress(data)).to.be.true; }); - it('should return true for valid email address', function() { + it('should return true for valid email address (internationalized domain name)', function() { const data = 'test@xn--wgv.xn--q9jyb4c'; expect(util.isEmailAddress(data)).to.be.true; }); - it('should return false for invalid email address', function() { + it('should return true for valid email address (trailing numbers in domain)', function() { + const data = 'test1@com.com09'; + expect(util.isEmailAddress(data)).to.be.true; + }); + it('should return true for valid email address (no . in domain part)', function() { + const data = 'test@localhost'; + expect(util.isEmailAddress(data)).to.be.true; + }); + it('should return true for valid email address (unicode chars)', function() { + const data = '🙂@localhost'; + expect(util.isEmailAddress(data)).to.be.true; + }); + it('should return false for invalid email address (full userID)', function() { const data = 'Test User '; expect(util.isEmailAddress(data)).to.be.false; }); - it('should return false for invalid email address', function() { - const data = 'test@examplecom'; + it('should return false for invalid email address (missing @)', function() { + const data = 'testexamplecom'; expect(util.isEmailAddress(data)).to.be.false; }); - it('should return false for invalid email address', function() { - const data = 'testexamplecom'; + it('should return false for invalid email address (invisible unicode control char)', function() { + const data = 'test\u{feff}ctrl@email.it'; + expect(util.isEmailAddress(data)).to.be.false; + }); + it('should return false for invalid email address (trailing punctuation)', function() { + const data = 'test@localhost.'; + expect(util.isEmailAddress(data)).to.be.false; + }); + it('should return false for invalid email address (including whitespace)', function() { + const data = 'test space@email.it'; expect(util.isEmailAddress(data)).to.be.false; }); it('should return false for empty string', function() { diff --git a/test/general/x25519.js b/test/general/x25519.js index 0a465b83..e0cc8628 100644 --- a/test/general/x25519.js +++ b/test/general/x25519.js @@ -1,17 +1,20 @@ -const { use: chaiUse, expect } = require('chai'); -chaiUse(require('chai-as-promised')); +import { use as chaiUse, expect } from 'chai'; +import chaiAsPromised from 'chai-as-promised'; // eslint-disable-line import/newline-after-import +chaiUse(chaiAsPromised); -const nacl = require('@openpgp/tweetnacl'); +import nacl from '@openpgp/tweetnacl'; +import openpgp from '../initOpenpgp.js'; -const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../..'); -const elliptic = require('../../src/crypto/public_key/elliptic'); -const signature = require('../../src/crypto/signature'); -const OID = require('../../src/type/oid'); -const util = require('../../src/util'); +import * as elliptic from '../../src/crypto/public_key/elliptic'; +import * as signature from '../../src/crypto/signature'; +import OID from '../../src/type/oid'; +import util from '../../src/util'; -const input = require('./testInputs'); +import * as input from './testInputs'; -module.exports = () => (openpgp.config.ci ? describe.skip : describe)('X25519 Cryptography (legacy format)', function () { +const isSafariOrHeadlessWebKit = () => typeof window !== 'undefined' && window.navigator.userAgent.match(/WebKit/) && !window.navigator.userAgent.match(/Chrome/); + +export default () => describe('X25519 Cryptography (legacy format)', function () { const data = { light: { id: '1ecdf026c0245830', @@ -215,10 +218,12 @@ module.exports = () => (openpgp.config.ci ? describe.skip : describe)('X25519 Cr expect(await result.signatures[0].verified).to.be.true; }); - describe('Ed25519 Test Vectors from RFC8032', function () { + // Safari implements the non-deterministic version of EdDSA (https://cfrg.github.io/draft-irtf-cfrg-det-sigs-with-noise/draft-irtf-cfrg-det-sigs-with-noise.html), + // hence these test vectors do not apply. + (isSafariOrHeadlessWebKit() ? describe.skip : describe)('Ed25519 Test Vectors from RFC8032', function () { // https://tools.ietf.org/html/rfc8032#section-7.1 function testVector(vector) { - const curve = new elliptic.CurveWithOID('ed25519'); + const curve = new elliptic.CurveWithOID(openpgp.enums.curve.ed25519Legacy); const { publicKey } = nacl.sign.keyPair.fromSeed(util.hexToUint8Array(vector.SECRET_KEY)); expect(publicKey).to.deep.equal(util.hexToUint8Array(vector.PUBLIC_KEY)); const data = vector.MESSAGE; @@ -381,7 +386,7 @@ function omnibus() { it('Omnibus Ed25519/Curve25519 Test', function() { const options = { userIDs: { name: 'Hi', email: 'hi@hel.lo' }, - curve: 'ed25519', + curve: 'ed25519Legacy', format: 'object' }; return openpgp.generateKey(options).then(async function({ privateKey, publicKey }) { @@ -394,10 +399,10 @@ function omnibus() { const hi = privateKey; const primaryKey = hi.keyPacket; const subkey = hi.subkeys[0]; - expect(hi.getAlgorithmInfo().curve).to.equal('ed25519'); - expect(hi.getAlgorithmInfo().algorithm).to.equal('eddsa'); - expect(subkey.getAlgorithmInfo().curve).to.equal('curve25519'); + expect(hi.getAlgorithmInfo().algorithm).to.equal('eddsaLegacy'); + expect(hi.getAlgorithmInfo().curve).to.equal('ed25519Legacy'); expect(subkey.getAlgorithmInfo().algorithm).to.equal('ecdh'); + expect(subkey.getAlgorithmInfo().curve).to.equal('curve25519Legacy'); // Verify that self Certificate is valid const user = hi.users[0]; @@ -409,15 +414,15 @@ function omnibus() { const options = { userIDs: { name: 'Bye', email: 'bye@good.bye' }, - curve: 'curve25519', + curve: 'curve25519Legacy', format: 'object' }; return openpgp.generateKey(options).then(async function({ privateKey: bye }) { - expect(bye.getAlgorithmInfo().curve).to.equal('ed25519'); - expect(bye.getAlgorithmInfo().algorithm).to.equal('eddsa'); - expect(bye.subkeys[0].getAlgorithmInfo().curve).to.equal('curve25519'); + expect(bye.getAlgorithmInfo().algorithm).to.equal('eddsaLegacy'); + expect(bye.getAlgorithmInfo().curve).to.equal('ed25519Legacy'); expect(bye.subkeys[0].getAlgorithmInfo().algorithm).to.equal('ecdh'); + expect(bye.subkeys[0].getAlgorithmInfo().curve).to.equal('curve25519Legacy'); // Verify that self Certificate is valid const user = bye.users[0]; diff --git a/test/initOpenpgp.js b/test/initOpenpgp.js new file mode 100644 index 00000000..4fd4a360 --- /dev/null +++ b/test/initOpenpgp.js @@ -0,0 +1,14 @@ +/** + * This module centralises the openpgp import and ensures that the module is initialised + * at the top of the test bundle, and that the config is initialised before the tests code runs (incl. that outside of `describe`). + */ + +import * as openpgp from 'openpgp'; + +if (typeof window !== 'undefined') { + window.openpgp = openpgp; +} + +openpgp.config.s2kIterationCountByte = 0; + +export default openpgp; diff --git a/test/karma.conf.js b/test/karma.conf.js deleted file mode 100644 index 25d5cf4f..00000000 --- a/test/karma.conf.js +++ /dev/null @@ -1,136 +0,0 @@ -/* eslint-disable no-process-env */ -const { chromium, firefox, webkit } = require('playwright'); - -process.env.CHROME_BIN = chromium.executablePath(); -process.env.FIREFOX_BIN = firefox.executablePath(); -process.env.WEBKIT_HEADLESS_BIN = webkit.executablePath(); - -module.exports = function(config) { - config.set({ - - // base path that will be used to resolve all patterns (eg. files, exclude) - basePath: '..', - - // hostname for local - hostname: '127.0.0.1', - - // frameworks to use - // available frameworks: https://npmjs.org/browse/keyword/karma-adapter - frameworks: ['mocha'], - - // plugins - plugins: [ - 'karma-mocha', - 'karma-chrome-launcher', - 'karma-firefox-launcher', - 'karma-webkit-launcher', - 'karma-mocha-reporter', - 'karma-browserstack-launcher' - ], - - client: { - mocha: { - timeout: 30000, - grep: process.env.LIGHTWEIGHT ? '@lightweight' : undefined - } - }, - - // list of files / patterns to load in the browser - files: [ - { - pattern: 'test/lib/unittests-bundle.js', - type: 'module' - }, - { - pattern: 'dist/**/*', - included: false - }, - { - pattern: 'test/**/*', - included: false - } - ], - - proxies: { - '/lib': '/base/test/lib', - '/worker': '/base/test/worker', - '/dist': '/base/dist' - }, - - // list of files to exclude - exclude: [ - ], - - // preprocess matching files before serving them to the browser - // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor - preprocessors: { - }, - - // test results reporter to use - // possible values: 'dots', 'progress' - // available reporters: https://npmjs.org/browse/keyword/karma-reporter - reporters: ['mocha', 'BrowserStack'], - - // web server host and port - port: 9876, - - // enable / disable colors in the output (reporters and logs) - colors: true, - - // level of logging - // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG - logLevel: config.LOG_INFO, - - // enable / disable watching file and executing tests whenever any file changes - autoWatch: false, - - browserStack: { - username: process.env.BROWSERSTACK_USERNAME, - accessKey: process.env.BROWSERSTACK_ACCESS_KEY, - build: process.env.GITHUB_SHA, - name: process.env.GITHUB_WORKFLOW, - project: `openpgpjs/${process.env.GITHUB_EVENT_NAME || 'push'}${process.env.LIGHTWEIGHT ? '/lightweight' : ''}`, - timeout: 450 - }, - - // define browsers - customLaunchers: { - bs_safari_latest: { // Webkit and Safari can differ in behavior - base: 'BrowserStack', - browser: 'Safari', - browser_version: 'latest', - os: 'OS X', - os_version: 'Ventura' - }, - bs_safari_13_1: { // no BigInt support - base: 'BrowserStack', - browser: 'Safari', - browser_version: '13.1', - os: 'OS X', - os_version: 'Catalina' - }, - bs_ios_14: { - base: 'BrowserStack', - device: 'iPhone 12', - real_mobile: true, - os: 'ios', - os_version: '14' - } - }, - - captureTimeout: 6e5, - browserDisconnectTolerance: 0, - browserDisconnectTimeout: 6e5, - browserSocketTimeout: 3e5, - browserNoActivityTimeout: 6e5, - - // start these browsers - // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher - browsers: ['ChromeHeadless', 'FirefoxHeadless', 'WebkitHeadless'], - - // Continuous Integration mode - // if true, Karma captures browsers, runs the tests and exits - singleRun: true - - }); -}; diff --git a/test/mockRandom.ts b/test/mockRandom.ts new file mode 100644 index 00000000..47f7b509 --- /dev/null +++ b/test/mockRandom.ts @@ -0,0 +1,29 @@ +import type { SinonStub } from 'sinon'; +import util from '../src/util'; + +const webcrypto = typeof crypto !== 'undefined' ? crypto : util.nodeRequire('crypto')?.webcrypto; + +type GetRandomValuesFn = typeof crypto.getRandomValues; +let original: GetRandomValuesFn | null = null; + +/** + * Mock `crypto.getRandomValues` using the mocked implementation + */ +export const mockCryptoRandomGenerator = ( + mockedImplementation: GetRandomValuesFn | SinonStub) => { + if (original !== null) { + throw new Error('random mock already initialized'); + } + + original = webcrypto.getRandomValues; + webcrypto.getRandomValues = mockedImplementation; +}; + +export const restoreCryptoRandomGenerator = () => { + if (!original) { + throw new Error('random mock was not initialized'); + } + + webcrypto.getRandomValues = original; + original = null; +}; diff --git a/test/security/index.js b/test/security/index.js index a83ecc21..6c227fc3 100644 --- a/test/security/index.js +++ b/test/security/index.js @@ -1,6 +1,11 @@ -module.exports = () => describe('Security', function () { - require('./message_signature_bypass')(); - require('./unsigned_subpackets')(); - require('./subkey_trust')(); - require('./preferred_algo_mismatch')(); +import testMessageSignatureBypess from './message_signature_bypass'; +import testUnsignedSubpackets from './unsigned_subpackets'; +import testSubkeyTrust from './subkey_trust'; +import testPreferredAlgoMismatch from './preferred_algo_mismatch'; + +export default () => describe('Security', function () { + testMessageSignatureBypess(); + testUnsignedSubpackets(); + testSubkeyTrust(); + testPreferredAlgoMismatch(); }); diff --git a/test/security/message_signature_bypass.js b/test/security/message_signature_bypass.js index 0a6276a9..614d5c05 100644 --- a/test/security/message_signature_bypass.js +++ b/test/security/message_signature_bypass.js @@ -1,8 +1,9 @@ -const { use: chaiUse, expect } = require('chai'); -chaiUse(require('chai-as-promised')); +import { use as chaiUse, expect } from 'chai'; +import chaiAsPromised from 'chai-as-promised'; // eslint-disable-line import/newline-after-import +chaiUse(chaiAsPromised); -const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../..'); -const util = require('../../src/util'); +import openpgp from '../initOpenpgp.js'; +import util from '../../src/util.js'; const { readKey, readCleartextMessage, SignaturePacket } = openpgp; @@ -101,4 +102,4 @@ async function fakeSignature() { expect(signatures).to.have.length(0); } -module.exports = () => it('Does not accept non-binary/text signatures', fakeSignature); +export default () => it('Does not accept non-binary/text signatures', fakeSignature); diff --git a/test/security/preferred_algo_mismatch.js b/test/security/preferred_algo_mismatch.js index e53c710e..c000d9d7 100644 --- a/test/security/preferred_algo_mismatch.js +++ b/test/security/preferred_algo_mismatch.js @@ -1,7 +1,8 @@ -const { use: chaiUse, expect } = require('chai'); -chaiUse(require('chai-as-promised')); +import { use as chaiUse, expect } from 'chai'; +import chaiAsPromised from 'chai-as-promised'; // eslint-disable-line import/newline-after-import +chaiUse(chaiAsPromised); -const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../..'); +import openpgp from '../initOpenpgp.js'; const armoredMessage = `-----BEGIN PGP MESSAGE----- Version: OpenPGP.js VERSION @@ -38,7 +39,7 @@ EnxUPL95HuMKoVkf4w== =oopr -----END PGP PRIVATE KEY BLOCK-----`; -module.exports = () => it('Does not accept message encrypted with algo not mentioned in preferred algorithms', async function() { +export default () => it('Does not accept message encrypted with algo not mentioned in preferred algorithms', async function() { const message = await openpgp.readMessage({ armoredMessage }); const privKey = await openpgp.readKey({ armoredKey: privateKeyArmor }); await expect(openpgp.decrypt({ message, decryptionKeys: [privKey] })).to.be.rejectedWith('A non-preferred symmetric algorithm was used.'); diff --git a/test/security/subkey_trust.js b/test/security/subkey_trust.js index 3aec7588..5d7f5fbe 100644 --- a/test/security/subkey_trust.js +++ b/test/security/subkey_trust.js @@ -1,7 +1,8 @@ -const { use: chaiUse, expect } = require('chai'); -chaiUse(require('chai-as-promised')); +import { use as chaiUse, expect } from 'chai'; +import chaiAsPromised from 'chai-as-promised'; // eslint-disable-line import/newline-after-import +chaiUse(chaiAsPromised); -const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../..'); +import openpgp from '../initOpenpgp.js'; const { readKey, PublicKey, readCleartextMessage, createCleartextMessage, enums, PacketList, SignaturePacket } = openpgp; @@ -33,7 +34,7 @@ async function generateTestData() { }; } -module.exports = () => it('Does not trust subkeys without Primary Key Binding Signature', async function() { +export default () => it('Does not trust subkeys without Primary Key Binding Signature', async function() { // attacker only has his own private key, // the victim's public key and a signed message const { victimPubKey, attackerPrivKey, signed } = await generateTestData(); @@ -49,7 +50,7 @@ module.exports = () => it('Does not trust subkeys without Primary Key Binding Si fakeBindingSignature.publicKeyAlgorithm = attackerPrivKey.keyPacket.algorithm; fakeBindingSignature.hashAlgorithm = enums.hash.sha256; fakeBindingSignature.keyFlags = [enums.keyFlags.signData]; - await fakeBindingSignature.sign(attackerPrivKey.keyPacket, dataToSign); + await fakeBindingSignature.sign(attackerPrivKey.keyPacket, dataToSign, undefined, undefined, openpgp.config); const newList = new PacketList(); newList.push( pktPubAttacker[0], // attacker private key diff --git a/test/security/unsigned_subpackets.js b/test/security/unsigned_subpackets.js index cc559bc4..82721103 100644 --- a/test/security/unsigned_subpackets.js +++ b/test/security/unsigned_subpackets.js @@ -1,7 +1,8 @@ -const { use: chaiUse, expect } = require('chai'); -chaiUse(require('chai-as-promised')); +import { use as chaiUse, expect } from 'chai'; +import chaiAsPromised from 'chai-as-promised'; // eslint-disable-line import/newline-after-import +chaiUse(chaiAsPromised); -const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../..'); +import openpgp from '../initOpenpgp.js'; const { readKey, PrivateKey, createMessage, enums, PacketList, SignaturePacket } = openpgp; @@ -89,4 +90,4 @@ async function makeKeyValid() { expect(await encryptFails(modifiedkey)).to.be.true; } -module.exports = () => it('Does not accept unsigned subpackets', makeKeyValid); +export default () => it('Does not accept unsigned subpackets', makeKeyValid); diff --git a/test/typescript/definitions.ts b/test/typescript/definitions.ts index 5d61f9c6..5258154d 100644 --- a/test/typescript/definitions.ts +++ b/test/typescript/definitions.ts @@ -7,6 +7,7 @@ */ import { ReadableStream as WebReadableStream } from 'web-streams-polyfill'; import { createReadStream } from 'fs'; +import { Readable as NodeNativeReadableStream } from 'stream'; import { expect } from 'chai'; import { @@ -15,13 +16,13 @@ import { encrypt, decrypt, sign, verify, config, enums, generateSessionKey, encryptSessionKey, decryptSessionKeys, LiteralDataPacket, PacketList, CompressedDataPacket, PublicKeyPacket, PublicSubkeyPacket, SecretKeyPacket, SecretSubkeyPacket, CleartextMessage, - WebStream, NodeStream, -} from '../..'; + WebStream, NodeWebStream, +} from 'openpgp'; (async () => { // Generate keys - const keyOptions = { userIDs: [{ email: 'user@corp.co' }], config: { v5Keys: true } }; + const keyOptions = { userIDs: [{ email: 'user@corp.co' }], config: { v6Keys: true } }; const { privateKey: privateKeyArmored, publicKey: publicKeyArmored } = await generateKey(keyOptions); const { privateKey: privateKeyBinary } = await generateKey({ ...keyOptions, format: 'binary' }); const { privateKey, publicKey, revocationCertificate } = await generateKey({ ...keyOptions, format: 'object' }); @@ -207,9 +208,9 @@ import { // Streaming - encrypt text message (armored output) try { - const nodeTextStream = createReadStream('non-existent-file', { encoding: 'utf8' }); + const nodeTextStream = NodeNativeReadableStream.toWeb(createReadStream('non-existent-file', { encoding: 'utf8' })); const messageFromNodeTextStream = await createMessage({ text: nodeTextStream }); - (await encrypt({ message: messageFromNodeTextStream, passwords: 'password', format: 'armored' })) as NodeStream; + (await encrypt({ message: messageFromNodeTextStream, passwords: 'password', format: 'armored' })) as NodeWebStream; } catch (err) {} const webTextStream = new WebReadableStream(); const messageFromWebTextStream = await createMessage({ text: webTextStream }); @@ -219,9 +220,9 @@ import { // Streaming - encrypt binary message (binary output) try { - const nodeBinaryStream = createReadStream('non-existent-file'); + const nodeBinaryStream = NodeNativeReadableStream.toWeb(createReadStream('non-existent-file')); const messageFromNodeBinaryStream = await createMessage({ binary: nodeBinaryStream }); - (await encrypt({ message: messageFromNodeBinaryStream, passwords: 'password', format: 'binary' })) as NodeStream; + (await encrypt({ message: messageFromNodeBinaryStream, passwords: 'password', format: 'binary' })) as NodeWebStream; } catch (err) {} const webBinaryStream = new WebReadableStream(); const messageFromWebBinaryStream = await createMessage({ binary: webBinaryStream }); diff --git a/test/unittests.html b/test/unittests.html index aedc5735..1e35aa4b 100644 --- a/test/unittests.html +++ b/test/unittests.html @@ -3,38 +3,38 @@ OpenPGPJS Unit Tests - - - - - +
diff --git a/test/unittests.js b/test/unittests.js index b8e43a1e..74ef06ad 100644 --- a/test/unittests.js +++ b/test/unittests.js @@ -1,16 +1,16 @@ -const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('..'); +import openpgp from './initOpenpgp.js'; (typeof window !== 'undefined' ? window : global).globalThis = (typeof window !== 'undefined' ? window : global); -(typeof window !== 'undefined' ? window : global).resolves = function(val) { +globalThis.resolves = function(val) { return new Promise(function(res) { res(val); }); }; -(typeof window !== 'undefined' ? window : global).rejects = function(val) { +globalThis.rejects = function(val) { return new Promise(function(res, rej) { rej(val); }); }; -(typeof window !== 'undefined' ? window : global).tryTests = function(name, tests, options) { +globalThis.tryTests = function(name, tests, options) { if (options.if) { describe(name, function() { if (options.before) { before(options.before); } @@ -26,10 +26,19 @@ const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp } }; +globalThis.loadStreamsPolyfill = function() { + // do not polyfill Node + const detectNodeWebStreams = () => typeof globalThis.process === 'object' && typeof globalThis.process.versions === 'object' && globalThis.ReadableStream; + + return detectNodeWebStreams() || import('web-streams-polyfill/polyfill'); +}; + +import runWorkerTests from './worker'; +import runCryptoTests from './crypto'; +import runGeneralTests from './general'; +import runSecurityTests from './security'; + describe('Unit Tests', function () { - - openpgp.config.s2kIterationCountByte = 0; - if (typeof window !== 'undefined') { // Safari 14.1.*, 15.* and 16.* seem to have issues handling rejections when their native TransformStream implementation is involved, // so for now we ignore unhandled rejections for those browser versions. @@ -55,8 +64,8 @@ describe('Unit Tests', function () { }); } - require('./worker')(); - require('./crypto')(); - require('./general')(); - require('./security')(); + runWorkerTests(); + runCryptoTests(); + runGeneralTests(); + runSecurityTests(); }); diff --git a/test/web-test-runner.browserstack.config.js b/test/web-test-runner.browserstack.config.js new file mode 100644 index 00000000..966c4840 --- /dev/null +++ b/test/web-test-runner.browserstack.config.js @@ -0,0 +1,49 @@ +import { browserstackLauncher } from '@web/test-runner-browserstack'; +import wtrConfig from './web-test-runner.config.js'; + +const sharedBrowserstackCapabilities = { + 'browserstack.user': process.env.BROWSERSTACK_USERNAME, + 'browserstack.key': process.env.BROWSERSTACK_ACCESS_KEY, + + project: `openpgpjs/${process.env.GITHUB_EVENT_NAME || 'push'}${process.env.LIGHTWEIGHT ? '/lightweight' : ''}@${process.env.GITHUB_REF_NAME}`, + name: process.env.GITHUB_WORKFLOW || 'local', + build: process.env.GITHUB_SHA || 'local', + 'browserstack.acceptInsecureCerts': true +}; + +export default { + ...wtrConfig, + protocol: 'https:', + http2: true, + sslKey: './127.0.0.1-key.pem', + sslCert: './127.0.0.1.pem', + testsStartTimeout: 45000, + browserStartTimeout: 120000, + testsFinishTimeout: 450000, + concurrentBrowsers: 3, + concurrency: 1, // see https://github.com/modernweb-dev/web/issues/2706 + coverage: false, + groups: [], // overwrite the field coming from `wrtConfig` + browsers: [ + browserstackLauncher({ + capabilities: { + ...sharedBrowserstackCapabilities, + browserName: 'Safari iOS 14', + device: 'iPhone 12', + real_mobile: true, + os: 'ios', + os_version: '14' // min supported version (iOS/Safari < 14 does not support native BigInts) + } + }), + browserstackLauncher({ + capabilities: { + ...sharedBrowserstackCapabilities, + browserName: 'Safari iOS latest', + device: 'iPhone 16', + real_mobile: true, + os: 'ios', + os_version: 'latest' + } + }) + ] +}; diff --git a/test/web-test-runner.config.js b/test/web-test-runner.config.js new file mode 100644 index 00000000..45e18856 --- /dev/null +++ b/test/web-test-runner.config.js @@ -0,0 +1,39 @@ +import { playwrightLauncher } from '@web/test-runner-playwright'; + +const sharedPlaywrightCIOptions = { + // createBrowserContext: ({ browser }) => browser.newContext({ ignoreHTTPSErrors: true }), + headless: true +}; + +export default { + nodeResolve: true, // to resolve npm module imports in `unittests.html` + files: './test/unittests.html', + protocol: 'http:', + hostname: '127.0.0.1', + testsStartTimeout: 45000, + browserStartTimeout: 120000, + testsFinishTimeout: 450000, + concurrentBrowsers: 3, + concurrency: 1, // see https://github.com/modernweb-dev/web/issues/2706 + coverage: false, + groups: [ + { name: 'local' }, // group meant to be used with either --browser or --manual options via CLI + { + name: 'headless:ci', + browsers: [ + playwrightLauncher({ + ...sharedPlaywrightCIOptions, + product: 'chromium' + }), + playwrightLauncher({ + ...sharedPlaywrightCIOptions, + product: 'firefox' + }), + playwrightLauncher({ + ...sharedPlaywrightCIOptions, + product: 'webkit' + }) + ] + } + ] +}; diff --git a/test/worker/application_worker.js b/test/worker/application_worker.js index 0d096613..1ffdc558 100644 --- a/test/worker/application_worker.js +++ b/test/worker/application_worker.js @@ -1,9 +1,8 @@ -/* globals tryTests: true */ +/* globals tryTests */ -const { expect } = require('chai'); +import { expect } from 'chai'; -/* eslint-disable no-invalid-this */ -module.exports = () => tryTests('Application Worker', tests, { +export default () => tryTests('Application Worker', tests, { if: typeof window !== 'undefined' && window.Worker && window.MessageChannel }); diff --git a/test/worker/index.js b/test/worker/index.js index 8d1b8b72..2654cac6 100644 --- a/test/worker/index.js +++ b/test/worker/index.js @@ -1,4 +1,6 @@ -module.exports = () => describe('Web Worker', function () { - require('./application_worker')(); +import testApplicationWorker from './application_worker.js'; + +export default () => describe('Web Worker', function () { + testApplicationWorker(); }); diff --git a/test/worker/worker_example.js b/test/worker/worker_example.js index b5b76341..fbd2cee1 100644 --- a/test/worker/worker_example.js +++ b/test/worker/worker_example.js @@ -1,4 +1,4 @@ -/* globals openpgp: true */ +/* globals openpgp */ importScripts('../../dist/openpgp.js'); diff --git a/tsconfig.json b/tsconfig.json index 60f1e065..1b8fdcb0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,8 +1,12 @@ { - "exclude": [ - "./build/" - ], "compilerOptions": { - "strict": true + "strict": true, + "target": "es2021", + "module": "esnext", + "moduleResolution": "Bundler", + "allowJs": true, + "paths": { + "openpgp": [ "." ] + }, } }