diff --git a/.config/dictionaries/project.dic b/.config/dictionaries/project.dic index 8077b8af53c..240f058bee4 100644 --- a/.config/dictionaries/project.dic +++ b/.config/dictionaries/project.dic @@ -91,6 +91,7 @@ HIDPI icudtl ideascale idents +ilap Instantitation integ Intellij @@ -255,4 +256,14 @@ xcworkspace yoroi unchunk EUTXO +eddsa toggleable +interps +todos +vsync +damian-molinski +LTRB +hotspots +precache +Precache +svgs diff --git a/.github/workflows/build-flutter-web.yml b/.github/workflows/build-flutter-web.yml index b4d009db2b2..c7bfff78407 100644 --- a/.github/workflows/build-flutter-web.yml +++ b/.github/workflows/build-flutter-web.yml @@ -42,7 +42,7 @@ jobs: earthfile: ./catalyst_voices/ flags: --allow-privileged targets: build-web - target_flags: --SECRETS_ARE_AVAILABLE=true, --SENTRY_DSN=${{ secrets.SENTRY_DSN }} + target_flags: --RUN_ON_PR=false --SENTRY_DSN=${{ secrets.SENTRY_DSN }} runner_address: ${{ secrets.EARTHLY_SATELLITE_ADDRESS }} artifact: "true" @@ -54,7 +54,6 @@ jobs: earthfile: ./catalyst_voices/ flags: --allow-privileged targets: package - target_flags: --SECRETS_ARE_AVAILABLE=true runner_address: ${{ secrets.EARTHLY_SATELLITE_ADDRESS }} artifact: "true" @@ -66,6 +65,5 @@ jobs: earthfile: ./catalyst_voices/ flags: --allow-privileged targets: publish - target_flags: --SECRETS_ARE_AVAILABLE=true runner_address: ${{ secrets.EARTHLY_SATELLITE_ADDRESS }} artifact: "true" diff --git a/.github/workflows/flutter-publish-packages.yml b/.github/workflows/flutter-publish-packages.yml index a55c407777b..7515393f89a 100644 --- a/.github/workflows/flutter-publish-packages.yml +++ b/.github/workflows/flutter-publish-packages.yml @@ -20,7 +20,7 @@ jobs: - uses: subosito/flutter-action@v2 with: channel: stable - flutter-version: 3.22.1 + flutter-version: 3.24.1 - uses: bluefireteam/melos-action@v3 with: run-versioning: true diff --git a/.github/workflows/stale-branches.yml b/.github/workflows/stale-branches.yml index db9ad2f31f3..06c8a6f2b7c 100644 --- a/.github/workflows/stale-branches.yml +++ b/.github/workflows/stale-branches.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Stale Branches - uses: crs-k/stale-branches@v5.0.2 + uses: crs-k/stale-branches@v6.0.2 with: repo-token: '${{ secrets.GITHUB_TOKEN }}' days-before-stale: 30 diff --git a/CHANGELOG.md b/CHANGELOG.md index d80953ac3a1..a1f6cda18bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,165 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## 2024-09-03 + +### Changes + +--- + +Packages with breaking changes: + + - [`catalyst_analysis` - `v2.0.0`](#catalyst_analysis---v200) + - [`catalyst_cardano` - `v0.3.0`](#catalyst_cardano---v030) + - [`catalyst_cardano_platform_interface` - `v0.3.0`](#catalyst_cardano_platform_interface---v030) + - [`catalyst_cardano_serialization` - `v0.4.0`](#catalyst_cardano_serialization---v040) + - [`catalyst_cardano_web` - `v0.3.0`](#catalyst_cardano_web---v030) + - [`catalyst_compression` - `v0.3.0`](#catalyst_compression---v030) + - [`catalyst_compression_platform_interface` - `v0.2.0`](#catalyst_compression_platform_interface---v020) + - [`catalyst_compression_web` - `v0.3.0`](#catalyst_compression_web---v030) + - [`catalyst_cose` - `v0.3.0`](#catalyst_cose---v030) + +Packages with other changes: + + - There are no other changes in this release. + +--- + +#### `catalyst_analysis` - `v2.0.0` + + - **BREAKING** **CHORE**: upgrade flutter to 3.24.1 and dart to 3.5.0 ([#725](https://github.com/input-output-hk/catalyst-voices/issues/725)). ([eb8a516e](https://github.com/input-output-hk/catalyst-voices/commit/eb8a516edbd25386c0fbe41501285870abf82543)) + +#### `catalyst_cardano` - `v0.3.0` + + - **FIX**: catalyst cardano null utxos ([#746](https://github.com/input-output-hk/catalyst-voices/issues/746)). ([3f2f5925](https://github.com/input-output-hk/catalyst-voices/commit/3f2f592593efe306f85fb0e81ce07aa1ea90b7b6)) + - **FIX**: rbac txn inputs hash ([#688](https://github.com/input-output-hk/catalyst-voices/issues/688)). ([b644026f](https://github.com/input-output-hk/catalyst-voices/commit/b644026fa3b675591d071819eda185365257f0d1)) + - **BREAKING** **FEAT**: add initial support for Cardano Native scripts, Plutus scripts, advanced transaction outputs, and additional transaction body fields and witnesses ([#713](https://github.com/input-output-hk/catalyst-voices/issues/713)). ([74fcb725](https://github.com/input-output-hk/catalyst-voices/commit/74fcb725f221bb3acf3824a3dd18a073d0a321e0)) + - **BREAKING** **CHORE**: upgrade flutter to 3.24.1 and dart to 3.5.0 ([#725](https://github.com/input-output-hk/catalyst-voices/issues/725)). ([eb8a516e](https://github.com/input-output-hk/catalyst-voices/commit/eb8a516edbd25386c0fbe41501285870abf82543)) + +#### `catalyst_cardano_platform_interface` - `v0.3.0` + + - **BREAKING** **CHORE**: upgrade flutter to 3.24.1 and dart to 3.5.0 ([#725](https://github.com/input-output-hk/catalyst-voices/issues/725)). ([eb8a516e](https://github.com/input-output-hk/catalyst-voices/commit/eb8a516edbd25386c0fbe41501285870abf82543)) + +#### `catalyst_cardano_serialization` - `v0.4.0` + + - **FIX**: to be signed message should be a plain cbor sequence ([#701](https://github.com/input-output-hk/catalyst-voices/issues/701)). ([7c2dec6e](https://github.com/input-output-hk/catalyst-voices/commit/7c2dec6e2f91c1f18a39e7646ee3a5ca6a6e7249)) + - **FIX**: rbac txn inputs hash ([#688](https://github.com/input-output-hk/catalyst-voices/issues/688)). ([b644026f](https://github.com/input-output-hk/catalyst-voices/commit/b644026fa3b675591d071819eda185365257f0d1)) + - **FEAT**(catalyst_cardano_serialization): add CborEncodable interface for standardized CBOR handling ([#696](https://github.com/input-output-hk/catalyst-voices/issues/696)). ([4222926f](https://github.com/input-output-hk/catalyst-voices/commit/4222926f028460ddb100008806fe39a38ac3511c)) + - **BREAKING** **FIX**: required signers are the hash of the public key, not the public key itself ([#703](https://github.com/input-output-hk/catalyst-voices/issues/703)). ([a63c4686](https://github.com/input-output-hk/catalyst-voices/commit/a63c4686ee6e79aa65ace0cb4ed9b0c91e994320)) + - **BREAKING** **FEAT**: add initial support for Cardano Native scripts, Plutus scripts, advanced transaction outputs, and additional transaction body fields and witnesses ([#713](https://github.com/input-output-hk/catalyst-voices/issues/713)). ([74fcb725](https://github.com/input-output-hk/catalyst-voices/commit/74fcb725f221bb3acf3824a3dd18a073d0a321e0)) + - **BREAKING** **CHORE**: upgrade flutter to 3.24.1 and dart to 3.5.0 ([#725](https://github.com/input-output-hk/catalyst-voices/issues/725)). ([eb8a516e](https://github.com/input-output-hk/catalyst-voices/commit/eb8a516edbd25386c0fbe41501285870abf82543)) + +#### `catalyst_cardano_web` - `v0.3.0` + + - **FIX**: catalyst cardano null utxos ([#746](https://github.com/input-output-hk/catalyst-voices/issues/746)). ([3f2f5925](https://github.com/input-output-hk/catalyst-voices/commit/3f2f592593efe306f85fb0e81ce07aa1ea90b7b6)) + - **BREAKING** **CHORE**: upgrade flutter to 3.24.1 and dart to 3.5.0 ([#725](https://github.com/input-output-hk/catalyst-voices/issues/725)). ([eb8a516e](https://github.com/input-output-hk/catalyst-voices/commit/eb8a516edbd25386c0fbe41501285870abf82543)) + +#### `catalyst_compression` - `v0.3.0` + + - **BREAKING** **CHORE**: upgrade flutter to 3.24.1 and dart to 3.5.0 ([#725](https://github.com/input-output-hk/catalyst-voices/issues/725)). ([eb8a516e](https://github.com/input-output-hk/catalyst-voices/commit/eb8a516edbd25386c0fbe41501285870abf82543)) + +#### `catalyst_compression_platform_interface` - `v0.2.0` + + - **BREAKING** **CHORE**: upgrade flutter to 3.24.1 and dart to 3.5.0 ([#725](https://github.com/input-output-hk/catalyst-voices/issues/725)). ([eb8a516e](https://github.com/input-output-hk/catalyst-voices/commit/eb8a516edbd25386c0fbe41501285870abf82543)) + +#### `catalyst_compression_web` - `v0.3.0` + + - **BREAKING** **CHORE**: upgrade flutter to 3.24.1 and dart to 3.5.0 ([#725](https://github.com/input-output-hk/catalyst-voices/issues/725)). ([eb8a516e](https://github.com/input-output-hk/catalyst-voices/commit/eb8a516edbd25386c0fbe41501285870abf82543)) + +#### `catalyst_cose` - `v0.3.0` + + - **BREAKING** **CHORE**: upgrade flutter to 3.24.1 and dart to 3.5.0 ([#725](https://github.com/input-output-hk/catalyst-voices/issues/725)). ([eb8a516e](https://github.com/input-output-hk/catalyst-voices/commit/eb8a516edbd25386c0fbe41501285870abf82543)) + + +## 2024-08-13 + +### Changes + +--- + +Packages with breaking changes: + + - [`catalyst_cardano_serialization` - `v0.3.0`](#catalyst_cardano_serialization---v030) + +Packages with other changes: + + - [`catalyst_cardano_web` - `v0.2.0+1`](#catalyst_cardano_web---v0201) + - [`catalyst_cardano` - `v0.2.0+1`](#catalyst_cardano---v0201) + - [`catalyst_cardano_platform_interface` - `v0.2.0+1`](#catalyst_cardano_platform_interface---v0201) + +Packages with dependency updates only: + +> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project. + + - `catalyst_cardano_web` - `v0.2.0+1` + - `catalyst_cardano` - `v0.2.0+1` + - `catalyst_cardano_platform_interface` - `v0.2.0+1` + +--- + +#### `catalyst_cardano_serialization` - `v0.3.0` + + - **BREAKING** **FIX**: cardano serialization must depend on flutter ([#679](https://github.com/input-output-hk/catalyst-voices/issues/679)). ([b7d5276b](https://github.com/input-output-hk/catalyst-voices/commit/b7d5276b238b4c7273997b004465e2ffb29f8436)) + + +## 2024-08-12 + +### Changes + +--- + +Packages with breaking changes: + + - [`catalyst_cardano` - `v0.2.0`](#catalyst_cardano---v020) + - [`catalyst_cardano_platform_interface` - `v0.2.0`](#catalyst_cardano_platform_interface---v020) + - [`catalyst_cardano_serialization` - `v0.2.0`](#catalyst_cardano_serialization---v020) + - [`catalyst_cardano_web` - `v0.2.0`](#catalyst_cardano_web---v020) + - [`catalyst_compression` - `v0.2.0`](#catalyst_compression---v020) + - [`catalyst_compression_web` - `v0.2.0`](#catalyst_compression_web---v020) + - [`catalyst_cose` - `v0.2.0`](#catalyst_cose---v020) + +Packages with other changes: + + - There are no other changes in this release. + +--- + +#### `catalyst_cardano` - `v0.2.0` + + - **BREAKING** **FIX**: signData return type should be DataSignature, not VkeyWitness ([#647](https://github.com/input-output-hk/catalyst-voices/issues/647)). ([69dba1d2](https://github.com/input-output-hk/catalyst-voices/commit/69dba1d24022eb77cc03ac670dda0da047304766)) + - **BREAKING** **FIX**: X509 registration metadata encoding ([#640](https://github.com/input-output-hk/catalyst-voices/issues/640)). ([c45a2ac9](https://github.com/input-output-hk/catalyst-voices/commit/c45a2ac96b34c4215352ece5ef9bd2fa73b591e8)) + - **BREAKING** **FEAT**: COSE_SIGN1 signatures and verification ([#669](https://github.com/input-output-hk/catalyst-voices/issues/669)). ([f5a910ef](https://github.com/input-output-hk/catalyst-voices/commit/f5a910efe36442171521b9ec429aed4a46e05b83)) + +#### `catalyst_cardano_platform_interface` - `v0.2.0` + + - **BREAKING** **FIX**: signData return type should be DataSignature, not VkeyWitness ([#647](https://github.com/input-output-hk/catalyst-voices/issues/647)). ([69dba1d2](https://github.com/input-output-hk/catalyst-voices/commit/69dba1d24022eb77cc03ac670dda0da047304766)) + +#### `catalyst_cardano_serialization` - `v0.2.0` + + - **FEAT**: add catv1 auth token generator ([#671](https://github.com/input-output-hk/catalyst-voices/issues/671)). ([79efc828](https://github.com/input-output-hk/catalyst-voices/commit/79efc82800a7e6aca3e8516bbb4866bd502e2f36)) + - **BREAKING** **FIX**: X509 registration metadata encoding ([#640](https://github.com/input-output-hk/catalyst-voices/issues/640)). ([c45a2ac9](https://github.com/input-output-hk/catalyst-voices/commit/c45a2ac96b34c4215352ece5ef9bd2fa73b591e8)) + - **BREAKING** **FEAT**: update transactions inputs hash size ([#643](https://github.com/input-output-hk/catalyst-voices/issues/643)). ([a729823d](https://github.com/input-output-hk/catalyst-voices/commit/a729823d9b2e0c369456f8e99f5b776f046e6d1f)) + +#### `catalyst_cardano_web` - `v0.2.0` + + - **BREAKING** **FIX**: signData return type should be DataSignature, not VkeyWitness ([#647](https://github.com/input-output-hk/catalyst-voices/issues/647)). ([69dba1d2](https://github.com/input-output-hk/catalyst-voices/commit/69dba1d24022eb77cc03ac670dda0da047304766)) + - **BREAKING** **FEAT**: COSE_SIGN1 signatures and verification ([#669](https://github.com/input-output-hk/catalyst-voices/issues/669)). ([f5a910ef](https://github.com/input-output-hk/catalyst-voices/commit/f5a910efe36442171521b9ec429aed4a46e05b83)) + +#### `catalyst_compression` - `v0.2.0` + + - **BREAKING** **FEAT**: COSE_SIGN1 signatures and verification ([#669](https://github.com/input-output-hk/catalyst-voices/issues/669)). ([f5a910ef](https://github.com/input-output-hk/catalyst-voices/commit/f5a910efe36442171521b9ec429aed4a46e05b83)) + +#### `catalyst_compression_web` - `v0.2.0` + + - **FEAT**: cose flutter package structure ([#649](https://github.com/input-output-hk/catalyst-voices/issues/649)). ([1875849c](https://github.com/input-output-hk/catalyst-voices/commit/1875849c530babbd69dce3882423cc7d9ffdbfa4)) + - **BREAKING** **FEAT**: COSE_SIGN1 signatures and verification ([#669](https://github.com/input-output-hk/catalyst-voices/issues/669)). ([f5a910ef](https://github.com/input-output-hk/catalyst-voices/commit/f5a910efe36442171521b9ec429aed4a46e05b83)) + +#### `catalyst_cose` - `v0.2.0` + + - **FEAT**: cose flutter package structure ([#649](https://github.com/input-output-hk/catalyst-voices/issues/649)). ([1875849c](https://github.com/input-output-hk/catalyst-voices/commit/1875849c530babbd69dce3882423cc7d9ffdbfa4)) + - **BREAKING** **FEAT**: COSE_SIGN1 signatures and verification ([#669](https://github.com/input-output-hk/catalyst-voices/issues/669)). ([f5a910ef](https://github.com/input-output-hk/catalyst-voices/commit/f5a910efe36442171521b9ec429aed4a46e05b83)) + + ## 2024-07-24 ### Changes diff --git a/catalyst-gateway-crates/c509-certificate/.cargo/config.toml b/catalyst-gateway-crates/c509-certificate/.cargo/config.toml deleted file mode 100644 index 2764f1df4e2..00000000000 --- a/catalyst-gateway-crates/c509-certificate/.cargo/config.toml +++ /dev/null @@ -1,93 +0,0 @@ -# Use MOLD linker where possible, but ONLY in CI applicable targets. - -# Configure how Docker container targets build. - -# If you want to customize these targets for a local build, then customize them in your: -# $CARGO_HOME/config.toml -# NOT in the project itself. -# These targets are ONLY the targets used by CI and inside docker builds. - -# DO NOT remove `"-C", "target-feature=+crt-static"` from the rustflags for these targets. - -# Should be the default to have fully static rust programs in CI -[target.x86_64-unknown-linux-musl] -linker = "clang" -rustflags = [ - "-C", "link-arg=-fuse-ld=/usr/bin/mold", - "-C", "target-feature=-crt-static" -] - -# Should be the default to have fully static rust programs in CI -[target.aarch64-unknown-linux-musl] -linker = "clang" -rustflags = [ - "-C", "link-arg=-fuse-ld=/usr/bin/mold", - "-C", "target-feature=-crt-static" -] - -[build] -rustflags = [] -rustdocflags = [ - "--enable-index-page", - "-Z", - "unstable-options", -] - -[profile.dev] -opt-level = 1 -debug = true -debug-assertions = true -overflow-checks = true -lto = false -panic = "unwind" -incremental = true -codegen-units = 256 - -[profile.release] -opt-level = 3 -debug = false -debug-assertions = false -overflow-checks = false -lto = "thin" -panic = "unwind" -incremental = false -codegen-units = 16 - -[profile.test] -opt-level = 3 -debug = true -lto = false -debug-assertions = true -incremental = true -codegen-units = 256 - -[profile.bench] -opt-level = 3 -debug = false -debug-assertions = false -overflow-checks = false -lto = "thin" -incremental = false -codegen-units = 16 - -[alias] -lint = "clippy --all-targets" -lintfix = "clippy --all-targets --fix --allow-dirty" -lint-vscode = "clippy --message-format=json-diagnostic-rendered-ansi --all-targets" - -docs = "doc --release --no-deps --document-private-items --bins --lib --examples" -# nightly docs build broken... when they are'nt we can enable these docs... --unit-graph --timings=html,json -Z unstable-options" -testunit = "nextest run --release --bins --lib --tests --benches --no-fail-fast -P ci" -testcov = "llvm-cov nextest --release --bins --lib --tests --benches --no-fail-fast -P ci" -testdocs = "test --doc --release" - -# Rust formatting, MUST be run with +nightly -fmtchk = "fmt -- --check -v --color=always" -fmtfix = "fmt -- -v" - -[term] -quiet = false # whether cargo output is quiet -verbose = false # whether cargo provides verbose output -color = "auto" # whether cargo colorizes output use `CARGO_TERM_COLOR="off"` to disable. -progress.when = "never" # whether cargo shows progress bar -progress.width = 80 # width of progress bar \ No newline at end of file diff --git a/catalyst-gateway-crates/c509-certificate/.config/nextest.toml b/catalyst-gateway-crates/c509-certificate/.config/nextest.toml deleted file mode 100644 index be3673830bb..00000000000 --- a/catalyst-gateway-crates/c509-certificate/.config/nextest.toml +++ /dev/null @@ -1,49 +0,0 @@ -# cspell: words scrollability testcase -[store] -# The directory under the workspace root at which nextest-related files are -# written. Profile-specific storage is currently written to dir/. -# dir = "target/nextest" - -[profile.default] -# Print out output for failing tests as soon as they fail, and also at the end -# of the run (for easy scrollability). -failure-output = "immediate-final" - -# Do not cancel the test run on the first failure. -fail-fast = true - -status-level = "all" -final-status-level = "all" - -[profile.ci] -# Print out output for failing tests as soon as they fail, and also at the end -# of the run (for easy scrollability). -failure-output = "immediate-final" -# Do not cancel the test run on the first failure. -fail-fast = false - -status-level = "all" -final-status-level = "all" - - -[profile.ci.junit] -# Output a JUnit report into the given file inside 'store.dir/'. -# If unspecified, JUnit is not written out. - -path = "junit.xml" - -# The name of the top-level "report" element in JUnit report. If aggregating -# reports across different test runs, it may be useful to provide separate names -# for each report. -report-name = "nextest" - -# Whether standard output and standard error for passing tests should be stored in the JUnit report. -# Output is stored in the and elements of the element. -store-success-output = true - -# Whether standard output and standard error for failing tests should be stored in the JUnit report. -# Output is stored in the and elements of the element. -# -# Note that if a description can be extracted from the output, it is always stored in the -# element. -store-failure-output = true diff --git a/catalyst-gateway-crates/c509-certificate/.gitignore b/catalyst-gateway-crates/c509-certificate/.gitignore deleted file mode 100644 index c1b3e696893..00000000000 --- a/catalyst-gateway-crates/c509-certificate/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -### Rust ### -# Generated by Cargo -# will have compiled files and executables -debug/ -target/ - -# These are backup files generated by rustfmt -**/*.rs.bk - -# MSVC Windows builds of rustc generate these, which store debugging information -*.pdb \ No newline at end of file diff --git a/catalyst-gateway-crates/c509-certificate/Cargo.toml b/catalyst-gateway-crates/c509-certificate/Cargo.toml deleted file mode 100644 index 935b29235d2..00000000000 --- a/catalyst-gateway-crates/c509-certificate/Cargo.toml +++ /dev/null @@ -1,80 +0,0 @@ -[package] -name = "c509-certificate" -description = "C509 certificate implementation" -keywords = ["cardano", "catalyst", "c509 certificate", "certificate", "x509"] -version = "0.0.1" -authors = [ - "Arissara Chotivichit " -] -homepage = "https://input-output-hk.github.io/catalyst-voices" -repository = "https://github.com/input-output-hk/catalyst-voices" -license = "MIT OR Apache-2.0" -edition = "2021" - -[lib] -crate-type = ["cdylib", "rlib"] - -[lints.rust] -warnings = "deny" -missing_docs = "deny" -let_underscore_drop = "deny" -non_ascii_idents = "deny" -single_use_lifetimes = "deny" -trivial_casts = "deny" -trivial_numeric_casts = "deny" - -[lints.rustdoc] -broken_intra_doc_links = "deny" -invalid_codeblock_attributes = "deny" -invalid_html_tags = "deny" -invalid_rust_codeblocks = "deny" -bare_urls = "deny" -unescaped_backticks = "deny" - -[lints.clippy] -pedantic = { level = "deny", priority = -1 } -unwrap_used = "deny" -expect_used = "deny" -exit = "deny" -get_unwrap = "deny" -index_refutable_slice = "deny" -indexing_slicing = "deny" -match_on_vec_items = "deny" -match_wild_err_arm = "deny" -missing_panics_doc = "deny" -panic = "deny" -string_slice = "deny" -unchecked_duration_subtraction = "deny" -unreachable = "deny" -missing_docs_in_private_items = "deny" - -[dependencies] -minicbor = { version = "0.24", features = ["std"] } -hex = "0.4.3" -oid = "0.2.1" -oid-registry = "0.7.0" -asn1-rs = "0.6.0" -anyhow = "1.0.86" -bimap = "0.6.3" -once_cell = "1.19.0" -strum = "0.26.3" -strum_macros = "0.26.3" -regex = "1.10.5" -ed25519-dalek = { version = "2.1.1", features = ["pem"] } -thiserror = "1.0.56" -serde = { version = "1.0.204", features = ["derive"] } -wasm-bindgen = "0.2.92" -serde-wasm-bindgen = "0.6.5" - -[package.metadata.cargo-machete] -ignored = ["strum"] - -[dev-dependencies] -clap = { version = "4.5.9", features = ["derive"] } -serde_json = "1.0.120" -rand = "0.8.5" -chrono = "0.4.38" - -[[example]] -name = "c509" -path = "examples/cli/main.rs" diff --git a/catalyst-gateway-crates/c509-certificate/Earthfile b/catalyst-gateway-crates/c509-certificate/Earthfile deleted file mode 100644 index 9509b7671b3..00000000000 --- a/catalyst-gateway-crates/c509-certificate/Earthfile +++ /dev/null @@ -1,31 +0,0 @@ -VERSION 0.8 - -IMPORT github.com/input-output-hk/catalyst-ci/earthly/rust:feat/faster-rust-tool-install AS rust-ci - -# builder : Set up our target toolchains, and copy our files. -builder: - DO rust-ci+SETUP - - COPY --dir .cargo .config Cargo.* clippy.toml deny.toml rustfmt.toml src examples . - -# check : Run basic check. -check: - FROM +builder - - DO rust-ci+EXECUTE --cmd="/scripts/std_checks.py" - -# build : Build the C509 library -build: - FROM +builder - - DO rust-ci+EXECUTE \ - --cmd="/scripts/std_build.py" \ - --args1="--libs=c509-certificate" - - RUN cargo install wasm-pack --version=0.12.1 --locked - -# js-wasm-package-locally : Generate the wasm package and save it locally -js-wasm-package-locally: - FROM +build - RUN wasm-pack build --target web - SAVE ARTIFACT ./pkg AS LOCAL ./pkg diff --git a/catalyst-gateway-crates/c509-certificate/clippy.toml b/catalyst-gateway-crates/c509-certificate/clippy.toml deleted file mode 100644 index 6933b816419..00000000000 --- a/catalyst-gateway-crates/c509-certificate/clippy.toml +++ /dev/null @@ -1 +0,0 @@ -allow-expect-in-tests = true diff --git a/catalyst-gateway-crates/c509-certificate/deny.toml b/catalyst-gateway-crates/c509-certificate/deny.toml deleted file mode 100644 index 5455931b26c..00000000000 --- a/catalyst-gateway-crates/c509-certificate/deny.toml +++ /dev/null @@ -1,117 +0,0 @@ -# cspell: words msvc, wasip, RUSTSEC, rustls, libssh, reqwest, tinyvec, Leay, webpki - -[graph] -# cargo-deny is really only ever intended to run on the "normal" tier-1 targets -targets = [ - "x86_64-unknown-linux-gnu", - "aarch64-unknown-linux-gnu", - "x86_64-unknown-linux-musl", - "aarch64-apple-darwin", - "x86_64-apple-darwin", - "x86_64-pc-windows-msvc", - "wasm32-unknown-unknown", - "wasm32-wasip1", - "wasm32-wasip2", -] - -[advisories] -version = 2 -ignore = [ - { id = "RUSTSEC-2020-0168", reason = "`mach` is used by wasmtime and we have no control over that." }, - { id = "RUSTSEC-2021-0145", reason = "we don't target windows, and don't use a custom global allocator." }, -] - -[bans] -multiple-versions = "warn" -wildcards = 'deny' -deny = [ - # { crate = "git2", use-instead = "gix" }, - { crate = "openssl", use-instead = "rustls" }, - { crate = "openssl-sys", use-instead = "rustls" }, - "libssh2-sys", - # { crate = "cmake", use-instead = "cc" }, - # { crate = "windows", reason = "bloated and unnecessary", use-instead = "ideally inline bindings, practically, windows-sys" }, -] -skip = [ - # { crate = "bitflags@1.3.2", reason = "https://github.com/seanmonstar/reqwest/pull/2130 should be in the next version" }, - # { crate = "winnow@0.5.40", reason = "gix 0.59 was yanked, see https://github.com/Byron/gitoxide/issues/1309" }, - # { crate = "heck@0.4.1", reason = "strum_macros uses this old version" }, - # { crate = "base64@0.21.7", reason = "gix-transport pulls in this old version, as well as a newer version via reqwest" }, - # { crate = "byte-array-literalsase64@0.21.7", reason = "gix-transport pulls in this old version, as well as a newer version via reqwest" }, -] -skip-tree = [ - { crate = "windows-sys@0.48.0", reason = "a foundational crate for many that bumps far too frequently to ever have a shared version" }, -] - -[sources] -unknown-registry = "deny" -unknown-git = "deny" - -# List of URLs for allowed Git repositories -allow-git = [ - "https://github.com/input-output-hk/hermes.git", - "https://github.com/input-output-hk/catalyst-pallas.git", - "https://github.com/bytecodealliance/wasmtime", - "https://github.com/aldanor/hdf5-rust", -] - -[licenses] -version = 2 -# Don't warn if a listed license isn't found -unused-allowed-license="allow" -# We want really high confidence when inferring licenses from text -confidence-threshold = 0.93 -allow = [ - "MIT", - "Apache-2.0", - "Unicode-DFS-2016", - "BSD-3-Clause", - "BSD-2-Clause", - "BlueOak-1.0.0", - "Apache-2.0 WITH LLVM-exception", - "CC0-1.0", - "ISC", - "Unicode-3.0", - "MPL-2.0", -] -exceptions = [ - #{ allow = ["Zlib"], crate = "tinyvec" }, - #{ allow = ["Unicode-DFS-2016"], crate = "unicode-ident" }, - #{ allow = ["OpenSSL"], crate = "ring" }, -] - -[[licenses.clarify]] -crate = "byte-array-literals" -expression = "Apache-2.0 WITH LLVM-exception" -license-files = [{ path = "../../../LICENSE", hash = 0x001c7e6c }] - -[[licenses.clarify]] -crate = "hdf5-src" -expression = "MIT" -license-files = [{ path = "../LICENSE-MIT", hash = 0x001c7e6c }] - -[[licenses.clarify]] -crate = "ring" -expression = "MIT" -license-files = [{ path = "LICENSE", hash = 0xbd0eed23 }] - -# SPDX considers OpenSSL to encompass both the OpenSSL and SSLeay licenses -# https://spdx.org/licenses/OpenSSL.html -# ISC - Both BoringSSL and ring use this for their new files -# MIT - "Files in third_party/ have their own licenses, as described therein. The MIT -# license, for third_party/fiat, which, unlike other third_party directories, is -# compiled into non-test libraries, is included below." -# OpenSSL - Obviously -#expression = "ISC AND MIT AND OpenSSL" -#license-files = [{ path = "LICENSE", hash = 0xbd0eed23 }] - -#[[licenses.clarify]] -#crate = "webpki" -#expression = "ISC" -#license-files = [{ path = "LICENSE", hash = 0x001c7e6c }] - -# Actually "ISC-style" -#[[licenses.clarify]] -#crate = "rustls-webpki" -#expression = "ISC" -#license-files = [{ path = "LICENSE", hash = 0x001c7e6c }] \ No newline at end of file diff --git a/catalyst-gateway-crates/c509-certificate/examples/cli/data/cert_sample_1.json b/catalyst-gateway-crates/c509-certificate/examples/cli/data/cert_sample_1.json deleted file mode 100644 index ca41ddabfab..00000000000 --- a/catalyst-gateway-crates/c509-certificate/examples/cli/data/cert_sample_1.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "self_signed": true, - "c509_certificate_type": 0, - "certificate_serial_number": 128269, - "issuer": [ - { - "oid": "2.5.4.3", - "value": [{ "text": "RFC test CA" }] - } - ], - "validity_not_before": null, - "validity_not_after": null, - "subject": [ - { - "oid": "2.5.4.3", - "value": [{ "text": "01-23-45-FF-FE-67-89-AB" }] - } - ], - "subject_public_key_algorithm": null, - "subject_public_key": "examples/cli/key/public_key.pem", - "extensions": [ - { - "oid": "2.5.29.15", - "value": { "int": 1 }, - "critical": false - } - ], - "issuer_signature_algorithm": null -} diff --git a/catalyst-gateway-crates/c509-certificate/examples/cli/main.rs b/catalyst-gateway-crates/c509-certificate/examples/cli/main.rs deleted file mode 100644 index f344200f000..00000000000 --- a/catalyst-gateway-crates/c509-certificate/examples/cli/main.rs +++ /dev/null @@ -1,338 +0,0 @@ -//! C509 certificate CLI - -use std::{ - fs::{self, File}, - io::Write, - path::PathBuf, -}; - -use asn1_rs::{oid, Oid}; -use c509_certificate::{ - c509_big_uint::UnwrappedBigUint, - c509_extensions::Extensions, - c509_issuer_sig_algo::IssuerSignatureAlgorithm, - c509_name::{rdn::RelativeDistinguishedName, Name, NameValue}, - c509_subject_pub_key_algo::SubjectPubKeyAlgorithm, - c509_time::Time, - signing::{PrivateKey, PublicKey}, - tbs_cert::TbsCert, -}; -use chrono::{DateTime, Utc}; -use clap::Parser; -use hex::ToHex; -use minicbor::Decode; -use rand::Rng; -use serde::{Deserialize, Serialize}; - -/// Commands for C509 certificate generation, verification and decoding -#[derive(Parser)] -#[command(version, about, long_about = None)] -enum Cli { - /// Generate C509 certificate, if private key is provided, self-signed certificate - /// will be generated. - Generate { - /// JSON file with information to create C509 certificate. - #[clap(short = 'f', long)] - json_file: PathBuf, - /// Optional output path that the generated C509 will be written to. - #[clap(short, long)] - output: Option, - /// Optional private key file, if provided, self-signed certificate will be - /// generated. Currently support only PEM format. - #[clap(long)] - private_key: Option, - #[clap(long)] - /// Optional key type. - key_type: Option, - }, - - /// C509 certificate signature verification. - Verify { - /// C509 certificate file - #[clap(short, long)] - file: PathBuf, - /// Public key file. Currently support only PEM format. - #[clap(long)] - public_key: PathBuf, - }, - - /// Decode C509 certificate back to JSON. - Decode { - /// C509 certificate file. - #[clap(short, long)] - file: PathBuf, - /// Optional output path of C509 certificate information in JSON format. - #[clap(short, long)] - output: Option, - }, -} - -impl Cli { - /// Function to execute the commands. - pub(crate) fn exec() -> anyhow::Result<()> { - let cli = Cli::parse(); - - match cli { - Cli::Generate { - json_file, - output, - private_key, - key_type, - } => { - let sk = match private_key { - Some(key) => Some(PrivateKey::from_file(key)?), - None => None, - }; - - generate(&json_file, output, sk.as_ref(), &key_type) - }, - Cli::Verify { file, public_key } => verify(&file, public_key), - Cli::Decode { file, output } => decode(&file, output), - } - } -} - -/// A struct representing the JSON format of C509 certificate. -#[derive(Deserialize, Serialize)] -struct C509Json { - /// Indicate whether the certificate is self-signed. - self_signed: bool, - /// Optional certificate type, if not provided, set to 0 as self-signed. - certificate_type: Option, - /// Optional serial number of the certificate, - /// if not provided, a random number will be generated. - serial_number: Option, - /// Optional issuer of the certificate, - /// if not provided, issuer is the same as subject. - issuer: Option, - /// Optional validity not before date, - /// if not provided, set to current time. - validity_not_before: Option, - /// Optional validity not after date, - /// if not provided, set to no expire date 9999-12-31T23:59:59+00:00. - validity_not_after: Option, - /// Relative distinguished name of the subject. - subject: RelativeDistinguishedName, - /// Optional subject public key algorithm of the certificate, - /// if not provided, set to Ed25519. - subject_public_key_algorithm: Option, - /// A path to the public key file. - /// Currently support only PEM format. - subject_public_key: String, - /// Extensions of the certificate. - extensions: Extensions, - /// Optional issuer signature algorithm of the certificate, - /// if not provided, set to Ed25519. - issuer_signature_algorithm: Option, - /// Optional issuer signature value of the certificate. - #[serde(skip_deserializing)] - issuer_signature_value: Option>, -} - -/// Ed25519 oid and parameter - default algorithm. -const ED25519: (Oid, Option) = (oid!(1.3.101 .112), None); - -/// Integer indicate that certificate is self-signed. -/// 0 for Natively Signed C509 Certificate following X.509 v3 -/// 1 for CBOR re-encoding of X.509 v3 Certificate -const SELF_SIGNED_INT: u8 = 0; - -// -------------------generate----------------------- - -/// A function to generate C509 certificate. -fn generate( - file: &PathBuf, output: Option, private_key: Option<&PrivateKey>, - key_type: &Option, -) -> anyhow::Result<()> { - let data = fs::read_to_string(file)?; - let c509_json: C509Json = serde_json::from_str(&data)?; - - validate_certificate_type(c509_json.self_signed, c509_json.certificate_type)?; - - let serial_number = parse_serial_number(c509_json.serial_number); - - let issuer = determine_issuer( - c509_json.self_signed, - c509_json.issuer, - c509_json.subject.clone(), - )?; - - // Parse validity dates or use defaults - // Now for not_before date - let not_before = parse_or_default_date(c509_json.validity_not_before, Utc::now().timestamp())?; - // Default as expire date for not_after - // Expire date = 9999-12-31T23:59:59+00:00 as mention in the C509 document - let not_after = parse_or_default_date( - c509_json.validity_not_after, - parse_or_default_date(Some("9999-12-31T23:59:59+00:00".to_string()), 0)?, - )?; - - let public_key = parse_public_key(&c509_json.subject_public_key)?; - - let key_type = get_key_type(key_type)?; - - // Create TbsCert instance - let tbs = TbsCert::new( - c509_json.certificate_type.unwrap_or(SELF_SIGNED_INT), - serial_number, - Name::new(NameValue::RelativeDistinguishedName(issuer)), - Time::new(not_before), - Time::new(not_after), - Name::new(NameValue::RelativeDistinguishedName(c509_json.subject)), - c509_json - .subject_public_key_algorithm - .unwrap_or(SubjectPubKeyAlgorithm::new(key_type.0.clone(), key_type.1)), - public_key.to_bytes(), - c509_json.extensions.clone(), - c509_json - .issuer_signature_algorithm - .unwrap_or(IssuerSignatureAlgorithm::new(key_type.0, ED25519.1)), - ); - - let cert = c509_certificate::generate(&tbs, private_key)?; - - // If the output path is provided, write to the file - if let Some(output) = output { - write_to_output_file(output, &cert)?; - }; - - println!("Hex: {:?}", hex::encode(&cert)); - println!("Bytes: {:?}", &cert); - - Ok(()) -} - -/// Write a data to a file given an output path. -fn write_to_output_file(output: PathBuf, data: &[u8]) -> anyhow::Result<()> { - let mut file = File::create(output).map_err(|e| anyhow::anyhow!(e))?; - file.write_all(data).map_err(|e| anyhow::anyhow!(e))?; - Ok(()) -} - -/// Determine issuer of the certificate. -/// If self-signed is true, issuer is the same as subject. -/// Otherwise, issuer must be present. -fn determine_issuer( - self_signed: bool, issuer: Option, - subject: RelativeDistinguishedName, -) -> anyhow::Result { - if self_signed { - Ok(subject) - } else { - issuer.ok_or_else(|| anyhow::anyhow!("Issuer must be present if self-signed is false")) - } -} - -/// Validate the certificate type. -fn validate_certificate_type( - self_signed: bool, certificate_type: Option, -) -> anyhow::Result<()> { - if self_signed && certificate_type.unwrap_or(SELF_SIGNED_INT) != SELF_SIGNED_INT { - return Err(anyhow::anyhow!( - "Certificate type must be 0 if self-signed is true" - )); - } - Ok(()) -} - -/// Parse public key from file path. -fn parse_public_key(public_key: &str) -> anyhow::Result { - let pk_path = PathBuf::from(public_key); - PublicKey::from_file(pk_path) -} - -/// Get the key type. Currently support only Ed25519. -fn get_key_type(key_type: &Option) -> anyhow::Result<(Oid<'static>, Option)> { - match key_type.as_deref() { - Some("ed25519") | None => Ok(ED25519), - Some(_) => Err(anyhow::anyhow!("Currently only support Ed25519")), - } -} - -/// Parse date string to i64. -fn parse_or_default_date(date_option: Option, default: i64) -> Result { - match date_option { - Some(date) => { - DateTime::parse_from_rfc3339(&date) - .map(|dt| dt.timestamp()) - .map_err(|e| anyhow::anyhow!(format!("Failed to parse date {date}: {e}",))) - }, - None => Ok(default), - } -} - -/// Generate random serial number if not provided -fn parse_serial_number(serial_number: Option) -> UnwrappedBigUint { - let random_number: u64 = rand::thread_rng().gen(); - serial_number.unwrap_or(UnwrappedBigUint::new(random_number)) -} - -// -------------------verify----------------------- - -/// Verify the signature of the certificate given public key file path. -fn verify(file: &PathBuf, public_key: PathBuf) -> anyhow::Result<()> { - let cert = fs::read(file)?; - let pk = PublicKey::from_file(public_key)?; - match c509_certificate::verify(&cert, &pk) { - Ok(()) => println!("Signature verified!"), - Err(e) => println!("Signature verification failed: {e}"), - }; - Ok(()) -} - -// -------------------decode----------------------- - -/// Decode the certificate to JSON. -fn decode(file: &PathBuf, output: Option) -> anyhow::Result<()> { - let cert = fs::read(file)?; - let mut d = minicbor::Decoder::new(&cert); - let c509 = c509_certificate::c509::C509::decode(&mut d, &mut ())?; - - let tbs_cert = c509.get_tbs_cert(); - let is_self_signed = tbs_cert.get_c509_certificate_type() == SELF_SIGNED_INT; - let c509_json = C509Json { - self_signed: is_self_signed, - certificate_type: Some(tbs_cert.get_c509_certificate_type()), - serial_number: Some(tbs_cert.get_certificate_serial_number().clone()), - issuer: Some(extract_relative_distinguished_name(tbs_cert.get_issuer())?), - validity_not_before: Some(time_to_string(tbs_cert.get_validity_not_before().to_i64())?), - validity_not_after: Some(time_to_string(tbs_cert.get_validity_not_after().to_i64())?), - subject: extract_relative_distinguished_name(tbs_cert.get_subject())?, - subject_public_key_algorithm: Some(tbs_cert.get_subject_public_key_algorithm().clone()), - // Return a hex formation of the public key - subject_public_key: tbs_cert.get_subject_public_key().encode_hex(), - extensions: tbs_cert.get_extensions().clone(), - issuer_signature_algorithm: Some(tbs_cert.get_issuer_signature_algorithm().clone()), - issuer_signature_value: c509.get_issuer_signature_value().clone(), - }; - - let data = serde_json::to_string(&c509_json)?; - // If the output path is provided, write to the file - if let Some(output) = output { - write_to_output_file(output, data.as_bytes())?; - }; - - println!("{data}"); - Ok(()) -} - -/// Extract a `RelativeDistinguishedName` from a `Name`. -fn extract_relative_distinguished_name(name: &Name) -> anyhow::Result { - match name.get_value() { - NameValue::RelativeDistinguishedName(rdn) => Ok(rdn.clone()), - _ => Err(anyhow::anyhow!("Expected RelativeDistinguishedName")), - } -} - -/// Convert time in i64 to string. -fn time_to_string(time: i64) -> anyhow::Result { - let datetime = - DateTime::from_timestamp(time, 0).ok_or_else(|| anyhow::anyhow!("Invalid timestamp"))?; - Ok(datetime.to_rfc3339()) -} - -// -------------------main----------------------- - -fn main() -> anyhow::Result<()> { - Cli::exec() -} diff --git a/catalyst-gateway-crates/c509-certificate/examples/web/index.html b/catalyst-gateway-crates/c509-certificate/examples/web/index.html deleted file mode 100644 index 7de6ca92294..00000000000 --- a/catalyst-gateway-crates/c509-certificate/examples/web/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - C509 certificate! - - - - - - - \ No newline at end of file diff --git a/catalyst-gateway-crates/c509-certificate/examples/web/index.js b/catalyst-gateway-crates/c509-certificate/examples/web/index.js deleted file mode 100644 index dd37a6f83f1..00000000000 --- a/catalyst-gateway-crates/c509-certificate/examples/web/index.js +++ /dev/null @@ -1,69 +0,0 @@ -// Testing the wasm binding JS functions. - -import init, { - generate, - verify, - decode, - PublicKey, - PrivateKey, -} from "../../pkg/c509_certificate.js"; - -const pem_sk = ` ------BEGIN PRIVATE KEY----- -MC4CAQAwBQYDK2VwBCIEIP1iI3LF7h89yY6QZmhDp4Y5FmTQ4oasbz2lEiaqqTzV ------END PRIVATE KEY----- -`; - -const pem_pk = ` ------BEGIN PUBLIC KEY----- -MCowBQYDK2VwAyEAtFuCleJwHS28jUCT+ulLl5c1+MXhehhDz2SimOhmWaI= ------END PUBLIC KEY----- -`; - -const tbs = { - c509_certificate_type: 0, - certificate_serial_number: 1000000n, - issuer: { - relative_distinguished_name: [ - { - oid: "2.5.4.3", - value: [{ text: "RFC test CA" }], - }, - ], - }, - validity_not_before: 1_672_531_200n, - validity_not_after: 1_767_225_600n, - subject: { text: "01-23-45-ff-fe-67-89-AB" }, - subject_public_key_algorithm: { - oid: "1.3.101.112", - }, - subject_public_key: [], - extensions: [ - { - oid: "2.5.29.19", - value: { int: -2n }, - critical: false, - }, - ], - issuer_signature_algorithm: { - oid: "1.3.101.112", - }, -}; - -async function run() { - await init(); - - let sk = PrivateKey.str_to_sk(pem_sk); - let pk = PublicKey.str_to_pk(pem_pk); - - // Call the generate with private key to create a signed version - let c509 = generate(tbs, sk); - console.log(c509); - // Verify the generated C509 with the public key - console.log(verify(c509, pk)); - // Decode the generated C509 back to readable format - let decoded_c509 = decode(c509); - console.log(decoded_c509.tbs_cert); -} - -run(); diff --git a/catalyst-gateway-crates/c509-certificate/rust-toolchain.toml b/catalyst-gateway-crates/c509-certificate/rust-toolchain.toml deleted file mode 100644 index 20a42f2a9f7..00000000000 --- a/catalyst-gateway-crates/c509-certificate/rust-toolchain.toml +++ /dev/null @@ -1,3 +0,0 @@ -[toolchain] -channel = "1.80" -profile = "default" \ No newline at end of file diff --git a/catalyst-gateway-crates/c509-certificate/rustfmt.toml b/catalyst-gateway-crates/c509-certificate/rustfmt.toml deleted file mode 100644 index 1a0573b222b..00000000000 --- a/catalyst-gateway-crates/c509-certificate/rustfmt.toml +++ /dev/null @@ -1,68 +0,0 @@ -# Enable unstable features: -# * imports_indent -# * imports_layout -# * imports_granularity -# * group_imports -# * reorder_impl_items -# * trailing_comma -# * where_single_line -# * wrap_comments -# * comment_width -# * blank_lines_upper_bound -# * condense_wildcard_suffixes -# * force_multiline_blocks -# * format_code_in_doc_comments -# * format_generated_files -# * hex_literal_case -# * inline_attribute_width -# * normalize_comments -# * normalize_doc_attributes -# * overflow_delimited_expr -unstable_features = true - -# Compatibility: -edition = "2021" - -# Tabs & spaces - Defaults, listed for clarity -tab_spaces = 4 -hard_tabs = false - -# Commas. -trailing_comma = "Vertical" -match_block_trailing_comma = true - -# General width constraints. -max_width = 100 - -# Comments: -normalize_comments = true -normalize_doc_attributes = true -wrap_comments = true -comment_width = 90 # small excess is okay but prefer 80 -format_code_in_doc_comments = true -format_generated_files = false - -# Imports. -imports_indent = "Block" -imports_layout = "Mixed" -group_imports = "StdExternalCrate" -reorder_imports = true -imports_granularity = "Crate" - -# Arguments: -use_small_heuristics = "Default" -fn_params_layout = "Compressed" -overflow_delimited_expr = true -where_single_line = true - -# Misc: -inline_attribute_width = 0 -blank_lines_upper_bound = 1 -reorder_impl_items = true -use_field_init_shorthand = true -force_multiline_blocks = true -condense_wildcard_suffixes = true -hex_literal_case = "Upper" - -# Ignored files: -ignore = [] \ No newline at end of file diff --git a/catalyst-gateway-crates/c509-certificate/src/c509.rs b/catalyst-gateway-crates/c509-certificate/src/c509.rs deleted file mode 100644 index cde35592bf2..00000000000 --- a/catalyst-gateway-crates/c509-certificate/src/c509.rs +++ /dev/null @@ -1,62 +0,0 @@ -//! C509 Certificate - -use minicbor::{encode::Write, Decode, Decoder, Encode, Encoder}; -use serde::{Deserialize, Serialize}; - -use crate::tbs_cert::TbsCert; - -#[derive(Deserialize, Serialize)] -/// A struct represents the `C509` Certificate. -pub struct C509 { - /// A TBS Certificate. - tbs_cert: TbsCert, - /// An optional `IssuerSignatureValue` of the C509 Certificate. - issuer_signature_value: Option>, -} - -impl C509 { - /// Create a new instance of C509 Certificate . - #[must_use] - pub fn new(tbs_cert: TbsCert, issuer_signature_value: Option>) -> Self { - Self { - tbs_cert, - issuer_signature_value, - } - } - - /// Get the `TBSCertificate` of the C509 Certificate. - #[must_use] - pub fn get_tbs_cert(&self) -> &TbsCert { - &self.tbs_cert - } - - /// Get the `IssuerSignatureValue` of the C509 Certificate. - #[must_use] - pub fn get_issuer_signature_value(&self) -> &Option> { - &self.issuer_signature_value - } -} - -impl Encode<()> for C509 { - fn encode( - &self, e: &mut Encoder, ctx: &mut (), - ) -> Result<(), minicbor::encode::Error> { - self.tbs_cert.encode(e, ctx)?; - match self.issuer_signature_value { - Some(ref value) => e.bytes(value)?, - None => e.null()?, - }; - Ok(()) - } -} - -impl Decode<'_, ()> for C509 { - fn decode(d: &mut Decoder<'_>, ctx: &mut ()) -> Result { - let tbs_cert = TbsCert::decode(d, ctx)?; - let issuer_signature_value = match d.datatype()? { - minicbor::data::Type::Bytes => Some(d.bytes()?.to_vec()), - _ => None, - }; - Ok(Self::new(tbs_cert, issuer_signature_value)) - } -} diff --git a/catalyst-gateway-crates/c509-certificate/src/c509_algo_identifier.rs b/catalyst-gateway-crates/c509-certificate/src/c509_algo_identifier.rs deleted file mode 100644 index b11153c8a9d..00000000000 --- a/catalyst-gateway-crates/c509-certificate/src/c509_algo_identifier.rs +++ /dev/null @@ -1,92 +0,0 @@ -//! C509 Algorithm Identifier -//! -//! This module handle the `AlgorithmIdentifier` type where OID does not fall into the -//! table. -//! -//! ```cddl -//! AlgorithmIdentifier = int / ~oid / [ algorithm: ~oid, parameters: bytes ] -//! ``` -//! -//! **Note** `AlgorithmIdentifier` that have the same OID with different parameters are -//! not implemented yet. -//! -//! For more information about `AlgorithmIdentifier`, -//! visit [C509 Certificate](https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/09/) - -use asn1_rs::Oid; -use minicbor::{encode::Write, Decode, Decoder, Encode, Encoder}; -use serde::{Deserialize, Serialize}; - -use crate::c509_oid::C509oid; - -/// A struct represents the `AlgorithmIdentifier` type. -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] -pub struct AlgorithmIdentifier { - /// A `C509oid` - oid: C509oid, - /// An optional parameter string - param: Option, -} - -impl AlgorithmIdentifier { - /// Create new instance of `AlgorithmIdentifier`. - #[must_use] - pub fn new(oid: Oid<'static>, param: Option) -> Self { - Self { - oid: C509oid::new(oid), - param, - } - } - - /// Get the OID. - pub(crate) fn get_oid(&self) -> Oid<'static> { - self.oid.clone().get_oid() - } - - /// Get the parameter. - pub(crate) fn get_param(&self) -> &Option { - &self.param - } -} - -impl Encode<()> for AlgorithmIdentifier { - fn encode( - &self, e: &mut Encoder, ctx: &mut (), - ) -> Result<(), minicbor::encode::Error> { - match &self.param { - // [ algorithm: ~oid, parameters: bytes ] - Some(p) => { - e.array(2)?; - self.oid.encode(e, ctx)?; - e.bytes(p.as_bytes())?; - }, - // ~oid - None => { - self.oid.encode(e, ctx)?; - }, - } - Ok(()) - } -} - -impl Decode<'_, ()> for AlgorithmIdentifier { - fn decode(d: &mut Decoder<'_>, ctx: &mut ()) -> Result { - // [ algorithm: ~oid, parameters: bytes ] - if d.datatype()? == minicbor::data::Type::Array { - let len = d.array()?.ok_or(minicbor::decode::Error::message( - "Failed to get array length", - ))?; - if len != 2 { - return Err(minicbor::decode::Error::message("Array length must be 2")); - } - let c509_oid = C509oid::decode(d, ctx)?; - let param = - String::from_utf8(d.bytes()?.to_vec()).map_err(minicbor::decode::Error::message)?; - Ok(AlgorithmIdentifier::new(c509_oid.get_oid(), Some(param))) - // ~oid - } else { - let oid = C509oid::decode(d, ctx)?; - Ok(AlgorithmIdentifier::new(oid.get_oid(), None)) - } - } -} diff --git a/catalyst-gateway-crates/c509-certificate/src/c509_attributes/attribute.rs b/catalyst-gateway-crates/c509-certificate/src/c509_attributes/attribute.rs deleted file mode 100644 index 507a5decb0a..00000000000 --- a/catalyst-gateway-crates/c509-certificate/src/c509_attributes/attribute.rs +++ /dev/null @@ -1,257 +0,0 @@ -//! C509 Attribute -//! -//! ```cddl -//! Attribute = ( attributeType: int, attributeValue: text ) // -//! ( attributeType: ~oid, attributeValue: bytes ) // -//! ( attributeType: pen, attributeValue: bytes ) -//! ``` -//! -//! For more information about Attribute, -//! visit [C509 Certificate](https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/09/) - -use std::str::FromStr; - -use asn1_rs::Oid; -use minicbor::{encode::Write, Decode, Decoder, Encode, Encoder}; -use serde::{Deserialize, Deserializer, Serialize}; - -use super::data::{get_oid_from_int, ATTRIBUTES_LOOKUP}; -use crate::c509_oid::{C509oid, C509oidRegistered}; - -/// A struct of C509 `Attribute` -#[derive(Debug, Clone, PartialEq)] -pub struct Attribute { - /// A registered OID of C509 `Attribute`. - registered_oid: C509oidRegistered, - /// A flag to indicate whether the value can have multiple value. - multi_value: bool, - /// A value of C509 `Attribute` can be a vector of text or bytes. - value: Vec, -} - -impl Attribute { - /// Create a new instance of `Attribute`. - #[must_use] - pub fn new(oid: Oid<'static>) -> Self { - Self { - registered_oid: C509oidRegistered::new(oid, ATTRIBUTES_LOOKUP.get_int_to_oid_table()), - multi_value: false, - value: Vec::new(), - } - } - - /// Add a value to `Attribute`. - pub fn add_value(&mut self, value: AttributeValue) { - self.value.push(value); - } - - /// Get the registered OID of `Attribute`. - pub(crate) fn get_registered_oid(&self) -> &C509oidRegistered { - &self.registered_oid - } - - /// Get the value of `Attribute`. - pub(crate) fn get_value(&self) -> &Vec { - &self.value - } - - /// Set whether `Attribute` can be PEN encoded. - pub(crate) fn set_pen_supported(self) -> Self { - Self { - registered_oid: self.registered_oid.pen_encoded(), - multi_value: self.multi_value, - value: self.value, - } - } - - /// Set whether `Attribute` can have multiple value. - pub(crate) fn set_multi_value(mut self) -> Self { - self.multi_value = true; - self - } -} - -/// A helper struct for deserialize and serialize `Attribute`. -#[derive(Debug, Deserialize, Serialize)] -struct Helper { - /// An OID value in string. - oid: String, - /// A value of C509 `Attribute` can be a vector of text or bytes. - value: Vec, -} - -impl<'de> Deserialize<'de> for Attribute { - fn deserialize(deserializer: D) -> Result - where D: Deserializer<'de> { - let helper = Helper::deserialize(deserializer)?; - let oid = - Oid::from_str(&helper.oid).map_err(|e| serde::de::Error::custom(format!("{e:?}")))?; - let mut attr = Attribute::new(oid); - for value in helper.value { - attr.add_value(value); - } - Ok(attr) - } -} - -impl Serialize for Attribute { - fn serialize(&self, serializer: S) -> Result - where S: serde::Serializer { - let helper = Helper { - oid: self.registered_oid.get_c509_oid().get_oid().to_string(), - value: self.value.clone(), - }; - helper.serialize(serializer) - } -} - -impl Encode<()> for Attribute { - fn encode( - &self, e: &mut Encoder, ctx: &mut (), - ) -> Result<(), minicbor::encode::Error> { - // Encode CBOR int if available - if let Some(&oid) = self - .registered_oid - .get_table() - .get_map() - .get_by_right(&self.registered_oid.get_c509_oid().get_oid()) - { - e.i16(oid)?; - } else { - // Encode unwrapped CBOR OID or CBOR PEN - self.registered_oid.get_c509_oid().encode(e, ctx)?; - } - - // Check if the attribute value is empty - if self.value.is_empty() { - return Err(minicbor::encode::Error::message("Attribute value is empty")); - } - - // If multi-value attributes, encode it as array - if self.multi_value { - e.array(self.value.len() as u64)?; - } - - // Encode each value in the attribute - for value in &self.value { - value.encode(e, ctx)?; - } - - Ok(()) - } -} - -impl Decode<'_, ()> for Attribute { - fn decode(d: &mut Decoder<'_>, ctx: &mut ()) -> Result { - // Handle CBOR int - let mut attr = if d.datatype()? == minicbor::data::Type::U8 { - let i = d.i16()?; - let oid = get_oid_from_int(i).map_err(minicbor::decode::Error::message)?; - Attribute::new(oid.clone()) - } else { - // Handle unwrapped CBOR OID or CBOR PEN - let c509_oid: C509oid = d.decode()?; - Attribute::new(c509_oid.get_oid()) - }; - - // Handle attribute value - if d.datatype()? == minicbor::data::Type::Array { - // When multi-value attribute - let len = d.array()?.ok_or_else(|| { - minicbor::decode::Error::message("Failed to get array length for attribute value") - })?; - - if len == 0 { - return Err(minicbor::decode::Error::message("Attribute value is empty")); - } - - for _ in 0..len { - attr.add_value(AttributeValue::decode(d, ctx)?); - } - attr = attr.set_multi_value(); - } else { - let value = AttributeValue::decode(d, ctx)?; - attr.add_value(value); - } - Ok(attr) - } -} - -// ------------------AttributeValue---------------------- - -/// An enum of possible value types for `Attribute`. -#[allow(clippy::module_name_repetitions)] -#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Deserialize, Serialize)] -#[serde(rename_all = "snake_case")] -pub enum AttributeValue { - /// A text string. - Text(String), - /// A byte vector. - Bytes(Vec), -} - -impl Encode<()> for AttributeValue { - fn encode( - &self, e: &mut Encoder, _ctx: &mut (), - ) -> Result<(), minicbor::encode::Error> { - match self { - AttributeValue::Text(text) => e.str(text)?, - AttributeValue::Bytes(bytes) => e.bytes(bytes)?, - }; - Ok(()) - } -} - -impl Decode<'_, ()> for AttributeValue { - fn decode(d: &mut Decoder<'_>, _ctx: &mut ()) -> Result { - match d.datatype()? { - minicbor::data::Type::String => Ok(AttributeValue::Text(d.str()?.to_string())), - minicbor::data::Type::Bytes => Ok(AttributeValue::Bytes(d.bytes()?.to_vec())), - _ => { - Err(minicbor::decode::Error::message( - "Invalid AttributeValue, value should be either String or Bytes", - )) - }, - } - } -} - -// ------------------Test---------------------- - -#[cfg(test)] -mod test_attribute { - use asn1_rs::oid; - - use super::*; - - #[test] - fn encode_decode_attribute_int() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - let mut attribute = Attribute::new(oid!(1.2.840 .113549 .1 .9 .1)); - attribute.add_value(AttributeValue::Text("example@example.com".to_string())); - attribute - .encode(&mut encoder, &mut ()) - .expect("Failed to encode Attribute"); - // Email Address example@example.com: 0x00736578616d706c65406578616d706c652e636f6d - assert_eq!( - hex::encode(buffer.clone()), - "00736578616d706c65406578616d706c652e636f6d" - ); - - let mut decoder = Decoder::new(&buffer); - let attribute_decoded = - Attribute::decode(&mut decoder, &mut ()).expect("Failed to decode Attribute"); - assert_eq!(attribute_decoded, attribute); - } - - #[test] - fn empty_attribute_value() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - let attribute = Attribute::new(oid!(1.2.840 .113549 .1 .9 .1)); - attribute - .encode(&mut encoder, &mut ()) - .expect_err("Failed to encode Attribute"); - } -} diff --git a/catalyst-gateway-crates/c509-certificate/src/c509_attributes/data.rs b/catalyst-gateway-crates/c509-certificate/src/c509_attributes/data.rs deleted file mode 100644 index b89fb429cb4..00000000000 --- a/catalyst-gateway-crates/c509-certificate/src/c509_attributes/data.rs +++ /dev/null @@ -1,88 +0,0 @@ -//! Attribute data provides a necessary information for encoding and decoding of C509 -//! Attribute. See [C509 Certificate](https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/09/) -//! Section 9.3 C509 Attributes Registry for more information. - -use anyhow::Error; -use asn1_rs::{oid, Oid}; -use once_cell::sync::Lazy; - -use crate::tables::IntegerToOidTable; - -/// Type of `Attribute` data -/// Int | OID | Name -type AttributeDataTuple = (i16, Oid<'static>, &'static str); - -/// `Attribute` data table -#[rustfmt::skip] -const ATTRIBUTE_DATA: [AttributeDataTuple; 30] = [ - // Int | OID | Name - (0, oid!(1.2.840.113549.1.9.1), "Email Address"), - (1, oid!(2.5.4.3), "Common Name"), - (2, oid!(2.5.4.4), "Surname"), - (3, oid!(2.5.4.5), "Serial Number"), - (4, oid!(2.5.4.6), "Country"), - (5, oid!(2.5.4.7), "Locality"), - (6, oid!(2.5.4.8), "State or Province"), - (7, oid!(2.5.4.9), "Street Address"), - (8, oid!(2.5.4.10), "Organization"), - (9, oid!(2.5.4.11), "Organizational Unit"), - (10, oid!(2.5.4.12), "Title"), - (11, oid!(2.5.4.15), "Business Category"), - (12, oid!(2.5.4.17), "Postal Code"), - (13, oid!(2.5.4.42), "Given Name"), - (14, oid!(2.5.4.43), "Initials"), - (15, oid!(2.5.4.44), "Generation Qualifier"), - (16, oid!(2.5.4.46), "DN Qualifier"), - (17, oid!(2.5.4.65), "Pseudonym"), - (18, oid!(2.5.4.97), "Organization Identifier"), - (19, oid!(1.3.6.1.4.1.311.60.2.1.1), "Inc. Locality"), - (20, oid!(1.3.6.1.4.1.311.60.2.1.2), "Inc. State or Province"), - (21, oid!(1.3.6.1.4.1.311.60.2.1.3), "Inc. Country"), - (22, oid!(0.9.2342.19200300.100.1.25), "Domain Component"), - (23, oid!(2.5.4.16), "Postal Address"), - (24, oid!(2.5.4.41), "Name"), - (25, oid!(2.5.4.20), "Telephone Number"), - (26, oid!(2.5.4.54), "Directory Management Domain Name"), - (27, oid!(0.9.2342.19200300.100.1.1), "userid"), - (28, oid!(1.2.840.113549.1.9.2), "Unstructured Name"), - (29, oid!(1.2.840.113549.1.9.8), "Unstructured Address"), -]; - -/// A struct of data that contains lookup tables for `Attribute`. -pub(crate) struct AttributeData { - /// A table of integer to OID, provide a bidirectional lookup. - int_to_oid_table: IntegerToOidTable, -} - -impl AttributeData { - /// Get the `IntegerToOidTable`. - pub(crate) fn get_int_to_oid_table(&self) -> &IntegerToOidTable { - &self.int_to_oid_table - } -} - -/// Define static lookup for attributes table -static ATTRIBUTES_TABLES: Lazy = Lazy::new(|| { - let mut int_to_oid_table = IntegerToOidTable::new(); - - for data in ATTRIBUTE_DATA { - int_to_oid_table.add(data.0, data.1); - } - - AttributeData { int_to_oid_table } -}); - -/// Static reference to the `AttributeData` lookup table. -pub(crate) static ATTRIBUTES_LOOKUP: &Lazy = &ATTRIBUTES_TABLES; - -/// Get the OID from the int value. -pub(crate) fn get_oid_from_int(i: i16) -> Result, Error> { - ATTRIBUTES_TABLES - .get_int_to_oid_table() - .get_map() - .get_by_left(&i) - .ok_or(Error::msg(format!( - "OID int not found in the attribute registry table given {i}" - ))) - .cloned() -} diff --git a/catalyst-gateway-crates/c509-certificate/src/c509_attributes/mod.rs b/catalyst-gateway-crates/c509-certificate/src/c509_attributes/mod.rs deleted file mode 100644 index 30fa1684ae4..00000000000 --- a/catalyst-gateway-crates/c509-certificate/src/c509_attributes/mod.rs +++ /dev/null @@ -1,128 +0,0 @@ -//! C509 `Attributes` containing `Attribute` -//! -//! ```cddl -//! Attributes = ( attributeType: int, attributeValue: [+text] ) // -//! ( attributeType: ~oid, attributeValue: [+bytes] ) -//! ``` -//! -//! Use case: -//! ```cddl -//! SubjectDirectoryAttributes = [+Attributes] -//! ``` -//! -//! For more information about `Atributes`, -//! visit [C509 Certificate](https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/09/) - -use attribute::Attribute; -use minicbor::{encode::Write, Decode, Decoder, Encode, Encoder}; - -pub mod attribute; -mod data; - -/// A struct of C509 `Attributes` containing a vector of `Attribute`. -#[derive(Debug, Clone, PartialEq)] -pub struct Attributes(Vec); - -impl Default for Attributes { - fn default() -> Self { - Self::new() - } -} - -impl Attributes { - /// Create a new instance of `Attributes` as empty vector. - #[must_use] - pub fn new() -> Self { - Self(Vec::new()) - } - - /// Add an `Attribute` to the `Attributes`. - /// and set `Attribute` value to support multiple value. - pub fn add_attr(&mut self, attribute: Attribute) { - self.0.push(attribute.set_multi_value()); - } -} - -impl Encode<()> for Attributes { - fn encode( - &self, e: &mut Encoder, ctx: &mut (), - ) -> Result<(), minicbor::encode::Error> { - if self.0.is_empty() { - return Err(minicbor::encode::Error::message( - "Attributes should not be empty", - )); - } - e.array(self.0.len() as u64)?; - for attribute in &self.0 { - attribute.encode(e, ctx)?; - } - Ok(()) - } -} - -impl Decode<'_, ()> for Attributes { - fn decode(d: &mut Decoder<'_>, _ctx: &mut ()) -> Result { - let len = d - .array()? - .ok_or_else(|| minicbor::decode::Error::message("Failed to get array length"))?; - if len == 0 { - return Err(minicbor::decode::Error::message("Attributes is empty")); - } - - let mut attributes = Attributes::new(); - - for _ in 0..len { - let attribute = Attribute::decode(d, &mut ())?; - attributes.add_attr(attribute); - } - - Ok(attributes) - } -} - -// ------------------Test---------------------- - -#[cfg(test)] -mod test_attributes { - use asn1_rs::oid; - use attribute::AttributeValue; - - use super::*; - - #[test] - fn encode_decode_attributes_int() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - let mut attr = Attribute::new(oid!(1.2.840 .113549 .1 .9 .1)); - attr.add_value(AttributeValue::Text("example@example.com".to_string())); - attr.add_value(AttributeValue::Text("example@example.com".to_string())); - let mut attributes = Attributes::new(); - attributes.add_attr(attr); - attributes - .encode(&mut encoder, &mut ()) - .expect("Failed to encode Attributes"); - // 1 Attribute value (array len 1): 0x81 - // Email Address: 0x00 - // Attribute value (array len 2): 0x82 - // example@example.com: 0x736578616d706c65406578616d706c652e636f6d - assert_eq!( - hex::encode(buffer.clone()), - "810082736578616d706c65406578616d706c652e636f6d736578616d706c65406578616d706c652e636f6d" - ); - - let mut decoder = Decoder::new(&buffer); - let attribute_decoded = - Attributes::decode(&mut decoder, &mut ()).expect("Failed to decode Attributes"); - assert_eq!(attribute_decoded, attributes); - } - - #[test] - fn empty_attributes() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - let attributes = Attributes::new(); - attributes - .encode(&mut encoder, &mut ()) - .expect_err("Failed to encode Attributes"); - } -} diff --git a/catalyst-gateway-crates/c509-certificate/src/c509_big_uint.rs b/catalyst-gateway-crates/c509-certificate/src/c509_big_uint.rs deleted file mode 100644 index 8d844e2e8ad..00000000000 --- a/catalyst-gateway-crates/c509-certificate/src/c509_big_uint.rs +++ /dev/null @@ -1,95 +0,0 @@ -//! C509 Unwrapped CBOR Unsigned Bignum (~biguint) -//! -//! Please refer to [CDDL Wrapping](https://datatracker.ietf.org/doc/html/rfc8610#section-3.7) -//! for unwrapped types. - -// cspell: words Bignum bignum biguint - -use minicbor::{encode::Write, Decode, Decoder, Encode, Encoder}; -use serde::{Deserialize, Serialize}; - -/// A struct representing an unwrapped CBOR unsigned bignum. -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] -pub struct UnwrappedBigUint(u64); - -impl UnwrappedBigUint { - /// Create a new instance of `UnwrappedBigUint`. - #[must_use] - pub fn new(uint: u64) -> Self { - Self(uint) - } -} - -impl Encode<()> for UnwrappedBigUint { - fn encode( - &self, e: &mut Encoder, _ctx: &mut (), - ) -> Result<(), minicbor::encode::Error> { - let bytes = self.0.to_be_bytes(); - // Trim leading zeros - let significant_bytes = bytes - .iter() - .skip_while(|&&b| b == 0) - .copied() - .collect::>(); - - e.bytes(&significant_bytes)?; - Ok(()) - } -} - -impl Decode<'_, ()> for UnwrappedBigUint { - fn decode(d: &mut Decoder<'_>, _ctx: &mut ()) -> Result { - // Turn bytes into u64 - let b = d - .bytes()? - .iter() - .fold(0, |acc, &b| (acc << 8) | u64::from(b)); - Ok(UnwrappedBigUint::new(b)) - } -} - -#[cfg(test)] -mod test_big_uint { - - use super::*; - - // Test reference https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/09/ - // A.1. Example RFC 7925 profiled X.509 Certificate - #[test] - fn test_encode_decode() { - let mut buffer = Vec::new(); - let mut encoder = minicbor::Encoder::new(&mut buffer); - // Serial Number: 128269 (0x1f50d) - let b_uint = UnwrappedBigUint::new(128_269); - b_uint - .encode(&mut encoder, &mut ()) - .expect("Failed to encode UnwrappedBigUint"); - assert_eq!(hex::encode(buffer.clone()), "4301f50d"); - - let mut decoder = minicbor::Decoder::new(&buffer); - let decoded_b_uint = UnwrappedBigUint::decode(&mut decoder, &mut ()) - .expect("Failed to decode UnwrappedBigUint"); - - assert_eq!(decoded_b_uint, b_uint); - } - - // Test reference https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/09/ - // A.2. Example IEEE 802.1AR profiled X.509 Certificate - #[test] - fn test_encode_decode_2() { - let mut buffer = Vec::new(); - let mut encoder = minicbor::Encoder::new(&mut buffer); - // Serial Number: 9112578475118446130 (0x7e7661d7b54e4632) - let b_uint = UnwrappedBigUint::new(9_112_578_475_118_446_130); - b_uint - .encode(&mut encoder, &mut ()) - .expect("Failed to encode UnwrappedBigUint"); - assert_eq!(hex::encode(buffer.clone()), "487e7661d7b54e4632"); - - let mut decoder = minicbor::Decoder::new(&buffer); - let decoded_b_uint = UnwrappedBigUint::decode(&mut decoder, &mut ()) - .expect("Failed to decode UnwrappedBigUint"); - - assert_eq!(decoded_b_uint, b_uint); - } -} diff --git a/catalyst-gateway-crates/c509-certificate/src/c509_extensions/alt_name.rs b/catalyst-gateway-crates/c509-certificate/src/c509_extensions/alt_name.rs deleted file mode 100644 index 7ab83513bca..00000000000 --- a/catalyst-gateway-crates/c509-certificate/src/c509_extensions/alt_name.rs +++ /dev/null @@ -1,161 +0,0 @@ -//! C509 Alternative Name uses for Subject Alternative Name extension and -//! Issuer Alternative Name extension. - -use minicbor::{encode::Write, Decode, Decoder, Encode, Encoder}; -use serde::{Deserialize, Serialize}; - -use crate::c509_general_names::{ - general_name::{GeneralName, GeneralNameTypeRegistry, GeneralNameValue}, - GeneralNames, -}; - -/// Alternative Name extension. -/// Can be interpreted as a `GeneralNames / text` -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] -pub struct AlternativeName(GeneralNamesOrText); - -impl AlternativeName { - /// Create a new instance of `AlternativeName` given value. - #[must_use] - pub fn new(value: GeneralNamesOrText) -> Self { - Self(value) - } -} - -impl Encode<()> for AlternativeName { - fn encode( - &self, e: &mut Encoder, ctx: &mut (), - ) -> Result<(), minicbor::encode::Error> { - self.0.encode(e, ctx) - } -} - -impl Decode<'_, ()> for AlternativeName { - fn decode(d: &mut Decoder<'_>, ctx: &mut ()) -> Result { - GeneralNamesOrText::decode(d, ctx).map(AlternativeName::new) - } -} - -// ------------------GeneralNamesOrText-------------------- - -/// Enum for type that can be a `GeneralNames` or a text use in `AlternativeName`. -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] -#[serde(rename_all = "snake_case")] -pub enum GeneralNamesOrText { - /// A value of `GeneralNames`. - GeneralNames(GeneralNames), - /// A text string. - Text(String), -} - -impl Encode<()> for GeneralNamesOrText { - fn encode( - &self, e: &mut Encoder, ctx: &mut (), - ) -> Result<(), minicbor::encode::Error> { - match self { - GeneralNamesOrText::GeneralNames(gns) => { - let gn = gns - .get_gns() - .first() - .ok_or(minicbor::encode::Error::message("GeneralNames is empty"))?; - // Check whether there is only 1 item in the array which is a DNSName - if gns.get_gns().len() == 1 && gn.get_gn_type().is_dns_name() { - gn.get_gn_value().encode(e, ctx)?; - } else { - gns.encode(e, ctx)?; - } - }, - GeneralNamesOrText::Text(text) => { - e.str(text)?; - }, - } - Ok(()) - } -} - -impl Decode<'_, ()> for GeneralNamesOrText { - fn decode(d: &mut Decoder<'_>, ctx: &mut ()) -> Result { - match d.datatype()? { - // If it is a string it is a GeneralNames with only 1 DNSName - minicbor::data::Type::String => { - let gn_dns = GeneralName::new( - GeneralNameTypeRegistry::DNSName, - GeneralNameValue::Text(d.str()?.to_string()), - ); - let mut gns = GeneralNames::new(); - gns.add_gn(gn_dns); - Ok(GeneralNamesOrText::GeneralNames(gns)) - }, - minicbor::data::Type::Array => { - Ok(GeneralNamesOrText::GeneralNames(GeneralNames::decode( - d, ctx, - )?)) - }, - _ => { - Err(minicbor::decode::Error::message( - "Invalid type for AlternativeName", - )) - }, - } - } -} - -// ------------------Test---------------------- - -#[cfg(test)] -mod test_alt_name { - use super::*; - use crate::c509_general_names::general_name::{ - GeneralName, GeneralNameTypeRegistry, GeneralNameValue, - }; - - #[test] - fn encode_only_dns() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - let mut gns = GeneralNames::new(); - gns.add_gn(GeneralName::new( - GeneralNameTypeRegistry::DNSName, - GeneralNameValue::Text("example.com".to_string()), - )); - let alt_name = AlternativeName::new(GeneralNamesOrText::GeneralNames(gns)); - alt_name - .encode(&mut encoder, &mut ()) - .expect("Failed to encode AlternativeName"); - // "example.com": 0x6b6578616d706c652e636f6d - assert_eq!(hex::encode(buffer.clone()), "6b6578616d706c652e636f6d"); - - let mut decoder = Decoder::new(&buffer); - let decoded_alt_name = AlternativeName::decode(&mut decoder, &mut ()) - .expect("Failed to decode Alternative Name"); - assert_eq!(decoded_alt_name, alt_name); - } - - #[test] - fn encode_decode_text() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - - let alt_name = AlternativeName::new(GeneralNamesOrText::Text("example.com".to_string())); - alt_name - .encode(&mut encoder, &mut ()) - .expect("Failed to encode AlternativeName"); - // "example.com": 0x6b6578616d706c652e636f6d - assert_eq!(hex::encode(buffer.clone()), "6b6578616d706c652e636f6d"); - - // If only text, it should be GeneralNames with only 1 DNSName - let mut gns = GeneralNames::new(); - gns.add_gn(GeneralName::new( - GeneralNameTypeRegistry::DNSName, - GeneralNameValue::Text("example.com".to_string()), - )); - - let mut decoder = Decoder::new(&buffer); - let decoded_alt_name = AlternativeName::decode(&mut decoder, &mut ()) - .expect("Failed to decode Alternative Name"); - assert_eq!( - decoded_alt_name, - AlternativeName::new(GeneralNamesOrText::GeneralNames(gns)) - ); - } -} diff --git a/catalyst-gateway-crates/c509-certificate/src/c509_extensions/extension/data.rs b/catalyst-gateway-crates/c509-certificate/src/c509_extensions/extension/data.rs deleted file mode 100644 index 9bf2b5a8fc8..00000000000 --- a/catalyst-gateway-crates/c509-certificate/src/c509_extensions/extension/data.rs +++ /dev/null @@ -1,115 +0,0 @@ -//! Extension data provides a necessary information for encoding and decoding of C509 -//! Extension. See [C509 Certificate](https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/09/) -//! Section 9.4 C509 Extensions Registry for more information. - -// cspell: words Evt - -use std::collections::HashMap; - -use anyhow::Error; -use asn1_rs::{oid, Oid}; -use once_cell::sync::Lazy; - -use super::ExtensionValueType; -use crate::tables::IntegerToOidTable; - -/// Type of `Extension` data -/// Int | OID | Type | Name -type ExtensionDataTuple = (i16, Oid<'static>, ExtensionValueType, &'static str); - -/// Create a type alias for `ExtensionValueType` -type Evt = ExtensionValueType; - -/// `Extension` data table -#[rustfmt::skip] -const EXTENSION_DATA: [ExtensionDataTuple; 25] = [ - // Int | OID | Type | Name - ( 1, oid!(2.5.29 .14), Evt::Bytes, "Subject Key Identifier"), - ( 2, oid!(2.5.29 .15), Evt::Int, "Key Usage"), - ( 3, oid!(2.5.29 .17), Evt::AlternativeName, "Subject Alternative Name"), - ( 4, oid!(2.5.29 .19), Evt::Int, "Basic Constraints"), - ( 5, oid!(2.5.29 .31), Evt::Unsupported, "CRL Distribution Points"), - ( 6, oid!(2.5.29 .32), Evt::Unsupported, "Certificate Policies"), - ( 7, oid!(2.5.29 .35), Evt::Unsupported, "Authority Key Identifier"), - ( 8, oid!(2.5.29 .37), Evt::Unsupported, "Extended Key Usage"), - ( 9, oid!(1.3.6 .1 .5 .5 .7 .1 .1), Evt::Unsupported, "Authority Information Access"), - (10, oid!(1.3.6 .1 .4 .1 .11129 .2 .4 .2), Evt::Unsupported, "Signed Certificate Timestamp List"), - (24, oid!(2.5.29 .9), Evt::Unsupported, "Subject Directory Attributes"), - (25, oid!(2.5.29 .18), Evt::AlternativeName, "Issuer Alternative Name"), - (26, oid!(2.5.29 .30), Evt::Unsupported, "Name Constraints"), - (27, oid!(2.5.29 .33), Evt::Unsupported, "Policy Mappings"), - (28, oid!(2.5.29 .36), Evt::Unsupported, "Policy Constraints"), - (29, oid!(2.5.29 .46), Evt::Unsupported, "Freshest CRL"), - (30, oid!(2.5.29 .54), Evt::Int, "Inhibit anyPolicy"), - (31, oid!(1.3.6 .1 .5 .5 .7 .1 .11), Evt::Unsupported, "Subject Information Access"), - (32, oid!(1.3.6 .1 .5 .5 .7 .1 .7), Evt::Unsupported, "IP Resources"), - (33, oid!(1.3.6 .1 .5 .5 .7 .1 .7), Evt::Unsupported, "AS Resource"), - (34, oid!(1.3.6 .1 .5 .5 .7 .1 .28), Evt::Unsupported, "IP Resources v2"), - (35, oid!(1.3.6 .1 .5 .5 .7 .1 .29), Evt::Unsupported, "AS Resources v2"), - (36, oid!(1.3.6 .1 .5 .5 .7 .1 .2), Evt::Unsupported, "Biometric Information"), - (37, oid!(1.3.6 .1 .4 .1 .11129 .2 .4 .4), Evt::Unsupported, "Precertificate Signing Certificate"), - (38, oid!(1.3.6 .1 .5 .5 .7 .48 .1 .5), Evt::Unsupported, "OCSP No Check"), -]; - -/// A struct of data that contains lookup tables for `Extension`. -pub(crate) struct ExtensionData { - /// A table of integer to OID, provide a bidirectional lookup. - int_to_oid_table: IntegerToOidTable, - /// A table of integer to `ExtensionValueType`, provide a lookup for `Extension` value - /// type. - int_to_type_table: HashMap, -} - -impl ExtensionData { - /// Get the `IntegerToOidTable`. - pub(crate) fn get_int_to_oid_table(&self) -> &IntegerToOidTable { - &self.int_to_oid_table - } - - /// Get the `int_to_type_table` - pub(crate) fn get_int_to_type_table(&self) -> &HashMap { - &self.int_to_type_table - } -} - -/// Define static lookup for extensions table -static EXTENSIONS_TABLES: Lazy = Lazy::new(|| { - let mut int_to_oid_table = IntegerToOidTable::new(); - let mut int_to_type_table = HashMap::::new(); - - for data in EXTENSION_DATA { - int_to_oid_table.add(data.0, data.1); - int_to_type_table.insert(data.0, data.2); - } - - ExtensionData { - int_to_oid_table, - int_to_type_table, - } -}); - -/// Static reference to the `ExtensionData` lookup table. -pub(crate) static EXTENSIONS_LOOKUP: &Lazy = &EXTENSIONS_TABLES; - -/// Get the OID from the int value. -pub(crate) fn get_oid_from_int(i: i16) -> Result, Error> { - EXTENSIONS_TABLES - .get_int_to_oid_table() - .get_map() - .get_by_left(&i) - .ok_or(Error::msg(format!( - "OID not found in the extension registry table given int {i}" - ))) - .cloned() -} - -/// Get the extension value type from the int value. -pub(crate) fn get_extension_type_from_int(i: i16) -> Result { - EXTENSIONS_TABLES - .get_int_to_type_table() - .get(&i) - .ok_or(Error::msg(format!( - "Extension value type not found in the extension registry table given int {i}" - ))) - .cloned() -} diff --git a/catalyst-gateway-crates/c509-certificate/src/c509_extensions/extension/mod.rs b/catalyst-gateway-crates/c509-certificate/src/c509_extensions/extension/mod.rs deleted file mode 100644 index 264b98d1a72..00000000000 --- a/catalyst-gateway-crates/c509-certificate/src/c509_extensions/extension/mod.rs +++ /dev/null @@ -1,344 +0,0 @@ -//! C509 Extension use to construct an Extensions message field for C509 Certificate. - -mod data; -use std::{fmt::Debug, str::FromStr}; - -use asn1_rs::Oid; -use data::{get_extension_type_from_int, get_oid_from_int, EXTENSIONS_LOOKUP}; -use minicbor::{encode::Write, Decode, Decoder, Encode, Encoder}; -use serde::{Deserialize, Deserializer, Serialize}; -use strum_macros::EnumDiscriminants; - -use super::alt_name::AlternativeName; -use crate::c509_oid::{C509oid, C509oidRegistered}; - -/// A struct of C509 `Extension` -#[derive(Debug, Clone, PartialEq)] -pub struct Extension { - /// The registered OID of the `Extension`. - registered_oid: C509oidRegistered, - /// The critical flag of the `Extension` negative if critical is true, otherwise - /// positive. - critical: bool, - /// The value of the `Extension` in `ExtensionValue`. - value: ExtensionValue, -} - -impl Extension { - /// Create a new instance of `Extension` using `OID` and value. - #[must_use] - pub fn new(oid: Oid<'static>, value: ExtensionValue, critical: bool) -> Self { - Self { - registered_oid: C509oidRegistered::new(oid, EXTENSIONS_LOOKUP.get_int_to_oid_table()) - .pen_encoded(), - critical, - value, - } - } - - /// Get the value of the `Extension` in `ExtensionValue`. - #[must_use] - pub fn get_value(&self) -> &ExtensionValue { - &self.value - } - - /// Get the critical flag of the `Extension`. - #[must_use] - pub fn get_critical(&self) -> bool { - self.critical - } - - /// Get the registered OID of the `Extension`. - #[must_use] - pub fn get_registered_oid(&self) -> &C509oidRegistered { - &self.registered_oid - } -} - -/// A helper struct to deserialize and serialize `Extension`. -#[derive(Debug, Deserialize, Serialize)] -struct Helper { - /// OID string value - oid: String, - /// Extension value - value: ExtensionValue, - /// Flag to indicate whether the extension is critical - critical: bool, -} - -impl<'de> Deserialize<'de> for Extension { - fn deserialize(deserializer: D) -> Result - where D: Deserializer<'de> { - let helper = Helper::deserialize(deserializer)?; - let oid = - Oid::from_str(&helper.oid).map_err(|e| serde::de::Error::custom(format!("{e:?}")))?; - - Ok(Extension::new(oid, helper.value, helper.critical)) - } -} - -impl Serialize for Extension { - fn serialize(&self, serializer: S) -> Result - where S: serde::Serializer { - let helper = Helper { - oid: self.registered_oid.get_c509_oid().get_oid().to_string(), - value: self.value.clone(), - critical: self.critical, - }; - helper.serialize(serializer) - } -} - -impl Encode<()> for Extension { - // Extension can be encoded as: - // - (extensionID: int, extensionValue: any) - // - (extensionID: ~oid, ? critical: true, extensionValue: bytes) - // - (extensionID: pen, ? critical: true, extensionValue: bytes) - fn encode( - &self, e: &mut Encoder, ctx: &mut (), - ) -> Result<(), minicbor::encode::Error> { - // Handle CBOR int based on OID mapping - if let Some(&mapped_oid) = self - .registered_oid - .get_table() - .get_map() - .get_by_right(&self.registered_oid.get_c509_oid().get_oid()) - { - // Determine encoded OID value based on critical flag - let encoded_oid = if self.critical { - -mapped_oid - } else { - mapped_oid - }; - e.i16(encoded_oid)?; - } else { - // Handle unwrapped CBOR OID or CBOR PEN - self.registered_oid.get_c509_oid().encode(e, ctx)?; - if self.critical { - e.bool(self.critical)?; - } - } - // Encode the extension value - self.value.encode(e, ctx)?; - Ok(()) - } -} - -impl Decode<'_, ()> for Extension { - fn decode(d: &mut Decoder<'_>, ctx: &mut ()) -> Result { - match d.datatype()? { - // Check whether OID is an int - // Even the encoding is i16, the minicbor decoder doesn't know what type we encoded, - // so need to check every possible type. - minicbor::data::Type::U8 - | minicbor::data::Type::U16 - | minicbor::data::Type::I8 - | minicbor::data::Type::I16 => { - let int_value = d.i16()?; - // OID can be negative due to critical flag, so need absolute the value - let abs_int_value = int_value.abs(); - let oid = - get_oid_from_int(abs_int_value).map_err(minicbor::decode::Error::message)?; - let value_type = get_extension_type_from_int(abs_int_value) - .map_err(minicbor::decode::Error::message)?; - - // Decode extension value - let extension_value = ExtensionValue::decode(d, &mut value_type.get_type())?; - Ok(Extension::new( - oid.to_owned(), - extension_value, - int_value.is_negative(), - )) - }, - _ => { - // Handle unwrapped CBOR OID or CBOR PEN - let c509_oid = C509oid::decode(d, ctx)?; - // Critical flag is optional, so if exist, this mean we have to decode it - let critical = if d.datatype()? == minicbor::data::Type::Bool { - d.bool()? - } else { - false - }; - - // Decode bytes for extension value - let extension_value = ExtensionValue::Bytes(d.bytes()?.to_vec()); - - Ok(Extension::new( - c509_oid.get_oid(), - extension_value, - critical, - )) - }, - } - } -} - -// -----------------ExtensionValue------------------------ - -/// Trait for `ExtensionValueType` -trait ExtensionValueTypeTrait { - /// Get the type of the `ExtensionValueType`. - fn get_type(&self) -> ExtensionValueType; -} - -/// An enum of possible value types for `Extension`. -#[allow(clippy::module_name_repetitions)] -#[derive(Debug, Clone, PartialEq, EnumDiscriminants, Deserialize, Serialize)] -#[strum_discriminants(name(ExtensionValueType))] -#[serde(rename_all = "snake_case")] -pub enum ExtensionValue { - /// An Integer in the range [-2^64, 2^64-1] - Int(i64), - /// A bytes. - Bytes(Vec), - /// An Alternative Name. - AlternativeName(AlternativeName), - /// An unsupported value. - Unsupported, -} - -impl ExtensionValueTypeTrait for ExtensionValueType { - fn get_type(&self) -> ExtensionValueType { - *self - } -} - -impl Encode<()> for ExtensionValue { - fn encode( - &self, e: &mut Encoder, ctx: &mut (), - ) -> Result<(), minicbor::encode::Error> { - match self { - ExtensionValue::Int(value) => { - e.i64(*value)?; - }, - ExtensionValue::Bytes(value) => { - e.bytes(value)?; - }, - ExtensionValue::AlternativeName(value) => { - value.encode(e, ctx)?; - }, - ExtensionValue::Unsupported => { - return Err(minicbor::encode::Error::message( - "Cannot encode unsupported Extension value", - )); - }, - } - Ok(()) - } -} - -impl Decode<'_, C> for ExtensionValue -where C: ExtensionValueTypeTrait + Debug -{ - fn decode(d: &mut Decoder<'_>, ctx: &mut C) -> Result { - match ctx.get_type() { - ExtensionValueType::Int => { - let value = d.i64()?; - Ok(ExtensionValue::Int(value)) - }, - ExtensionValueType::Bytes => { - let value = d.bytes()?.to_vec(); - Ok(ExtensionValue::Bytes(value)) - }, - ExtensionValueType::AlternativeName => { - let value = AlternativeName::decode(d, &mut ())?; - Ok(ExtensionValue::AlternativeName(value)) - }, - ExtensionValueType::Unsupported => { - Err(minicbor::decode::Error::message( - "Cannot decode Unsupported extension value", - )) - }, - } - } -} - -// ------------------Test---------------------- - -#[cfg(test)] -mod test_extension { - use asn1_rs::oid; - - use super::*; - - #[test] - fn int_oid_inhibit_anypolicy_value_unsigned_int() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - - let ext = Extension::new(oid!(2.5.29 .54), ExtensionValue::Int(2), false); - ext.encode(&mut encoder, &mut ()) - .expect("Failed to encode Extension"); - // Inhibit anyPolicy : 0x181e - // 2 : 0x02 - assert_eq!(hex::encode(buffer.clone()), "181e02"); - - let mut decoder = Decoder::new(&buffer); - let decoded_ext = - Extension::decode(&mut decoder, &mut ()).expect("Failed to decode Extension"); - assert_eq!(decoded_ext, ext); - } - - #[test] - fn unwrapped_oid_critical_key_usage_value_int() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - - let ext = Extension::new(oid!(2.5.29 .15), ExtensionValue::Int(-1), true); - ext.encode(&mut encoder, &mut ()) - .expect("Failed to encode Extension"); - // Key Usage with critical true: 0x21 - // -1 : 0x20 - assert_eq!(hex::encode(buffer.clone()), "2120"); - - let mut decoder = Decoder::new(&buffer); - let decoded_ext = - Extension::decode(&mut decoder, &mut ()).expect("Failed to decode Extension"); - assert_eq!(decoded_ext, ext); - } - - #[test] - fn oid_unwrapped_value_bytes_string() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - - // Not PEN OID and not in the registry table - // Value should be bytes - let ext = Extension::new( - oid!(2.16.840 .1 .101 .3 .4 .2 .1), - ExtensionValue::Bytes("test".as_bytes().to_vec()), - false, - ); - ext.encode(&mut encoder, &mut ()) - .expect("Failed to encode Extension"); - // OID : 0x49608648016503040201 - // "test".as_bytes() : 0x4474657374 - assert_eq!( - hex::encode(buffer.clone()), - "496086480165030402014474657374" - ); - - let mut decoder = Decoder::new(&buffer); - let decoded_ext = - Extension::decode(&mut decoder, &mut ()).expect("Failed to decode Extension"); - assert_eq!(decoded_ext, ext); - } - - #[test] - fn encode_decode_mismatch_type() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - - // Subject Key Identifier should be bytes - let ext = Extension::new(oid!(2.5.29 .14), ExtensionValue::Int(2), false); - ext.encode(&mut encoder, &mut ()) - .expect("Failed to encode Extension"); - // SubjectKeyIdentifier : 0x01 - // 2 : 0x02 - assert_eq!(hex::encode(buffer.clone()), "0102"); - - let mut decoder = Decoder::new(&buffer); - // Decode should fail, because rely on the int value - Extension::decode(&mut decoder, &mut ()).expect_err("Failed to decode Extension"); - } -} diff --git a/catalyst-gateway-crates/c509-certificate/src/c509_extensions/mod.rs b/catalyst-gateway-crates/c509-certificate/src/c509_extensions/mod.rs deleted file mode 100644 index 71d71d422ec..00000000000 --- a/catalyst-gateway-crates/c509-certificate/src/c509_extensions/mod.rs +++ /dev/null @@ -1,225 +0,0 @@ -//! C509 Extension as a part of `TBSCertificate` used in C509 Certificate. -//! -//! Extension fallback of C509 OID extension -//! Given OID if not found in the registered OID table, it will be encoded as a PEN OID. -//! If the OID is not a PEN OID, it will be encoded as an unwrapped OID. -//! -//! ```cddl -//! Extensions and Extension can be encoded as the following: -//! Extensions = [ * Extension ] / int -//! Extension = ( extensionID: int, extensionValue: any ) // -//! ( extensionID: ~oid, ? critical: true, -//! extensionValue: bytes ) // -//! ( extensionID: pen, ? critical: true, -//! extensionValue: bytes ) -//! ``` -//! -//! For more information about Extensions, -//! visit [C509 Certificate](https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/09/) - -pub mod alt_name; -pub mod extension; - -use std::fmt::Debug; - -use asn1_rs::{oid, Oid}; -use extension::{Extension, ExtensionValue}; -use minicbor::{encode::Write, Decode, Decoder, Encode, Encoder}; -use serde::{Deserialize, Serialize}; - -/// OID of `KeyUsage` extension -static KEY_USAGE_OID: Oid<'static> = oid!(2.5.29 .15); - -/// A struct of C509 Extensions containing a vector of `Extension`. -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] -pub struct Extensions(Vec); - -impl Default for Extensions { - fn default() -> Self { - Self::new() - } -} - -impl Extensions { - /// Create a new instance of `Extensions` as empty vector. - #[must_use] - pub fn new() -> Self { - Self(Vec::new()) - } - - /// Add an `Extension` to the `Extensions`. - pub fn add_ext(&mut self, extension: Extension) { - self.0.push(extension); - } -} - -impl Encode<()> for Extensions { - fn encode( - &self, e: &mut Encoder, ctx: &mut (), - ) -> Result<(), minicbor::encode::Error> { - // If there is only one extension and it is KeyUsage, encode as int - // encoding as absolute value of the second int and the sign of the first int - if let Some(extension) = self.0.first() { - if self.0.len() == 1 - && extension.get_registered_oid().get_c509_oid().get_oid() == KEY_USAGE_OID - { - match extension.get_value() { - ExtensionValue::Int(value) => { - let ku_value = if extension.get_critical() { - -value - } else { - *value - }; - e.i64(ku_value)?; - return Ok(()); - }, - _ => { - return Err(minicbor::encode::Error::message( - "KeyUsage extension value should be an integer", - )); - }, - } - } - } - // Else handle the array of `Extension` - e.array(self.0.len() as u64)?; - for extension in &self.0 { - extension.encode(e, ctx)?; - } - Ok(()) - } -} - -impl Decode<'_, ()> for Extensions { - fn decode(d: &mut Decoder<'_>, _ctx: &mut ()) -> Result { - // If only KeyUsage is in the extension -> will only contain an int - if d.datatype()? == minicbor::data::Type::U8 || d.datatype()? == minicbor::data::Type::I8 { - // Check if it's a negative number (critical extension) - let critical = d.datatype()? == minicbor::data::Type::I8; - // Note that 'KeyUsage' BIT STRING is interpreted as an unsigned integer, - // so we can absolute the value - let value = d.i64()?.abs(); - - let extension_value = ExtensionValue::Int(value); - let mut extensions = Extensions::new(); - extensions.add_ext(Extension::new( - KEY_USAGE_OID.clone(), - extension_value, - critical, - )); - return Ok(extensions); - } - // Handle array of extensions - let len = d - .array()? - .ok_or_else(|| minicbor::decode::Error::message("Failed to get array length"))?; - let mut extensions = Extensions::new(); - for _ in 0..len { - let extension = Extension::decode(d, &mut ())?; - extensions.add_ext(extension); - } - - Ok(extensions) - } -} - -// ------------------Test---------------------- - -#[cfg(test)] -mod test_extensions { - use super::*; - - #[test] - fn one_extension_key_usage() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - - let mut exts = Extensions::new(); - exts.add_ext(Extension::new( - oid!(2.5.29 .15), - ExtensionValue::Int(2), - false, - )); - exts.encode(&mut encoder, &mut ()) - .expect("Failed to encode Extensions"); - // 1 extension - // value 2 : 0x02 - assert_eq!(hex::encode(buffer.clone()), "02"); - - let mut decoder = Decoder::new(&buffer); - let decoded_exts = - Extensions::decode(&mut decoder, &mut ()).expect("Failed to decode Extensions"); - assert_eq!(decoded_exts, exts); - } - - #[test] - fn one_extension_key_usage_set_critical() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - - let mut exts = Extensions::new(); - exts.add_ext(Extension::new( - oid!(2.5.29 .15), - ExtensionValue::Int(2), - true, - )); - exts.encode(&mut encoder, &mut ()) - .expect("Failed to encode Extensions"); - // 1 extension - // value -2 : 0x21 - assert_eq!(hex::encode(buffer.clone()), "21"); - - let mut decoder = Decoder::new(&buffer); - let decoded_exts = - Extensions::decode(&mut decoder, &mut ()).expect("Failed to decode Extensions"); - assert_eq!(decoded_exts, exts); - } - - #[test] - fn multiple_extensions() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - - let mut exts = Extensions::new(); - exts.add_ext(Extension::new( - oid!(2.5.29 .15), - ExtensionValue::Int(2), - false, - )); - - exts.add_ext(Extension::new( - oid!(2.5.29 .14), - ExtensionValue::Bytes([1, 2, 3, 4].to_vec()), - false, - )); - exts.encode(&mut encoder, &mut ()) - .expect("Failed to encode Extensions"); - - // 2 extensions (array of 2): 0x82 - // KeyUsage with value 2: 0x0202 - // SubjectKeyIdentifier with value [1,2,3,4]: 0x0401020304 - assert_eq!(hex::encode(buffer.clone()), "820202014401020304"); - - let mut decoder = Decoder::new(&buffer); - let decoded_exts = - Extensions::decode(&mut decoder, &mut ()).expect("Failed to decode Extensions"); - assert_eq!(decoded_exts, exts); - } - - #[test] - fn zero_extensions() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - - let exts = Extensions::new(); - exts.encode(&mut encoder, &mut ()) - .expect("Failed to encode Extensions"); - assert_eq!(hex::encode(buffer.clone()), "80"); - - let mut decoder = Decoder::new(&buffer); - // Extensions can have 0 length - let decoded_exts = - Extensions::decode(&mut decoder, &mut ()).expect("Failed to decode Extensions"); - assert_eq!(decoded_exts, exts); - } -} diff --git a/catalyst-gateway-crates/c509-certificate/src/c509_general_names/data.rs b/catalyst-gateway-crates/c509-certificate/src/c509_general_names/data.rs deleted file mode 100644 index 71ff7649492..00000000000 --- a/catalyst-gateway-crates/c509-certificate/src/c509_general_names/data.rs +++ /dev/null @@ -1,132 +0,0 @@ -//! General Name data provides a necessary information for encoding and decoding of C509 -//! General Name. See [C509 Certificate](https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/09/) -//! Section 9.9 C509 General Names Registry for more information. - -// cspell: words Gntr Gnvt - -use std::collections::HashMap; - -use anyhow::Error; -use bimap::BiMap; -use once_cell::sync::Lazy; - -use super::general_name::{GeneralNameTypeRegistry, GeneralNameValueType}; -use crate::tables::{IntTable, TableTrait}; - -/// Type of `GeneralName` data. -/// Int | Name | Type -type GeneralNameDataTuple = (i16, GeneralNameTypeRegistry, GeneralNameValueType); - -/// Create a type alias for `GeneralNameTypeRegistry` -type Gntr = GeneralNameTypeRegistry; -/// Create a type alias for `GeneralNameValueType` -type Gnvt = GeneralNameValueType; - -/// `GeneralName` data table. -#[rustfmt::skip] -const GENERAL_NAME_DATA: [GeneralNameDataTuple; 10] = [ - // Int | Name | Type - (-3, Gntr::OtherNameBundleEID, Gnvt::Unsupported), - (-2, Gntr::OtherNameSmtpUTF8Mailbox, Gnvt::Text), - (-1, Gntr::OtherNameHardwareModuleName, Gnvt::OtherNameHWModuleName), - (0, Gntr::OtherName, Gnvt::OtherNameHWModuleName), - (1, Gntr::Rfc822Name, Gnvt::Text), - (2, Gntr::DNSName, Gnvt::Text), - (4, Gntr::DirectoryName, Gnvt::Name), - (6, Gntr::UniformResourceIdentifier, Gnvt::Text), - (7, Gntr::IPAddress, Gnvt::Bytes), - (8, Gntr::RegisteredID, Gnvt::Oid), -]; - -/// A struct of data that contains lookup table for `GeneralName`. -pub(crate) struct GeneralNameData { - /// A table of integer to `GeneralNameTypeRegistry`, provide a bidirectional lookup. - int_to_name_table: IntegerToGNTable, - /// A table of integer to `GeneralNameValueType`, provide a lookup for the type of - /// `GeneralName` value. - int_to_type_table: HashMap, -} - -impl GeneralNameData { - /// Get the `int_to_name_table`. - pub(crate) fn get_int_to_name_table(&self) -> &IntegerToGNTable { - &self.int_to_name_table - } - - /// Get the `int_to_type_table`. - pub(crate) fn get_int_to_type_table(&self) -> &HashMap { - &self.int_to_type_table - } -} - -/// A struct of integer to `GeneralNameTypeRegistry` table. -#[derive(Debug, Clone, PartialEq)] -pub(crate) struct IntegerToGNTable(IntTable); - -impl IntegerToGNTable { - /// Create a new instance of `IntegerToGNTable`. - pub(crate) fn new() -> Self { - Self(IntTable::::new()) - } - - /// Add a new integer to `GeneralNameTypeRegistry` map table. - pub(crate) fn add(&mut self, k: i16, v: GeneralNameTypeRegistry) { - self.0.add(k, v); - } - - /// Get the map table of integer to `GeneralNameTypeRegistry`. - pub(crate) fn get_map(&self) -> &BiMap { - self.0.get_map() - } -} - -/// Define static lookup for general names table -static GENERAL_NAME_TABLES: Lazy = Lazy::new(|| { - let mut int_to_name_table = IntegerToGNTable::new(); - let mut int_to_type_table = HashMap::new(); - - for data in GENERAL_NAME_DATA { - int_to_name_table.add(data.0, data.1); - int_to_type_table.insert(data.0, data.2); - } - - GeneralNameData { - int_to_name_table, - int_to_type_table, - } -}); - -/// Get the general name from the int value. -pub(crate) fn get_gn_from_int(i: i16) -> Result { - GENERAL_NAME_TABLES - .get_int_to_name_table() - .get_map() - .get_by_left(&i) - .ok_or(Error::msg(format!( - "GeneralName not found in the general name registry table given int {i}" - ))) - .cloned() -} - -/// Get the int value from the general name. -pub(crate) fn get_int_from_gn(gn: GeneralNameTypeRegistry) -> Result { - GENERAL_NAME_TABLES - .get_int_to_name_table() - .get_map() - .get_by_right(&gn) - .ok_or(Error::msg(format!( - "Int value not found in the general name registry table given GeneralName {gn:?}" - ))) - .cloned() -} - -/// Get the general name value type from the int value. -pub(crate) fn get_gn_value_type_from_int(i: i16) -> Result { - GENERAL_NAME_TABLES - .get_int_to_type_table() - .get(&i) - .ok_or(Error::msg(format!( - "General name value type not found in the general name registry table given {i}" - ))) - .cloned() -} diff --git a/catalyst-gateway-crates/c509-certificate/src/c509_general_names/general_name.rs b/catalyst-gateway-crates/c509-certificate/src/c509_general_names/general_name.rs deleted file mode 100644 index 9d7b06ecb2d..00000000000 --- a/catalyst-gateway-crates/c509-certificate/src/c509_general_names/general_name.rs +++ /dev/null @@ -1,330 +0,0 @@ -//! C509 General Name -//! -//! For more information about `GeneralName`, -//! visit [C509 Certificate](https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/09/) - -use std::fmt::Debug; - -use minicbor::{encode::Write, Decode, Decoder, Encode, Encoder}; -use serde::{Deserialize, Serialize}; -use strum_macros::{EnumDiscriminants, EnumIs}; - -use super::{ - data::{get_gn_from_int, get_gn_value_type_from_int, get_int_from_gn}, - other_name_hw_module::OtherNameHardwareModuleName, -}; -use crate::{c509_name::Name, c509_oid::C509oid}; - -/// A struct represents a `GeneralName`. -/// ```cddl -/// GeneralName = ( GeneralNameType : int, GeneralNameValue : any ) -/// ``` -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] -pub struct GeneralName { - /// A registered general name type. - gn_type: GeneralNameTypeRegistry, - /// A general name value. - value: GeneralNameValue, -} - -#[allow(dead_code)] -impl GeneralName { - /// Create a new instance of `GeneralName`. - #[must_use] - pub fn new(gn_type: GeneralNameTypeRegistry, value: GeneralNameValue) -> Self { - Self { gn_type, value } - } - - /// Get the `GeneralName` type. - #[must_use] - pub fn get_gn_type(&self) -> &GeneralNameTypeRegistry { - &self.gn_type - } - - /// Get the value of the `GeneralName` in `GeneralNameValue`. - #[must_use] - pub fn get_gn_value(&self) -> &GeneralNameValue { - &self.value - } -} - -impl Encode<()> for GeneralName { - fn encode( - &self, e: &mut Encoder, ctx: &mut (), - ) -> Result<(), minicbor::encode::Error> { - // Encode GeneralNameType as int - let i = get_int_from_gn(self.gn_type).map_err(minicbor::encode::Error::message)?; - e.i16(i)?; - // Encode GeneralNameValue as its type - self.value.encode(e, ctx)?; - Ok(()) - } -} - -impl Decode<'_, ()> for GeneralName { - fn decode(d: &mut Decoder<'_>, _ctx: &mut ()) -> Result { - if minicbor::data::Type::U8 == d.datatype()? || minicbor::data::Type::I8 == d.datatype()? { - let i = d.i16()?; - let gn = get_gn_from_int(i).map_err(minicbor::decode::Error::message)?; - let value_type = - get_gn_value_type_from_int(i).map_err(minicbor::decode::Error::message)?; - Ok(GeneralName::new( - gn, - GeneralNameValue::decode(d, &mut value_type.get_type())?, - )) - } else { - // GeneralName is not type int - Err(minicbor::decode::Error::message( - "GeneralName id type invalid, expected int", - )) - } - } -} - -// -----------------GeneralNameTypeRegistry------------------------ - -/// Enum of `GeneralName` registered in table Section 9.9 C509. -#[allow(clippy::module_name_repetitions)] -#[derive(Debug, Copy, PartialEq, Clone, Eq, Hash, EnumIs, Deserialize, Serialize)] -pub enum GeneralNameTypeRegistry { - /// An otherName with `BundleEID`. - OtherNameBundleEID, // EID - /// An otherName with `SmtpUTF8Mailbox`. - OtherNameSmtpUTF8Mailbox, - /// An otherName with `HardwareModuleName`. - OtherNameHardwareModuleName, - /// An otherName. - OtherName, - /// A rfc822Name. - Rfc822Name, - /// A dNSName. - DNSName, - /// A directoryName. - DirectoryName, - /// A uniformResourceIdentifier. - UniformResourceIdentifier, - /// An iPAddress. - IPAddress, - /// A registeredID. - RegisteredID, -} - -// -------------------GeneralNameValue---------------------- - -/// An enum of possible value types for `GeneralName`. -#[allow(clippy::module_name_repetitions)] -#[derive(Debug, PartialEq, Clone, EnumDiscriminants, Deserialize, Serialize)] -#[strum_discriminants(name(GeneralNameValueType))] -#[serde(rename_all = "snake_case")] -pub enum GeneralNameValue { - /// A text string. - Text(String), - /// A otherName + hardwareModuleName. - OtherNameHWModuleName(OtherNameHardwareModuleName), - /// A bytes. - Bytes(Vec), - /// An OID - Oid(C509oid), - /// Name - Name(Name), - /// An unsupported value. - Unsupported, -} - -/// Trait for `GeneralNameValueType` -trait GeneralNameValueTrait { - /// Get the type of the `GeneralNameValueType`. - fn get_type(&self) -> GeneralNameValueType; -} - -impl GeneralNameValueTrait for GeneralNameValueType { - fn get_type(&self) -> GeneralNameValueType { - *self - } -} - -impl Encode<()> for GeneralNameValue { - fn encode( - &self, e: &mut Encoder, ctx: &mut (), - ) -> Result<(), minicbor::encode::Error> { - match self { - GeneralNameValue::Text(value) => { - e.str(value)?; - }, - GeneralNameValue::Bytes(value) => { - e.bytes(value)?; - }, - GeneralNameValue::Oid(value) => { - value.encode(e, ctx)?; - }, - GeneralNameValue::OtherNameHWModuleName(value) => { - value.encode(e, ctx)?; - }, - GeneralNameValue::Name(value) => { - Name::encode(value, e, ctx)?; - }, - GeneralNameValue::Unsupported => { - return Err(minicbor::encode::Error::message( - "Cannot encode unsupported GeneralName value", - )) - }, - }; - Ok(()) - } -} -impl Decode<'_, C> for GeneralNameValue -where C: GeneralNameValueTrait + Debug -{ - fn decode(d: &mut Decoder<'_>, ctx: &mut C) -> Result { - match ctx.get_type() { - GeneralNameValueType::Text => { - let value = d.str()?.to_string(); - Ok(GeneralNameValue::Text(value)) - }, - GeneralNameValueType::Bytes => { - let value = d.bytes()?.to_vec(); - Ok(GeneralNameValue::Bytes(value)) - }, - GeneralNameValueType::Oid => { - let value = C509oid::decode(d, &mut ())?; - Ok(GeneralNameValue::Oid(value)) - }, - GeneralNameValueType::OtherNameHWModuleName => { - let value = OtherNameHardwareModuleName::decode(d, &mut ())?; - Ok(GeneralNameValue::OtherNameHWModuleName(value)) - }, - GeneralNameValueType::Name => { - let value = Name::decode(d, &mut ())?; - Ok(GeneralNameValue::Name(value)) - }, - GeneralNameValueType::Unsupported => { - Err(minicbor::decode::Error::message( - "Cannot decode Unsupported GeneralName value", - )) - }, - } - } -} - -// ------------------Test---------------------- - -#[cfg(test)] -mod test_general_name { - use std::net::Ipv4Addr; - - use asn1_rs::oid; - - use super::*; - - #[test] - fn encode_decode_text() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - - let gn = GeneralName::new( - GeneralNameTypeRegistry::DNSName, - GeneralNameValue::Text("example.com".to_string()), - ); - gn.encode(&mut encoder, &mut ()) - .expect("Failed to encode GeneralName"); - // DNSName: 0x02 - // "example.com": 0x6b6578616d706c652e636f6d - assert_eq!(hex::encode(buffer.clone()), "026b6578616d706c652e636f6d"); - - let mut decoder = Decoder::new(&buffer); - let gn_decoded = - GeneralName::decode(&mut decoder, &mut ()).expect("Failed to decode GeneralName"); - assert_eq!(gn_decoded, gn); - } - - #[test] - fn encode_decode_hw_module_name() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - - let hw = OtherNameHardwareModuleName::new(oid!(2.16.840 .1 .101 .3 .4 .2 .1), vec![ - 0x01, 0x02, 0x03, 0x04, - ]); - let gn = GeneralName::new( - GeneralNameTypeRegistry::OtherNameHardwareModuleName, - GeneralNameValue::OtherNameHWModuleName(hw), - ); - gn.encode(&mut encoder, &mut ()) - .expect("Failed to encode GeneralName"); - // OtherNameHardwareModuleName: 0x20 - // [ ~oid, bytes ] = 0x82496086480165030402014401020304 - assert_eq!( - hex::encode(buffer.clone()), - "2082496086480165030402014401020304" - ); - - let mut decoder = Decoder::new(&buffer); - let gn_decoded = - GeneralName::decode(&mut decoder, &mut ()).expect("Failed to decode GeneralName"); - assert_eq!(gn_decoded, gn); - } - - #[test] - fn encode_decode_ip() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - - let ipv4 = Ipv4Addr::new(192, 168, 1, 1); - let gn = GeneralName::new( - GeneralNameTypeRegistry::IPAddress, - GeneralNameValue::Bytes(ipv4.octets().to_vec()), - ); - - gn.encode(&mut encoder, &mut ()) - .expect("Failed to encode GeneralName"); - // IPAddress: 0x07 - // 192.168.1.1 bytes: 0x44c0a8010 - assert_eq!(hex::encode(buffer.clone()), "0744c0a80101"); - - let mut decoder = Decoder::new(&buffer); - let gn_decoded = - GeneralName::decode(&mut decoder, &mut ()).expect("Failed to decode GeneralName"); - assert_eq!(gn_decoded, gn); - } - - #[test] - fn encode_decode_oid() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - - let gn = GeneralName::new( - GeneralNameTypeRegistry::RegisteredID, - GeneralNameValue::Oid(C509oid::new(oid!(2.16.840 .1 .101 .3 .4 .2 .1))), - ); - gn.encode(&mut encoder, &mut ()) - .expect("Failed to encode GeneralName"); - // RegisteredID: 0x08 - // oid: 49608648016503040201 - assert_eq!(hex::encode(buffer.clone()), "0849608648016503040201"); - - let mut decoder = Decoder::new(&buffer); - let gn_decoded = - GeneralName::decode(&mut decoder, &mut ()).expect("Failed to decode GeneralName"); - assert_eq!(gn_decoded, gn); - } - - #[test] - fn encode_decode_mismatch_type() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - - let gn = GeneralName::new( - GeneralNameTypeRegistry::OtherNameSmtpUTF8Mailbox, - GeneralNameValue::Oid(C509oid::new(oid!(2.16.840 .1 .101 .3 .4 .2 .1))), - ); - gn.encode(&mut encoder, &mut ()) - .expect("Failed to encode GeneralName"); - // OtherNameSmtpUTF8Mailbox: 0x21 - // oid: 49608648016503040201 - assert_eq!(hex::encode(buffer.clone()), "2149608648016503040201"); - - let mut decoder = Decoder::new(&buffer); - // Decode should fail, because rely on the int value - GeneralName::decode(&mut decoder, &mut ()).expect_err("Failed to decode GeneralName"); - } -} diff --git a/catalyst-gateway-crates/c509-certificate/src/c509_general_names/mod.rs b/catalyst-gateway-crates/c509-certificate/src/c509_general_names/mod.rs deleted file mode 100644 index f5778fb0142..00000000000 --- a/catalyst-gateway-crates/c509-certificate/src/c509_general_names/mod.rs +++ /dev/null @@ -1,168 +0,0 @@ -//! C509 General Names -//! -//! For more information about `GeneralNames`, -//! visit [C509 Certificate](https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/09/) - -mod data; -pub mod general_name; -pub mod other_name_hw_module; -use general_name::GeneralName; -use minicbor::{encode::Write, Decode, Decoder, Encode, Encoder}; -use serde::{Deserialize, Serialize}; - -/// A struct represents an array of `GeneralName`. -/// -/// ```cddl -/// GeneralNames = [ + GeneralName ] -/// ``` -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] -pub struct GeneralNames(Vec); - -impl Default for GeneralNames { - fn default() -> Self { - Self::new() - } -} - -impl GeneralNames { - /// Create a new instance of `GeneralNames` as empty vector. - #[must_use] - pub fn new() -> Self { - Self(Vec::new()) - } - - /// Add a new `GeneralName` to the `GeneralNames`. - pub fn add_gn(&mut self, gn: GeneralName) { - self.0.push(gn); - } - - /// Get the a vector of `GeneralName`. - pub(crate) fn get_gns(&self) -> &Vec { - &self.0 - } -} - -impl Encode<()> for GeneralNames { - fn encode( - &self, e: &mut Encoder, ctx: &mut (), - ) -> Result<(), minicbor::encode::Error> { - if self.0.is_empty() { - return Err(minicbor::encode::Error::message( - "GeneralNames should not be empty", - )); - } - // The general name type should be included in array too - e.array(self.0.len() as u64 * 2)?; - for gn in &self.0 { - gn.encode(e, ctx)?; - } - Ok(()) - } -} - -impl Decode<'_, ()> for GeneralNames { - fn decode(d: &mut Decoder<'_>, ctx: &mut ()) -> Result { - let len = d.array()?.ok_or(minicbor::decode::Error::message( - "GeneralNames should be an array", - ))?; - let mut gn = GeneralNames::new(); - for _ in 0..len / 2 { - gn.add_gn(GeneralName::decode(d, ctx)?); - } - Ok(gn) - } -} - -// ------------------Test---------------------- - -#[cfg(test)] -mod test_general_names { - - use std::net::Ipv4Addr; - - use asn1_rs::oid; - use general_name::{GeneralNameTypeRegistry, GeneralNameValue}; - use other_name_hw_module::OtherNameHardwareModuleName; - - use super::*; - use crate::c509_oid::C509oid; - - #[test] - fn encode_decode_gns() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - - let mut gns = GeneralNames::new(); - gns.add_gn(GeneralName::new( - GeneralNameTypeRegistry::DNSName, - GeneralNameValue::Text("example.com".to_string()), - )); - gns.add_gn(GeneralName::new( - GeneralNameTypeRegistry::OtherNameHardwareModuleName, - GeneralNameValue::OtherNameHWModuleName(OtherNameHardwareModuleName::new( - oid!(2.16.840 .1 .101 .3 .4 .2 .1), - vec![0x01, 0x02, 0x03, 0x04], - )), - )); - gns.add_gn(GeneralName::new( - GeneralNameTypeRegistry::IPAddress, - GeneralNameValue::Bytes(Ipv4Addr::new(192, 168, 1, 1).octets().to_vec()), - )); - gns.add_gn(GeneralName::new( - GeneralNameTypeRegistry::RegisteredID, - GeneralNameValue::Oid(C509oid::new(oid!(2.16.840 .1 .101 .3 .4 .2 .1))), - )); - gns.encode(&mut encoder, &mut ()) - .expect("Failed to encode GeneralNames"); - // Array of 4 GeneralName (type, value) so 8 items: 0x88 - assert_eq!(hex::encode(buffer.clone()), "88026b6578616d706c652e636f6d20824960864801650304020144010203040744c0a801010849608648016503040201"); - - let mut decoder = Decoder::new(&buffer); - let gns_decoded = - GeneralNames::decode(&mut decoder, &mut ()).expect("Failed to decode GeneralName"); - assert_eq!(gns_decoded, gns); - } - - #[test] - fn encode_decode_gns_with_same_gn_type() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - - let mut gns = GeneralNames::new(); - gns.add_gn(GeneralName::new( - GeneralNameTypeRegistry::DNSName, - GeneralNameValue::Text("example.com".to_string()), - )); - gns.add_gn(GeneralName::new( - GeneralNameTypeRegistry::DNSName, - GeneralNameValue::Text("example.com".to_string()), - )); - gns.add_gn(GeneralName::new( - GeneralNameTypeRegistry::DNSName, - GeneralNameValue::Text("example.com".to_string()), - )); - gns.encode(&mut encoder, &mut ()) - .expect("Failed to encode GeneralNames"); - // Array of 3 GeneralName (type, value) so 6 items: 0x86 - // DNSName with "example.com": 0x026b6578616d706c652e636f6d - assert_eq!( - hex::encode(buffer.clone()), - "86026b6578616d706c652e636f6d026b6578616d706c652e636f6d026b6578616d706c652e636f6d" - ); - - let mut decoder = Decoder::new(&buffer); - let gns_decoded = - GeneralNames::decode(&mut decoder, &mut ()).expect("Failed to decode GeneralName"); - assert_eq!(gns_decoded, gns); - } - - #[test] - fn encode_decode_gns_empty() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - - let gns = GeneralNames::new(); - gns.encode(&mut encoder, &mut ()) - .expect_err("GeneralNames should not be empty"); - } -} diff --git a/catalyst-gateway-crates/c509-certificate/src/c509_general_names/other_name_hw_module.rs b/catalyst-gateway-crates/c509-certificate/src/c509_general_names/other_name_hw_module.rs deleted file mode 100644 index 745b3cca0da..00000000000 --- a/catalyst-gateway-crates/c509-certificate/src/c509_general_names/other_name_hw_module.rs +++ /dev/null @@ -1,55 +0,0 @@ -//! `OtherNameHardwareModuleName`, special type for `hardwareModuleName` type of -//! otherName. When 'otherName + hardwareModuleName' is used, then `[ ~oid, bytes ]` is -//! used to contain the pair ( hwType, hwSerialNum ) directly as specified in -//! [RFC4108](https://datatracker.ietf.org/doc/rfc4108/) - -use asn1_rs::Oid; -use minicbor::{encode::Write, Decode, Decoder, Encode, Encoder}; -use serde::{Deserialize, Serialize}; - -use crate::c509_oid::C509oid; - -/// A struct represents the hardwareModuleName type of otherName. -/// Containing a pair of ( hwType, hwSerialNum ) as mentioned in -/// [RFC4108](https://datatracker.ietf.org/doc/rfc4108/) -#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)] -pub struct OtherNameHardwareModuleName { - /// The hardware type OID. - hw_type: C509oid, - /// The hardware serial number represent in bytes. - hw_serial_num: Vec, -} - -impl OtherNameHardwareModuleName { - /// Create a new instance of `OtherNameHardwareModuleName`. - #[must_use] - pub fn new(hw_type: Oid<'static>, hw_serial_num: Vec) -> Self { - Self { - hw_type: C509oid::new(hw_type), - hw_serial_num, - } - } -} - -impl Encode<()> for OtherNameHardwareModuleName { - fn encode( - &self, e: &mut Encoder, ctx: &mut (), - ) -> Result<(), minicbor::encode::Error> { - e.array(2)?; - self.hw_type.encode(e, ctx)?; - e.bytes(&self.hw_serial_num)?; - Ok(()) - } -} - -impl<'a> Decode<'a, ()> for OtherNameHardwareModuleName { - fn decode(d: &mut Decoder<'a>, ctx: &mut ()) -> Result { - d.array()?; - let hw_type = C509oid::decode(d, ctx)?; - let hw_serial_num = d.bytes()?.to_vec(); - Ok(OtherNameHardwareModuleName::new( - hw_type.get_oid(), - hw_serial_num, - )) - } -} diff --git a/catalyst-gateway-crates/c509-certificate/src/c509_issuer_sig_algo/data.rs b/catalyst-gateway-crates/c509-certificate/src/c509_issuer_sig_algo/data.rs deleted file mode 100644 index 1d0e77bf392..00000000000 --- a/catalyst-gateway-crates/c509-certificate/src/c509_issuer_sig_algo/data.rs +++ /dev/null @@ -1,82 +0,0 @@ -//! Signature algorithm data provides a necessary information for encoding and decoding of -//! C509 `issuerSignatureAlgorithm`. See [C509 Certificate](https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/09/) -//! Section 9.10 C509 Signature Algorithms Registry for more information. - -// cspell: words RSASSA XMSS - -use anyhow::Error; -use asn1_rs::{oid, Oid}; -use once_cell::sync::Lazy; - -use crate::tables::IntegerToOidTable; - -/// Type of algorithm data -/// INT | OID | Name -type AlgorithmDataTuple = (i16, Oid<'static>, &'static str); - -/// Signature algorithm data table. -#[rustfmt::skip] -const SIG_ALGO_DATA: [AlgorithmDataTuple; 22] = [ - // Int | OID | Name - (-256, oid!(1.2.840.113549.1.1.5), "RSASSA-PKCS1-v1_5 with SHA-1"), - (-255, oid!(1.2.840.10045.4.1), "ECDSA with SHA-1"), - (0, oid!(1.2.840.10045.4.3.2), "ECDSA with SHA-256"), - (1, oid!(1.2.840.10045.4.3.3), "ECDSA with SHA-384"), - (2, oid!(1.2.840.10045.4.3.4), "ECDSA with SHA-512"), - (3, oid!(1.3.6.1.5.5.7.6.32), "ECDSA with SHAKE128"), - (4, oid!(1.3.6.1.5.5.7.6.33), "ECDSA with SHAKE256"), - (12, oid!(1.3.101.112), "Ed25519"), - (13, oid!(1.3.101.113), "Ed448"), - (14, oid!(1.3.6.1.5.5.7.6.26), "SHA-256 with HMAC-SHA256"), - (15, oid!(1.3.6.1.5.5.7.6.27), "SHA-384 with HMAC-SHA384"), - (16, oid!(1.3.6.1.5.5.7.6.28), "SHA-512 with HMAC-SHA512"), - (23, oid!(1.2.840.113549.1.1.11), "RSASSA-PKCS1-v1_5 with SHA-256"), - (24, oid!(1.2.840.113549.1.1.12), "RSASSA-PKCS1-v1_5 with SHA-384"), - (25, oid!(1.2.840.113549.1.1.13), "RSASSA-PKCS1-v1_5 with SHA-512"), - (26, oid!(1.2.840.113549.1.1.10), "RSASSA-PSS with SHA-256"), - // (27, oid!(1.2.840.113549.1.1.10), "RSASSA-PSS with SHA-384"), - // (28, oid!(1.2.840.113549.1.1.10), "RSASSA-PSS with SHA-512"), - (29, oid!(1.3.6.1.5.5.7.6.30), "RSASSA-PSS with SHAKE128"), - (30, oid!(1.3.6.1.5.5.7.6.3), "RSASSA-PSS with SHAKE256"), - (42, oid!(1.2.840.113549.1.9.16.3.17), "HSS / LMS"), - (43, oid!(0.4.0.127.0.15.1.1.13.0), "XMSS"), - (44, oid!(0.4.0.127.0.15.1.1.14.0), "XMSS^MT"), - (45, oid!(1.2.156.10197.1.501), "SM2 with SM3"), -]; - -/// A struct of data that contains lookup table of integer to OID in -/// bidirectional way for `IssuerSignatureAlgorithm`. -pub(crate) struct IssuerSigAlgoData(IntegerToOidTable); - -impl IssuerSigAlgoData { - /// Get the `IntegerToOidTable` - pub(crate) fn get_int_to_oid_table(&self) -> &IntegerToOidTable { - &self.0 - } -} - -/// Define static lookup for issuer signature algorithm table -static ISSUER_SIG_ALGO_TABLE: Lazy = Lazy::new(|| { - let mut int_to_oid_table = IntegerToOidTable::new(); - - for data in SIG_ALGO_DATA { - int_to_oid_table.add(data.0, data.1); - } - - IssuerSigAlgoData(int_to_oid_table) -}); - -/// Static reference to the `IssuerSigAlgoData` lookup table. -pub(crate) static ISSUER_SIG_ALGO_LOOKUP: &Lazy = &ISSUER_SIG_ALGO_TABLE; - -/// Get the OID from the int value. -pub(crate) fn get_oid_from_int(i: i16) -> Result, Error> { - ISSUER_SIG_ALGO_TABLE - .get_int_to_oid_table() - .get_map() - .get_by_left(&i) - .ok_or(Error::msg(format!( - "OID not found in the signature algorithms registry table given int {i}" - ))) - .cloned() -} diff --git a/catalyst-gateway-crates/c509-certificate/src/c509_issuer_sig_algo/mod.rs b/catalyst-gateway-crates/c509-certificate/src/c509_issuer_sig_algo/mod.rs deleted file mode 100644 index 33e67941c45..00000000000 --- a/catalyst-gateway-crates/c509-certificate/src/c509_issuer_sig_algo/mod.rs +++ /dev/null @@ -1,178 +0,0 @@ -//! C509 Issuer Signature Algorithm as a part of `TBSCertificate` used in C509 -//! Certificate. -//! -//! ```cddl -//! issuerSignatureAlgorithm: AlgorithmIdentifier -//! ``` - -mod data; - -use std::str::FromStr; - -use asn1_rs::Oid; -use data::{get_oid_from_int, ISSUER_SIG_ALGO_LOOKUP}; -use minicbor::{encode::Write, Decode, Decoder, Encode, Encoder}; -use serde::{Deserialize, Deserializer, Serialize}; - -use crate::{c509_algo_identifier::AlgorithmIdentifier, c509_oid::C509oidRegistered}; - -/// A struct represents the `IssuerSignatureAlgorithm` -#[derive(Debug, Clone, PartialEq)] -pub struct IssuerSignatureAlgorithm { - /// The registered OID of the `IssuerSignatureAlgorithm`. - registered_oid: C509oidRegistered, - /// An `AlgorithmIdentifier` type - algo_identifier: AlgorithmIdentifier, -} - -impl IssuerSignatureAlgorithm { - /// Create new instance of `IssuerSignatureAlgorithm` where it registered with - /// Issuer Signature Algorithm lookup table. - pub fn new(oid: Oid<'static>, param: Option) -> Self { - Self { - registered_oid: C509oidRegistered::new( - oid.clone(), - ISSUER_SIG_ALGO_LOOKUP.get_int_to_oid_table(), - ), - algo_identifier: AlgorithmIdentifier::new(oid, param), - } - } -} -/// Helper struct for deserialize and serialize `IssuerSignatureAlgorithm`. -#[derive(Debug, Deserialize, Serialize)] -struct Helper { - /// OID as string. - oid: String, - /// Optional parameter. - param: Option, -} - -impl<'de> Deserialize<'de> for IssuerSignatureAlgorithm { - fn deserialize(deserializer: D) -> Result - where D: Deserializer<'de> { - let helper = Helper::deserialize(deserializer)?; - let oid = - Oid::from_str(&helper.oid).map_err(|e| serde::de::Error::custom(format!("{e:?}")))?; - - Ok(IssuerSignatureAlgorithm::new(oid, helper.param)) - } -} - -impl Serialize for IssuerSignatureAlgorithm { - fn serialize(&self, serializer: S) -> Result - where S: serde::Serializer { - let helper = Helper { - oid: self.registered_oid.get_c509_oid().get_oid().to_string(), - param: self.algo_identifier.get_param().clone(), - }; - helper.serialize(serializer) - } -} - -impl Encode<()> for IssuerSignatureAlgorithm { - fn encode( - &self, e: &mut Encoder, ctx: &mut (), - ) -> Result<(), minicbor::encode::Error> { - if let Some(&i) = self - .registered_oid - .get_table() - .get_map() - .get_by_right(&self.registered_oid.get_c509_oid().get_oid()) - { - e.i16(i)?; - } else { - AlgorithmIdentifier::encode(&self.algo_identifier, e, ctx)?; - } - Ok(()) - } -} - -impl Decode<'_, ()> for IssuerSignatureAlgorithm { - fn decode(d: &mut Decoder<'_>, ctx: &mut ()) -> Result { - match d.datatype()? { - // Check i16 for -256 and -256 - minicbor::data::Type::U8 | minicbor::data::Type::I16 => { - let i = d.i16()?; - let oid = get_oid_from_int(i).map_err(minicbor::decode::Error::message)?; - Ok(Self::new(oid, None)) - }, - _ => { - let algo_identifier = AlgorithmIdentifier::decode(d, ctx)?; - Ok(IssuerSignatureAlgorithm::new( - algo_identifier.get_oid(), - algo_identifier.get_param().clone(), - )) - }, - } - } -} - -// ------------------Test---------------------- - -#[cfg(test)] -mod test_issuer_signature_algorithm { - use asn1_rs::oid; - - use super::*; - - #[test] - fn test_registered_oid() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - - let isa = IssuerSignatureAlgorithm::new(oid!(1.3.101 .112), None); - isa.encode(&mut encoder, &mut ()) - .expect("Failed to encode IssuerSignatureAlgorithm"); - - // Ed25519 - int 12: 0x0c - assert_eq!(hex::encode(buffer.clone()), "0c"); - - let mut decoder = Decoder::new(&buffer); - let decoded_isa = IssuerSignatureAlgorithm::decode(&mut decoder, &mut ()) - .expect("Failed to decode IssuerSignatureAlgorithm"); - assert_eq!(decoded_isa, isa); - } - - #[test] - fn test_unregistered_oid() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - - let isa = IssuerSignatureAlgorithm::new(oid!(2.16.840 .1 .101 .3 .4 .2 .1), None); - isa.encode(&mut encoder, &mut ()) - .expect("Failed to encode IssuerSignatureAlgorithm"); - - // 2.16.840 .1 .101 .3 .4 .2 .1: 0x49608648016503040201 - assert_eq!(hex::encode(buffer.clone()), "49608648016503040201"); - - let mut decoder = Decoder::new(&buffer); - let decoded_isa = IssuerSignatureAlgorithm::decode(&mut decoder, &mut ()) - .expect("Failed to decode IssuerSignatureAlgorithm"); - assert_eq!(decoded_isa, isa); - } - - #[test] - fn test_unregistered_oid_with_param() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - - let isa = IssuerSignatureAlgorithm::new( - oid!(2.16.840 .1 .101 .3 .4 .2 .1), - Some("example".to_string()), - ); - isa.encode(&mut encoder, &mut ()) - .expect("Failed to encode IssuerSignatureAlgorithm"); - // Array of 2 items: 0x82 - // 2.16.840 .1 .101 .3 .4 .2 .1: 0x49608648016503040201 - // bytes "example": 0x476578616d706c65 - assert_eq!( - hex::encode(buffer.clone()), - "8249608648016503040201476578616d706c65" - ); - - let mut decoder = Decoder::new(&buffer); - let decoded_isa = IssuerSignatureAlgorithm::decode(&mut decoder, &mut ()) - .expect("Failed to decode IssuerSignatureAlgorithm"); - assert_eq!(decoded_isa, isa); - } -} diff --git a/catalyst-gateway-crates/c509-certificate/src/c509_name/mod.rs b/catalyst-gateway-crates/c509-certificate/src/c509_name/mod.rs deleted file mode 100644 index 5e0028dfdf4..00000000000 --- a/catalyst-gateway-crates/c509-certificate/src/c509_name/mod.rs +++ /dev/null @@ -1,521 +0,0 @@ -//! C509 type Name -//! -//! Currently only support natively signed c509 certificate, so all text strings -//! are UTF-8 encoded and all attributeType should be non-negative. -//! -//! ```cddl -//! Name = [ * RelativeDistinguishedName ] / text / bytes -//! RelativeDistinguishedName = Attribute / [ 2* Attribute ] -//! Attribute = ( attributeType: int, attributeValue: text ) // -//! ( attributeType: ~oid, attributeValue: bytes ) // -//! ( attributeType: pen, attributeValue: bytes ) -//! ``` -//! -//! For more information about Name, -//! visit [C509 Certificate](https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/09/) - -// cspell: words rdns - -pub mod rdn; -use asn1_rs::{oid, Oid}; -use minicbor::{encode::Write, Decode, Decoder, Encode, Encoder}; -use rdn::RelativeDistinguishedName; -use regex::Regex; -use serde::{Deserialize, Serialize}; - -use crate::c509_attributes::attribute::{Attribute, AttributeValue}; - -/// OID of `CommonName` attribute. -const COMMON_NAME_OID: Oid<'static> = oid!(2.5.4 .3); -/// EUI-64 prefix. -const EUI64_PREFIX: u8 = 0x01; -/// Hex prefix. -const HEX_PREFIX: u8 = 0x00; -/// Total length of CBOR byte for EUI-64. -const EUI64_LEN: usize = 9; -/// Total length of CBOR byte for EUI-64 mapped from a 48-bit MAC address. -const EUI64_MAC_LEN: usize = 7; - -// ------------------Name---------------------- - -/// A struct of C509 Name with `NameValue`. -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] -pub struct Name(NameValue); - -impl Name { - /// Create a new instance of `Name` its value. - #[must_use] - pub fn new(value: NameValue) -> Self { - Self(value) - } - - /// Get the value of the `Name`. - #[must_use] - pub fn get_value(&self) -> &NameValue { - &self.0 - } -} - -impl Encode<()> for Name { - fn encode( - &self, e: &mut Encoder, ctx: &mut (), - ) -> Result<(), minicbor::encode::Error> { - self.0.encode(e, ctx) - } -} - -impl Decode<'_, ()> for Name { - fn decode(d: &mut Decoder<'_>, ctx: &mut ()) -> Result { - NameValue::decode(d, ctx).map(Name::new) - } -} - -// ------------------NameValue---------------------- - -/// An enum of possible value types for `Name`. -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] -#[serde(rename_all = "snake_case")] -pub enum NameValue { - /// A relative distinguished name. - RelativeDistinguishedName(RelativeDistinguishedName), - /// A text. - Text(String), - /// bytes. - Bytes(Vec), -} - -impl Encode<()> for NameValue { - fn encode( - &self, e: &mut Encoder, ctx: &mut (), - ) -> Result<(), minicbor::encode::Error> { - match self { - NameValue::RelativeDistinguishedName(rdn) => { - let attr = rdn.get_attributes(); - let attr_first = attr.first().ok_or(minicbor::encode::Error::message( - "Cannot get the first Attribute", - ))?; - // If Name contains a single Attribute of type CommonName - if attr.len() == 1 - && attr_first.get_registered_oid().get_c509_oid().get_oid() == COMMON_NAME_OID - { - // Get the value of the attribute - let cn_value = - attr_first - .get_value() - .first() - .ok_or(minicbor::encode::Error::message( - "Cannot get the first Attribute value", - ))?; - - encode_cn_value(e, cn_value)?; - } else { - rdn.encode(e, ctx)?; - } - }, - NameValue::Text(text) => { - e.str(text)?; - }, - NameValue::Bytes(bytes) => { - e.bytes(bytes)?; - }, - } - Ok(()) - } -} - -impl Decode<'_, ()> for NameValue { - fn decode(d: &mut Decoder<'_>, ctx: &mut ()) -> Result { - match d.datatype()? { - minicbor::data::Type::Array => { - Ok(NameValue::RelativeDistinguishedName( - RelativeDistinguishedName::decode(d, ctx)?, - )) - }, - // If Name is a text string, the attribute is a CommonName - minicbor::data::Type::String => Ok(create_rdn_with_cn_attr(d.str()?.to_string())), - minicbor::data::Type::Bytes => decode_bytes(d), - _ => { - Err(minicbor::decode::Error::message( - "Name must be an array, text or bytes", - )) - }, - } - } -} - -/// Encode common name value. -fn encode_cn_value( - e: &mut Encoder, cn_value: &AttributeValue, -) -> Result<(), minicbor::encode::Error> { - let hex_regex = Regex::new(r"^[0-9a-f]+$").map_err(minicbor::encode::Error::message)?; - let eui64_regex = - Regex::new(r"^([0-9A-F]{2}-){7}[0-9A-F]{2}$").map_err(minicbor::encode::Error::message)?; - let mac_eui64_regex = Regex::new(r"^([0-9A-F]{2}-){3}FF-FE-([0-9A-F]{2}-){2}[0-9A-F]{2}$") - .map_err(minicbor::encode::Error::message)?; - - match cn_value { - AttributeValue::Text(s) => { - // If the text string has an even length ≥ 2 and contains only the - // symbols '0'–'9' or 'a'–'f', it is encoded as a CBOR byte - // string, prefixed with an initial byte set to '00' - if hex_regex.is_match(s) && s.len() % 2 == 0 { - let decoded_bytes = hex::decode(s).map_err(minicbor::encode::Error::message)?; - e.bytes(&[&[HEX_PREFIX], &decoded_bytes[..]].concat())?; - - // An EUI-64 mapped from a 48-bit MAC address (i.e., of the form - // "HH-HH-HH-FF-FE-HH-HH-HH) is encoded as a CBOR byte string prefixed with an - // initial byte set to '01', for a total length of 7. - } else if mac_eui64_regex.is_match(s) { - let clean_name = s.replace('-', ""); - let decoded_bytes = - hex::decode(clean_name).map_err(minicbor::encode::Error::message)?; - let chunk2 = decoded_bytes - .get(..3) - .ok_or(minicbor::encode::Error::message( - "Failed to get MAC EUI-64 bytes index 0 to 2", - ))?; - let chunk3 = decoded_bytes - .get(5..) - .ok_or(minicbor::encode::Error::message( - "Failed to get MAC EUI-64 bytes index 5 to 6", - ))?; - e.bytes(&[&[EUI64_PREFIX], chunk2, chunk3].concat())?; - - // an EUI-64 of the form "HH-HH-HH-HH-HH-HH-HH-HH" where 'H' - // is one of the symbols '0'–'9' or 'A'–'F' it is encoded as a - // CBOR byte string prefixed with an initial byte set to '01', for a total - // length of 9. - } else if eui64_regex.is_match(s) { - let clean_name = s.replace('-', ""); - let decoded_bytes = - hex::decode(clean_name).map_err(minicbor::encode::Error::message)?; - e.bytes(&[&[EUI64_PREFIX], &decoded_bytes[..]].concat())?; - } else { - e.str(s)?; - } - }, - AttributeValue::Bytes(_) => { - return Err(minicbor::encode::Error::message( - "CommonName attribute value must be a text string", - )); - }, - } - Ok(()) -} - -/// Format EUI bytes. -fn formatted_eui_bytes(data: &[u8]) -> String { - data.iter() - .map(|b| format!("{b:02X}")) - .collect::>() - .join("-") -} - -/// Decode bytes. -fn decode_bytes(d: &mut Decoder<'_>) -> Result { - let bytes = d.bytes()?; - - let first_i = bytes.first().ok_or(minicbor::decode::Error::message( - "Failed to get the first index of bytes", - ))?; - - // Bytes prefix - match *first_i { - // 0x00 for hex - HEX_PREFIX => decode_hex_cn_bytes(bytes), - // 0x01 for EUI - EUI64_PREFIX => decode_eui_cn_bytes(bytes), - _ => Ok(NameValue::Bytes(bytes.to_vec())), - } -} - -/// Decode common name hex bytes. -fn decode_hex_cn_bytes(bytes: &[u8]) -> Result { - let text = hex::encode(bytes.get(1..).ok_or(minicbor::decode::Error::message( - "Failed to get hex bytes index", - ))?); - Ok(create_rdn_with_cn_attr(text)) -} - -/// Decode common name EUI-64 bytes. -fn decode_eui_cn_bytes(bytes: &[u8]) -> Result { - // Check the length of the bytes to determine what EUI type it is - match bytes.len() { - // EUI-64 mapped from a 48-bit MAC address - EUI64_MAC_LEN => { - let chunk1 = bytes.get(1..4).ok_or(minicbor::decode::Error::message( - "Failed to get EUI-64 bytes index 1 to 3", - ))?; - let chunk4 = bytes.get(4..).ok_or(minicbor::decode::Error::message( - "Failed to get EUI-64 bytes index 4 to 7", - ))?; - // Turn it into HH-HH-HH-FF-FE-HH-HH-HH - let data = [chunk1, &[0xFF], &[0xFE], chunk4].concat(); - let text = formatted_eui_bytes(&data); - Ok(create_rdn_with_cn_attr(text)) - }, - // EUI-64 - EUI64_LEN => { - let text = formatted_eui_bytes(bytes.get(1..).ok_or( - minicbor::decode::Error::message("Failed to get EUI-64 bytes index"), - )?); - Ok(create_rdn_with_cn_attr(text)) - }, - _ => { - Err(minicbor::decode::Error::message( - "EUI-64 or MAC address must be 7 or 9 bytes", - )) - }, - } -} - -/// Create a relative distinguished name with attribute common name from string. -fn create_rdn_with_cn_attr(text: String) -> NameValue { - let mut attr = Attribute::new(COMMON_NAME_OID); - attr.add_value(AttributeValue::Text(text)); - let mut rdn = RelativeDistinguishedName::new(); - rdn.add_attr(attr); - NameValue::RelativeDistinguishedName(rdn) -} - -// ------------------Test---------------------- - -#[cfg(test)] -pub(crate) mod test_name { - use super::*; - use crate::c509_attributes::attribute::Attribute; - - // Test data from https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/09/ - // A.1.1. Example C509 Certificate Encoding - pub(crate) fn name_cn_text() -> (Name, String) { - let mut attr = Attribute::new(oid!(2.5.4 .3)); - attr.add_value(AttributeValue::Text("RFC test CA".to_string())); - let mut rdn = RelativeDistinguishedName::new(); - rdn.add_attr(attr); - - ( - Name::new(NameValue::RelativeDistinguishedName(rdn)), - // "RFC test CA" Text string: 6b5246432074657374204341 - "6b5246432074657374204341".to_string(), - ) - } - - #[test] - fn encode_decode_type_name_cn() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - - let name = name_cn_text().0; - name.encode(&mut encoder, &mut ()) - .expect("Failed to encode Name"); - - assert_eq!(hex::encode(buffer.clone()), name_cn_text().1); - - let mut decoder = Decoder::new(&buffer); - let name_decoded = Name::decode(&mut decoder, &mut ()).expect("Failed to decode Name"); - assert_eq!(name_decoded, name); - } - - #[test] - fn encode_decode_type_name_hex() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - - let mut attr = Attribute::new(oid!(2.5.4 .3)); - attr.add_value(AttributeValue::Text("000123abcd".to_string())); - let mut rdn = RelativeDistinguishedName::new(); - rdn.add_attr(attr); - - let name = Name::new(NameValue::RelativeDistinguishedName(rdn)); - name.encode(&mut encoder, &mut ()) - .expect("Failed to encode Name"); - - // Bytes of length 6: 0x46 - // Prefix of CommonName hex: 0x00 - // Bytes 000123abcd: 0x000123abcd - assert_eq!(hex::encode(buffer.clone()), "4600000123abcd"); - - let mut decoder = Decoder::new(&buffer); - let name_decoded = Name::decode(&mut decoder, &mut ()).expect("Failed to decode Name"); - assert_eq!(name_decoded, name); - } - - #[test] - fn encode_decode_type_name_hex_cap() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - - let mut attr = Attribute::new(oid!(2.5.4 .3)); - attr.add_value(AttributeValue::Text("000123ABCD".to_string())); - let mut rdn = RelativeDistinguishedName::new(); - rdn.add_attr(attr); - - let name = Name::new(NameValue::RelativeDistinguishedName(rdn)); - name.encode(&mut encoder, &mut ()) - .expect("Failed to encode Name"); - - // String of len 10: 0x6a - // String 000123abcd: 30303031323341424344 - assert_eq!(hex::encode(buffer.clone()), "6a30303031323341424344"); - - let mut decoder = Decoder::new(&buffer); - let name_decoded = Name::decode(&mut decoder, &mut ()).expect("Failed to decode Name"); - assert_eq!(name_decoded, name); - } - - // Test data from https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/09/ - // A.1. Example RFC 7925 profiled X.509 Certificate - pub(crate) fn name_cn_eui_mac() -> (Name, String) { - let mut attr = Attribute::new(oid!(2.5.4 .3)); - attr.add_value(AttributeValue::Text("01-23-45-FF-FE-67-89-AB".to_string())); - let mut rdn = RelativeDistinguishedName::new(); - rdn.add_attr(attr); - - ( - Name::new(NameValue::RelativeDistinguishedName(rdn)), - // Bytes of length 7: 0x47 - // "01-23-45-FF-FE-67-89-AB" special encode: 0x010123456789AB - "47010123456789ab".to_string(), - ) - } - - #[test] - fn encode_decode_type_name_cn_eui_mac() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - - let name = name_cn_eui_mac().0; - - name.encode(&mut encoder, &mut ()) - .expect("Failed to encode Name"); - assert_eq!(hex::encode(buffer.clone()), name_cn_eui_mac().1); - - let mut decoder = Decoder::new(&buffer); - let name_decoded = Name::decode(&mut decoder, &mut ()).expect("Failed to decode Name"); - assert_eq!(name_decoded, name); - } - - #[test] - fn encode_decode_type_name_cn_eui_mac_un_cap() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - - let mut attr = Attribute::new(oid!(2.5.4 .3)); - attr.add_value(AttributeValue::Text("01-23-45-ff-fe-67-89-AB".to_string())); - let mut rdn = RelativeDistinguishedName::new(); - rdn.add_attr(attr); - let name = Name::new(NameValue::RelativeDistinguishedName(rdn)); - - name.encode(&mut encoder, &mut ()) - .expect("Failed to encode Name"); - - // String of len 23: 0x77 - // "01-23-45-ff-fe-67-89-AB": 0x7730312d32332d34352d66662d66652d36372d38392d4142 - assert_eq!( - hex::encode(buffer.clone()), - "7730312d32332d34352d66662d66652d36372d38392d4142" - ); - - let mut decoder = Decoder::new(&buffer); - let name_decoded = Name::decode(&mut decoder, &mut ()).expect("Failed to decode Name"); - assert_eq!(name_decoded, name); - } - - #[test] - fn encode_decode_type_name_cn_eui() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - - let mut attr = Attribute::new(oid!(2.5.4 .3)); - attr.add_value(AttributeValue::Text("01-23-45-67-89-AB-00-01".to_string())); - let mut rdn = RelativeDistinguishedName::new(); - rdn.add_attr(attr); - - let name = Name::new(NameValue::RelativeDistinguishedName(rdn)); - - name.encode(&mut encoder, &mut ()) - .expect("Failed to encode Name"); - - assert_eq!(hex::encode(buffer.clone()), "49010123456789ab0001"); - - let mut decoder = Decoder::new(&buffer); - let name_decoded = Name::decode(&mut decoder, &mut ()).expect("Failed to decode Name"); - assert_eq!(name_decoded, name); - } - - #[test] - fn encode_decode_type_name_cn_eui_un_cap() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - - let mut attr = Attribute::new(oid!(2.5.4 .3)); - attr.add_value(AttributeValue::Text("01-23-45-67-89-ab-00-01".to_string())); - let mut rdn = RelativeDistinguishedName::new(); - rdn.add_attr(attr); - - let name = Name::new(NameValue::RelativeDistinguishedName(rdn)); - - name.encode(&mut encoder, &mut ()) - .expect("Failed to encode Name"); - - // String of len 23: 0x77 - // "01-23-45-67-89-ab-00-01": 0x7730312d32332d34352d36372d38392d61622d30302d3031 - assert_eq!( - hex::encode(buffer.clone()), - "7730312d32332d34352d36372d38392d61622d30302d3031" - ); - - let mut decoder = Decoder::new(&buffer); - let name_decoded = Name::decode(&mut decoder, &mut ()).expect("Failed to decode Name"); - assert_eq!(name_decoded, name); - } - - // Test data from https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/09/ - // A.2. Example IEEE 802.1AR profiled X.509 Certificate - // Issuer: C=US, ST=CA, O=Example Inc, OU=certification, CN=802.1AR CA - pub(crate) fn names() -> (Name, String) { - let mut attr1 = Attribute::new(oid!(2.5.4 .6)); - attr1.add_value(AttributeValue::Text("US".to_string())); - let mut attr2 = Attribute::new(oid!(2.5.4 .8)); - attr2.add_value(AttributeValue::Text("CA".to_string())); - let mut attr3 = Attribute::new(oid!(2.5.4 .10)); - attr3.add_value(AttributeValue::Text("Example Inc".to_string())); - let mut attr4 = Attribute::new(oid!(2.5.4 .11)); - attr4.add_value(AttributeValue::Text("certification".to_string())); - let mut attr5 = Attribute::new(oid!(2.5.4 .3)); - attr5.add_value(AttributeValue::Text("802.1AR CA".to_string())); - - let mut rdn = RelativeDistinguishedName::new(); - rdn.add_attr(attr1); - rdn.add_attr(attr2); - rdn.add_attr(attr3); - rdn.add_attr(attr4); - rdn.add_attr(attr5); - - ( - Name::new(NameValue::RelativeDistinguishedName(rdn)), - // Array of 10 items [4, "US", 6, "CA", 8, "Example Inc", 9, "certification", 1, "802.1AR CA"] : 0x8a - // attr1: 0x04625553 - // attr2: 0x06624341 - // attr3: 0x086b4578616d706c6520496e63 - // attr4: 0x096d63657274696669636174696f6e - // attr5: 0x016a3830322e314152204341 - "8a0462555306624341086b4578616d706c6520496e63096d63657274696669636174696f6e016a3830322e314152204341".to_string(), - ) - } - #[test] - fn encode_decode_type_name_rdns() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - - let name = names().0; - - name.encode(&mut encoder, &mut ()) - .expect("Failed to encode Name"); - assert_eq!(hex::encode(buffer.clone()), names().1); - - let mut decoder = Decoder::new(&buffer); - let name_decoded = Name::decode(&mut decoder, &mut ()).expect("Failed to decode Name"); - assert_eq!(name_decoded, name); - } -} diff --git a/catalyst-gateway-crates/c509-certificate/src/c509_name/rdn.rs b/catalyst-gateway-crates/c509-certificate/src/c509_name/rdn.rs deleted file mode 100644 index ef71481fa28..00000000000 --- a/catalyst-gateway-crates/c509-certificate/src/c509_name/rdn.rs +++ /dev/null @@ -1,171 +0,0 @@ -//! C509 Relative Distinguished Name -//! -//! For more information about `RelativeDistinguishedName`, -//! visit [C509 Certificate](https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/09/) - -// cspell: words rdns - -use minicbor::{encode::Write, Decode, Decoder, Encode, Encoder}; -use serde::{Deserialize, Serialize}; - -use crate::c509_attributes::attribute::Attribute; - -/// A struct represents a Relative Distinguished Name containing vector of `Attribute`. -/// -/// ```cddl -/// RelativeDistinguishedName = Attribute / [ 2* Attribute ] -/// ``` -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] -pub struct RelativeDistinguishedName(Vec); - -impl Default for RelativeDistinguishedName { - fn default() -> Self { - Self::new() - } -} - -impl RelativeDistinguishedName { - /// Create a new instance of `RelativeDistinguishedName` as empty vector. - #[must_use] - pub fn new() -> Self { - Self(Vec::new()) - } - - /// Add an `Attribute` to the `RelativeDistinguishedName`. - pub fn add_attr(&mut self, attribute: Attribute) { - // RelativeDistinguishedName support pen encoding - self.0.push(attribute.set_pen_supported()); - } - - /// Get the a vector of `Attribute`. - pub(crate) fn get_attributes(&self) -> &Vec { - &self.0 - } -} - -impl Encode<()> for RelativeDistinguishedName { - // ```cddl - // RelativeDistinguishedName = Attribute / [ 2* Attribute ] - // ``` - fn encode( - &self, e: &mut Encoder, ctx: &mut (), - ) -> Result<(), minicbor::encode::Error> { - // Should contain >= 1 attribute - if self.0.is_empty() { - return Err(minicbor::encode::Error::message( - "RelativeDistinguishedName should not be empty", - )); - } - - if self.0.len() == 1 { - self.0.first().encode(e, ctx)?; - } else { - // The attribute type should be included in array too - e.array(self.0.len() as u64 * 2)?; - for attr in &self.0 { - attr.encode(e, ctx)?; - } - } - Ok(()) - } -} - -impl Decode<'_, ()> for RelativeDistinguishedName { - fn decode(d: &mut Decoder<'_>, ctx: &mut ()) -> Result { - let mut rdn = RelativeDistinguishedName::new(); - - match d.datatype()? { - minicbor::data::Type::Array => { - let len = d.array()?.ok_or(minicbor::decode::Error::message( - "Failed to get array length for relative distinguished name", - ))?; - // Should contain >= 1 attribute - if len == 0 { - return Err(minicbor::decode::Error::message( - "RelativeDistinguishedName should not be empty", - )); - } - // The attribute type is included in an array, so divide by 2 - for _ in 0..len / 2 { - rdn.add_attr(Attribute::decode(d, ctx)?); - } - }, - _ => rdn.add_attr(Attribute::decode(d, ctx)?), - } - Ok(rdn) - } -} - -// -------------------Test---------------------- - -#[cfg(test)] -mod test_relative_distinguished_name { - - use asn1_rs::oid; - - use super::*; - use crate::c509_attributes::attribute::AttributeValue; - - #[test] - fn encode_decode_rdn() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - - let mut attr = Attribute::new(oid!(1.2.840 .113549 .1 .9 .1)); - attr.add_value(AttributeValue::Text("example@example.com".to_string())); - - let mut rdn = RelativeDistinguishedName::new(); - rdn.add_attr(attr); - rdn.encode(&mut encoder, &mut ()) - .expect("Failed to encode RDN"); - // Email Address: 0x00 - // "example@example.como": 736578616d706c65406578616d706c652e636f6d - assert_eq!( - hex::encode(buffer.clone()), - "00736578616d706c65406578616d706c652e636f6d" - ); - - let mut decoder = Decoder::new(&buffer); - let rdn_decoded = RelativeDistinguishedName::decode(&mut decoder, &mut ()) - .expect("Failed to decode RelativeDistinguishedName"); - assert_eq!(rdn_decoded, rdn); - } - - #[test] - fn encode_decode_rdns() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - - let mut attr1 = Attribute::new(oid!(1.2.840 .113549 .1 .9 .1)); - attr1.add_value(AttributeValue::Text("example@example.com".to_string())); - let mut attr2 = Attribute::new(oid!(2.5.4 .3)); - attr2.add_value(AttributeValue::Text("example".to_string())); - - let mut rdns = RelativeDistinguishedName::new(); - rdns.add_attr(attr1); - rdns.add_attr(attr2); - - rdns.encode(&mut encoder, &mut ()) - .expect("Failed to encode RDN"); - // Array of 2 attributes: 0x84 - // Email Address example@example.com: 0x00736578616d706c65406578616d706c652e636f6d - // Common Name example: 0x01676578616d706c65 - assert_eq!( - hex::encode(buffer.clone()), - "8400736578616d706c65406578616d706c652e636f6d01676578616d706c65" - ); - let mut decoder = Decoder::new(&buffer); - let rdn_decoded = RelativeDistinguishedName::decode(&mut decoder, &mut ()) - .expect("Failed to decode RelativeDistinguishedName"); - assert_eq!(rdn_decoded, rdns); - } - - #[test] - fn empty_rdn() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - let rdn = RelativeDistinguishedName::new(); - rdn.encode(&mut encoder, &mut ()) - .expect_err("Failed to encode RDN"); - } -} diff --git a/catalyst-gateway-crates/c509-certificate/src/c509_oid.rs b/catalyst-gateway-crates/c509-certificate/src/c509_oid.rs deleted file mode 100644 index 646263a14a9..00000000000 --- a/catalyst-gateway-crates/c509-certificate/src/c509_oid.rs +++ /dev/null @@ -1,243 +0,0 @@ -//! C509 OID provides an encoding and decoding of C509 Object Identifier (OID). -//! -//! Please refer to [RFC9090](https://datatracker.ietf.org/doc/rfc9090/) for OID encoding -//! Please refer to [CDDL Wrapping](https://datatracker.ietf.org/doc/html/rfc8610#section-3.7) -//! for unwrapped types. - -use std::str::FromStr; - -use anyhow::Result; -use asn1_rs::oid; -use minicbor::{data::Tag, decode, encode::Write, Decode, Decoder, Encode, Encoder}; -use oid_registry::Oid; -use serde::{Deserialize, Deserializer, Serialize}; - -use crate::tables::IntegerToOidTable; - -/// IANA Private Enterprise Number (PEN) OID prefix. -const PEN_PREFIX: Oid<'static> = oid!(1.3.6 .1 .4 .1); - -/// Tag number representing IANA Private Enterprise Number (PEN) OID. -const OID_PEN_TAG: u64 = 112; - -/// A strut of C509 OID with Registered Integer. -#[derive(Debug, Clone, PartialEq)] -pub struct C509oidRegistered { - /// The `C509oid`. - oid: C509oid, - /// The registration table. - registration_table: &'static IntegerToOidTable, -} - -impl C509oidRegistered { - /// Create a new instance of `C509oidRegistered`. - pub(crate) fn new(oid: Oid<'static>, table: &'static IntegerToOidTable) -> Self { - Self { - oid: C509oid::new(oid), - registration_table: table, - } - } - - /// Is PEN Encoding supported for this OID. - /// Depends on each registration table. - pub(crate) fn pen_encoded(mut self) -> Self { - self.oid.pen_supported = true; - self - } - - /// Get the `C509oid`. - pub(crate) fn get_c509_oid(&self) -> C509oid { - self.oid.clone() - } - - /// Get the registration table. - pub(crate) fn get_table(&self) -> &'static IntegerToOidTable { - self.registration_table - } -} - -// ----------------------------------------- - -/// A struct represent an instance of `C509oid`. -#[derive(Debug, PartialEq, Clone, Eq, Hash)] -pub struct C509oid { - /// The OID. - oid: Oid<'static>, - /// The flag to indicate whether PEN encoding is supported. - pen_supported: bool, -} - -/// A helper struct for deserialize and serialize `C509oid`. -#[derive(Debug, Deserialize, Serialize)] -struct Helper { - /// OID value in string. - oid: String, -} - -impl<'de> Deserialize<'de> for C509oid { - fn deserialize(deserializer: D) -> Result - where D: Deserializer<'de> { - let helper = Helper::deserialize(deserializer)?; - let oid = - Oid::from_str(&helper.oid).map_err(|e| serde::de::Error::custom(format!("{e:?}")))?; - Ok(C509oid::new(oid)) - } -} - -impl Serialize for C509oid { - fn serialize(&self, serializer: S) -> Result - where S: serde::Serializer { - let helper = Helper { - oid: self.oid.to_string(), - }; - helper.serialize(serializer) - } -} - -impl C509oid { - /// Create an new instance of `C509oid`. - /// Default value of PEN flag is false - #[must_use] - pub fn new(oid: Oid<'static>) -> Self { - Self { - oid, - pen_supported: false, - } - } - - /// Is PEN Encoding supported for this OID - pub(crate) fn pen_encoded(mut self) -> Self { - self.pen_supported = true; - self - } - - /// Get the underlying OID of the `C509oid` - #[must_use] - pub fn get_oid(self) -> Oid<'static> { - self.oid.clone() - } -} - -impl Encode<()> for C509oid { - /// Encode an OID - /// If `pen_supported` flag is set, and OID start with a valid `PEN_PREFIX`, - /// it is encoded as PEN (Private Enterprise Number) - /// else encode as an unwrapped OID (~oid) - as bytes string without tag. - /// - /// # Returns - /// - /// A vector of bytes containing the CBOR encoded OID. - /// If the encoding fails, it will return an error. - fn encode( - &self, e: &mut Encoder, _ctx: &mut (), - ) -> Result<(), minicbor::encode::Error> { - // Check if PEN encoding is supported and the OID starts with the PEN prefix. - if self.pen_supported && self.oid.starts_with(&PEN_PREFIX) { - // Set the CBOR tag. - e.tag(Tag::new(OID_PEN_TAG))?; - // Convert OID originally store as [u8] to [u64] - // This process is necessary to get the correct OID - // For example given - 1.3.6 .1 .4 .1.4.999 - // This OID will be stored as [u8] - [43, 6, 1, 4, 1, 4, 135, 103] - // The first 2 integer has a special encoding formula where, - // values is computed using X * 40 + Y (See RFC9090 for more info) - // The number 999 exceed the 225 limit (max of u8), so it will be encoded as 2 bytes - let raw_oid: Vec = - self.oid - .iter() - .map(Iterator::collect) - .ok_or(minicbor::encode::Error::message( - "Failed to collect OID components from iterator", - ))?; - let raw_pen_prefix: Vec = PEN_PREFIX.iter().map(Iterator::collect).ok_or( - minicbor::encode::Error::message("Failed to collect OID components from iterator"), - )?; - // relative_oid is OID that follows PEN_PREFIX (relative to PEN_PREFIX) - // Use the [u64] PEN prefix length to extract the relative OID - let oid_slice = - raw_oid - .get(raw_pen_prefix.len()..) - .ok_or(minicbor::encode::Error::message( - "Failed to get a OID slice", - ))?; - let relative_oid = Oid::from_relative(oid_slice) - .map_err(|_| minicbor::encode::Error::message("Failed to get a relative OID"))?; - return e.bytes(relative_oid.as_bytes())?.ok(); - } - let oid_bytes = self.oid.as_bytes(); - e.bytes(oid_bytes)?.ok() - } -} - -impl Decode<'_, ()> for C509oid { - /// Decode an OID - /// If the data to be decoded is a `Tag`, and the tag is an `OID_PEN_TAG`, - /// then decode the OID as Private Enterprise Number (PEN) OID. - /// else decode the OID as unwrapped OID (~oid) - as bytes string without tag. - - /// # Returns - /// - /// A C509oid instance. - /// If the decoding fails, it will return an error. - fn decode(d: &mut Decoder, _ctx: &mut ()) -> Result { - if (minicbor::data::Type::Tag == d.datatype()?) && (Tag::new(OID_PEN_TAG) == d.tag()?) { - let oid_bytes = d.bytes()?; - // raw_oid contains the whole OID which stored in bytes - let mut raw_oid = Vec::new(); - raw_oid.extend_from_slice(PEN_PREFIX.as_bytes()); - raw_oid.extend_from_slice(oid_bytes); - // Convert the raw_oid to Oid - let oid = Oid::new(raw_oid.into()); - return Ok(C509oid::new(oid).pen_encoded()); - } - // Not a PEN Relative OID, so treat as a normal OID - let oid_bytes = d.bytes()?; - let oid = Oid::new(oid_bytes.to_owned().into()); - Ok(C509oid::new(oid)) - } -} - -// ----------------------------------------- - -#[cfg(test)] -mod test_c509_oid { - - use super::*; - - // Test reference 3.1. Encoding of the SHA-256 OID - // https://datatracker.ietf.org/doc/rfc9090/ - #[test] - fn encode_decode_unwrapped() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - let oid = C509oid::new(oid!(2.16.840 .1 .101 .3 .4 .2 .1)); - oid.encode(&mut encoder, &mut ()) - .expect("Failed to encode OID"); - assert_eq!(hex::encode(buffer.clone()), "49608648016503040201"); - - let mut decoder = Decoder::new(&buffer); - let decoded_oid = C509oid::decode(&mut decoder, &mut ()).expect("Failed to decode OID"); - assert_eq!(decoded_oid, oid); - } - - #[test] - fn encode_decode_pen() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - let oid = C509oid::new(oid!(1.3.6 .1 .4 .1 .1 .1 .29)).pen_encoded(); - oid.encode(&mut encoder, &mut ()) - .expect("Failed to encode OID"); - assert_eq!(hex::encode(buffer.clone()), "d8704301011d"); - - let mut decoder = Decoder::new(&buffer); - let decoded_oid = C509oid::decode(&mut decoder, &mut ()).expect("Failed to decode OID"); - assert_eq!(decoded_oid, oid); - } - - #[test] - fn partial_equal() { - let oid1 = C509oid::new(oid_registry::OID_HASH_SHA1); - let oid2 = C509oid::new(oid!(1.3.14 .3 .2 .26)); - assert_eq!(oid1, oid2); - } -} diff --git a/catalyst-gateway-crates/c509-certificate/src/c509_subject_pub_key_algo/data.rs b/catalyst-gateway-crates/c509-certificate/src/c509_subject_pub_key_algo/data.rs deleted file mode 100644 index bb37a2d4bf5..00000000000 --- a/catalyst-gateway-crates/c509-certificate/src/c509_subject_pub_key_algo/data.rs +++ /dev/null @@ -1,75 +0,0 @@ -//! Public key algorithm data provides a necessary information for encoding and decoding -//! of C509 `subjectPublicKeyAlgorithm`. See [C509 Certificate](https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/09/) -//! Section 9.11 C509 Public Key Algorithms Registry for more information. - -// cspell: words Weierstraß secp XMSS brainpool - -use anyhow::Error; -use asn1_rs::{oid, Oid}; -use once_cell::sync::Lazy; - -use crate::tables::IntegerToOidTable; - -/// Type of algorithm data -/// INT | OID | Name -type AlgorithmDataTuple = (i16, Oid<'static>, &'static str); - -/// Public key algorithm data table. -#[rustfmt::skip] -const PUB_KEY_ALGO_DATA: [AlgorithmDataTuple; 9] = [ - // Int | OID | Name - (0, oid!(1.2.840.113549.1.1.1), "RSA"), - (1, oid!(1.2.840.10045.2.1), "EC Public Key (Weierstraß) with secp256r1"), - // (2, oid!(1.2.840.10045.2.1), "EC Public Key (Weierstraß) with secp384r1"), - // (3, oid!(1.2.840.10045.2.1), "EC Public Key (Weierstraß) with secp521r1"), - (8, oid!(1.3.101.110), "X25519 (Montgomery)"), - (9, oid!(1.3.101.111), "X448 (Montgomery)"), - (10, oid!(1.3.101.112), "Ed25519 (Twisted Edwards)"), - (11, oid!(1.3.101.113), "Ed448 (Edwards)"), - (16, oid!(1.2.840.113549.1.9.16.3.17), "HSS / LMS"), - (17, oid!(0.4.0.127.0.15.1.1.13.0), "XMSS"), - (18, oid!(0.4.0.127.0.15.1.1.14.0), "XMSS^MT"), - // (24, oid!(1.2.840.10045.2.1), "EC Public Key (Weierstraß) with brainpoolP256r1"), - // (25, oid!(1.2.840.10045.2.1), "EC Public Key (Weierstraß) with brainpoolP384r1"), - // (26, oid!(1.2.840.10045.2.1), "EC Public Key (Weierstraß) with brainpoolP512r1"), - // (27, oid!(1.2.840.10045.2.1), "EC Public Key (Weierstraß) with FRP256v1"), - // (28, oid!(1.2.840.10045.2.1), "EC Public Key (Weierstraß) with sm2p256v1"), -]; - -/// A struct of data that contains lookup table of integer to OID in -/// bidirectional way for `SubjectPublicKeyAlgorithm`. -pub(crate) struct SubjectPubKeyAlgoData(IntegerToOidTable); - -impl SubjectPubKeyAlgoData { - /// Get the `IntegerToOidTable` - pub(crate) fn get_int_to_oid_table(&self) -> &IntegerToOidTable { - &self.0 - } -} - -/// Define static lookup for subject publickey table -static SUBJECT_PUB_KEY_ALGO_TABLE: Lazy = Lazy::new(|| { - let mut int_to_oid_table = IntegerToOidTable::new(); - - for data in PUB_KEY_ALGO_DATA { - int_to_oid_table.add(data.0, data.1); - } - - SubjectPubKeyAlgoData(int_to_oid_table) -}); - -/// Static reference to the `SubjectPubKeyAlgoData` lookup table. -pub(crate) static SUBJECT_PUB_KEY_ALGO_LOOKUP: &Lazy = - &SUBJECT_PUB_KEY_ALGO_TABLE; - -/// Get the OID from the int value. -pub(crate) fn get_oid_from_int(i: i16) -> Result, Error> { - SUBJECT_PUB_KEY_ALGO_TABLE - .get_int_to_oid_table() - .get_map() - .get_by_left(&i) - .ok_or(Error::msg(format!( - "OID not found in the public key algorithms registry table given int {i}" - ))) - .cloned() -} diff --git a/catalyst-gateway-crates/c509-certificate/src/c509_subject_pub_key_algo/mod.rs b/catalyst-gateway-crates/c509-certificate/src/c509_subject_pub_key_algo/mod.rs deleted file mode 100644 index 3c56175fbda..00000000000 --- a/catalyst-gateway-crates/c509-certificate/src/c509_subject_pub_key_algo/mod.rs +++ /dev/null @@ -1,178 +0,0 @@ -//! C509 Issuer Signature Algorithm as a part of `TBSCertificate` used in C509 -//! Certificate. -//! -//! ```cddl -//! subjectPublicKeyAlgorithm: AlgorithmIdentifier -//! ``` - -// cspell: words spka - -mod data; - -use std::str::FromStr; - -use asn1_rs::Oid; -use data::{get_oid_from_int, SUBJECT_PUB_KEY_ALGO_LOOKUP}; -use minicbor::{encode::Write, Decode, Decoder, Encode, Encoder}; -use serde::{Deserialize, Deserializer, Serialize}; - -use crate::{c509_algo_identifier::AlgorithmIdentifier, c509_oid::C509oidRegistered}; - -/// A struct represents the `SubjectPubKeyAlgorithm` -#[derive(Debug, Clone, PartialEq)] -pub struct SubjectPubKeyAlgorithm { - /// The registered OID of the `SubjectPubKeyAlgorithm`. - registered_oid: C509oidRegistered, - /// An `AlgorithmIdentifier` type - algo_identifier: AlgorithmIdentifier, -} - -impl SubjectPubKeyAlgorithm { - /// Create new instance of `SubjectPubKeyAlgorithm` where it registered with - /// Subject Public Key Algorithm lookup table. - pub fn new(oid: Oid<'static>, param: Option) -> Self { - Self { - registered_oid: C509oidRegistered::new( - oid.clone(), - SUBJECT_PUB_KEY_ALGO_LOOKUP.get_int_to_oid_table(), - ), - algo_identifier: AlgorithmIdentifier::new(oid, param), - } - } -} - -/// Helper struct for deserialize and serialize `SubjectPubKeyAlgorithm`. -#[derive(Debug, Deserialize, Serialize)] -struct Helper { - /// OID as string. - oid: String, - /// Optional parameter. - param: Option, -} - -impl<'de> Deserialize<'de> for SubjectPubKeyAlgorithm { - fn deserialize(deserializer: D) -> Result - where D: Deserializer<'de> { - let helper = Helper::deserialize(deserializer)?; - let oid = - Oid::from_str(&helper.oid).map_err(|e| serde::de::Error::custom(format!("{e:?}")))?; - - Ok(SubjectPubKeyAlgorithm::new(oid, helper.param)) - } -} - -impl Serialize for SubjectPubKeyAlgorithm { - fn serialize(&self, serializer: S) -> Result - where S: serde::Serializer { - let helper = Helper { - oid: self.registered_oid.get_c509_oid().get_oid().to_string(), - param: self.algo_identifier.get_param().clone(), - }; - helper.serialize(serializer) - } -} - -impl Encode<()> for SubjectPubKeyAlgorithm { - fn encode( - &self, e: &mut Encoder, ctx: &mut (), - ) -> Result<(), minicbor::encode::Error> { - if let Some(&i) = self - .registered_oid - .get_table() - .get_map() - .get_by_right(&self.registered_oid.get_c509_oid().get_oid()) - { - e.i16(i)?; - } else { - AlgorithmIdentifier::encode(&self.algo_identifier, e, ctx)?; - } - Ok(()) - } -} - -impl Decode<'_, ()> for SubjectPubKeyAlgorithm { - fn decode(d: &mut Decoder<'_>, ctx: &mut ()) -> Result { - // Check u8 for 0 - 28 - if d.datatype()? == minicbor::data::Type::U8 { - let i = d.i16()?; - let oid = get_oid_from_int(i).map_err(minicbor::decode::Error::message)?; - Ok(Self::new(oid, None)) - } else { - let algo_identifier = AlgorithmIdentifier::decode(d, ctx)?; - Ok(SubjectPubKeyAlgorithm::new( - algo_identifier.get_oid(), - algo_identifier.get_param().clone(), - )) - } - } -} - -// ------------------Test---------------------- - -#[cfg(test)] -mod test_subject_public_key_algorithm { - use asn1_rs::oid; - - use super::*; - - #[test] - fn test_registered_oid() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - - let spka = SubjectPubKeyAlgorithm::new(oid!(1.3.101 .112), None); - spka.encode(&mut encoder, &mut ()) - .expect("Failed to encode SubjectPubKeyAlgorithm"); - - // Ed25519 - int 10: 0x0a - assert_eq!(hex::encode(buffer.clone()), "0a"); - - let mut decoder = Decoder::new(&buffer); - let decoded_spka = SubjectPubKeyAlgorithm::decode(&mut decoder, &mut ()) - .expect("Failed to decode SubjectPubKeyAlgorithm"); - assert_eq!(decoded_spka, spka); - } - - #[test] - fn test_unregistered_oid() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - - let spka = SubjectPubKeyAlgorithm::new(oid!(2.16.840 .1 .101 .3 .4 .2 .1), None); - spka.encode(&mut encoder, &mut ()) - .expect("Failed to encode SubjectPubKeyAlgorithm"); - - // 2.16.840 .1 .101 .3 .4 .2 .1: 0x49608648016503040201 - assert_eq!(hex::encode(buffer.clone()), "49608648016503040201"); - - let mut decoder = Decoder::new(&buffer); - let decoded_spka = SubjectPubKeyAlgorithm::decode(&mut decoder, &mut ()) - .expect("Failed to decode SubjectPubKeyAlgorithm"); - assert_eq!(decoded_spka, spka); - } - - #[test] - fn test_unregistered_oid_with_param() { - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - - let spka = SubjectPubKeyAlgorithm::new( - oid!(2.16.840 .1 .101 .3 .4 .2 .1), - Some("example".to_string()), - ); - spka.encode(&mut encoder, &mut ()) - .expect("Failed to encode SubjectPubKeyAlgorithm"); - // Array of 2 items: 0x82 - // 2.16.840 .1 .101 .3 .4 .2 .1: 0x49608648016503040201 - // bytes "example": 0x476578616d706c65 - assert_eq!( - hex::encode(buffer.clone()), - "8249608648016503040201476578616d706c65" - ); - - let mut decoder = Decoder::new(&buffer); - let decoded_spka = SubjectPubKeyAlgorithm::decode(&mut decoder, &mut ()) - .expect("Failed to decode SubjectPubKeyAlgorithm"); - assert_eq!(decoded_spka, spka); - } -} diff --git a/catalyst-gateway-crates/c509-certificate/src/c509_time.rs b/catalyst-gateway-crates/c509-certificate/src/c509_time.rs deleted file mode 100644 index 48c370f22e6..00000000000 --- a/catalyst-gateway-crates/c509-certificate/src/c509_time.rs +++ /dev/null @@ -1,101 +0,0 @@ -//! C509 Time - -use minicbor::{encode::Write, Decode, Decoder, Encode, Encoder}; -use serde::{Deserialize, Serialize}; - -/// A struct representing a time where it accept seconds since the Unix epoch. -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] -pub struct Time(i64); - -/// No expiration date in seconds since the Unix epoch. -const NO_EXP_DATE: i64 = 253_402_300_799; - -impl Time { - /// Create a new instance of `Time`. - #[must_use] - pub fn new(time: i64) -> Self { - Self(time) - } - - /// Get the time in i64. - #[must_use] - pub fn to_i64(&self) -> i64 { - self.0 - } -} - -impl Encode<()> for Time { - fn encode( - &self, e: &mut Encoder, _ctx: &mut (), - ) -> Result<(), minicbor::encode::Error> { - if self.0 == NO_EXP_DATE { - e.null()?; - } else { - e.i64(self.0)?; - } - Ok(()) - } -} - -impl Decode<'_, ()> for Time { - fn decode(d: &mut Decoder<'_>, _ctx: &mut ()) -> Result { - match d.datatype()? { - minicbor::data::Type::U8 - | minicbor::data::Type::I8 - | minicbor::data::Type::U16 - | minicbor::data::Type::I16 - | minicbor::data::Type::U32 - | minicbor::data::Type::I32 - | minicbor::data::Type::U64 - | minicbor::data::Type::I64 => { - let time = d.i64()?; - Ok(Time::new(time)) - }, - minicbor::data::Type::Null => { - d.null()?; - Ok(Time::new(NO_EXP_DATE)) - }, - _ => Err(minicbor::decode::Error::message("Invalid type for Time")), - } - } -} - -#[cfg(test)] -mod test_time { - - use super::*; - - #[test] - fn test_encode_decode_no_exp_date() { - let mut buffer = Vec::new(); - let mut encoder = minicbor::Encoder::new(&mut buffer); - let time = Time::new(NO_EXP_DATE); - time.encode(&mut encoder, &mut ()) - .expect("Failed to encode Time"); - // null: 0xf6 - assert_eq!(hex::encode(buffer.clone()), "f6"); - - let mut decoder = minicbor::Decoder::new(&buffer); - let decoded_time = Time::decode(&mut decoder, &mut ()).expect("Failed to decode Time"); - - assert_eq!(decoded_time, time); - } - - // Test reference https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/09/ - // A.1. Example RFC 7925 profiled X.509 Certificate - #[test] - fn test_encode_decode() { - let mut buffer = Vec::new(); - let mut encoder = minicbor::Encoder::new(&mut buffer); - // Jan 1 00:00:00 2023 GMT - let time = Time::new(1_672_531_200); - time.encode(&mut encoder, &mut ()) - .expect("Failed to encode Time"); - assert_eq!(hex::encode(buffer.clone()), "1a63b0cd00"); - - let mut decoder = minicbor::Decoder::new(&buffer); - let decoded_time = Time::decode(&mut decoder, &mut ()).expect("Failed to decode Time"); - - assert_eq!(decoded_time, time); - } -} diff --git a/catalyst-gateway-crates/c509-certificate/src/lib.rs b/catalyst-gateway-crates/c509-certificate/src/lib.rs deleted file mode 100644 index 56fea7c16ab..00000000000 --- a/catalyst-gateway-crates/c509-certificate/src/lib.rs +++ /dev/null @@ -1,134 +0,0 @@ -//! CBOR Encoded X.509 Certificate (C509 Certificate) library -//! -//! This crate provides a functionality for generating C509 Certificate. -//! -//! ## C509 certificate contains 2 parts -//! 1. `TBSCertificate` -//! 2. `issuerSignatureValue` -//! -//! In order to generate an unsigned C509 certificate, the TBS Certificate must be -//! provided. Then the unsigned C509 certificate will then be used to calculate the -//! issuerSignatureValue. -//! -//! # TBS Certificate -//! -//! The To Be Sign Certificate contains the following fields: -//! * c509CertificateType: A certificate type, whether 0 a natively signed C509 -//! certificate following X.509 v3 or 1 a CBOR re-encoded X.509 v3 DER certificate. -//! * certificateSerialNumber: A unique serial number for the certificate. -//! * issuer: The entity that issued the certificate. -//! * validityNotBefore: The duration for which the Certificate Authority (CA) -//! guarantees it will retain information regarding the certificate's status on which -//! the period begins. -//! * validityNotAfter: The duration for which the Certificate Authority (CA) -//! guarantees it will retain information regarding the certificate's status on which -//! the period ends. -//! * subject: The entity associated with the public key stored in the subject public -//! key field. -//! * subjectPublicKeyAlgorithm: The algorithm that the public key is used. -//! * subjectPublicKey: The public key of the subject. -//! * extensions: A list of extensions defined for X.509 v3 certificate, providing -//! additional attributes for users or public keys, and for managing relationships -//! between Certificate Authorities (CAs). -//! * issuerSignatureAlgorithm: The algorithm used to sign the certificate (must be the -//! algorithm uses to create `IssuerSignatureValue`). -//! -//! Please refer to the [C509 Certificate](https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/09/) for more information. - -use anyhow::anyhow; -use c509::C509; -use minicbor::{Decode, Encode}; -use signing::{PrivateKey, PublicKey}; -use tbs_cert::TbsCert; -pub mod c509; -pub mod c509_algo_identifier; -pub mod c509_attributes; -pub mod c509_big_uint; -pub mod c509_extensions; -pub mod c509_general_names; -pub mod c509_issuer_sig_algo; -pub mod c509_name; -pub mod c509_oid; -pub mod c509_subject_pub_key_algo; -pub mod c509_time; -pub mod signing; -mod tables; -pub mod tbs_cert; -pub mod wasm_binding; - -/// Generate a signed or unsigned C509 certificate. -/// -/// # Arguments -/// - `tbs_cert` - A TBS certificate. -/// - `private_key` - An optional private key, if provided certificate is signed. -/// -/// # Returns -/// Returns a signed or unsigned C509 certificate. -/// -/// # Errors -/// -/// Returns an error if tne data cannot be converted to CBOR bytes. - -pub fn generate(tbs_cert: &TbsCert, private_key: Option<&PrivateKey>) -> anyhow::Result> { - // Encode the TbsCert - let encoded_tbs = { - let mut buffer = Vec::new(); - let mut encoder = minicbor::Encoder::new(&mut buffer); - tbs_cert.encode(&mut encoder, &mut ())?; - buffer - }; - let sign_data = private_key.map(|pk| pk.sign(&encoded_tbs)); - - // Encode the whole C509 certificate including `TbSCert` and `issuerSignatureValue` - let encoded_c509 = { - let mut buffer = Vec::new(); - let mut encoder = minicbor::Encoder::new(&mut buffer); - let c509 = C509::new(tbs_cert.clone(), sign_data); - c509.encode(&mut encoder, &mut ())?; - buffer - }; - Ok(encoded_c509) -} - -/// Verify the signature of a C509 certificate. -/// -/// # Arguments -/// - `c509` - The cbor encoded C509 certificate to verify. -/// - `public_key` - The public key used to verify the certificate. -/// -/// # Errors -/// Returns an error if the `issuer_signature_value` is invalid or the signature cannot be -/// verified. -pub fn verify(c509: &[u8], public_key: &PublicKey) -> anyhow::Result<()> { - let mut d = minicbor::Decoder::new(c509); - let c509 = C509::decode(&mut d, &mut ())?; - let mut encoded_tbs = Vec::new(); - let mut encoder = minicbor::Encoder::new(&mut encoded_tbs); - c509.get_tbs_cert().encode(&mut encoder, &mut ())?; - let issuer_sig = c509.get_issuer_signature_value().clone().ok_or(anyhow!( - "Signature verification failed, No issuer signature" - ))?; - public_key.verify(&encoded_tbs, &issuer_sig) -} - -#[cfg(test)] -mod test { - use std::str::FromStr; - - use signing::tests::private_key_str; - use tbs_cert::test_tbs_cert::tbs; - - use super::*; - - #[test] - fn test_generate_and_verify_signed_c509_cert() { - let tbs_cert = tbs(); - - let private_key = FromStr::from_str(&private_key_str()).expect("Cannot create private key"); - - let signed_c509 = generate(&tbs_cert, Some(&private_key)) - .expect("Failed to generate signed C509 certificate"); - - assert!(verify(&signed_c509, &private_key.public_key()).is_ok()); - } -} diff --git a/catalyst-gateway-crates/c509-certificate/src/signing.rs b/catalyst-gateway-crates/c509-certificate/src/signing.rs deleted file mode 100644 index c118c57875c..00000000000 --- a/catalyst-gateway-crates/c509-certificate/src/signing.rs +++ /dev/null @@ -1,227 +0,0 @@ -//! ED25519 public and private key implementation. - -// cspell: words outpubkey genpkey - -use std::{fmt::Display, path::Path, str::FromStr}; - -use ed25519_dalek::{ - ed25519::signature::Signer, - pkcs8::{DecodePrivateKey, DecodePublicKey}, - SigningKey, VerifyingKey, -}; -use wasm_bindgen::prelude::wasm_bindgen; - -/// Public or private key decoding from string error. -#[derive(thiserror::Error, Debug)] -#[error("Cannot decode key from string. Invalid PEM format.")] -struct KeyPemDecodingError; - -/// Ed25519 private key instance. -/// Wrapper over `ed25519_dalek::SigningKey`. -#[allow(dead_code)] -#[wasm_bindgen] -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct PrivateKey(SigningKey); - -/// File open and read error. -#[derive(thiserror::Error, Debug)] -struct FileError { - /// File location. - location: String, - /// File open and read error. - msg: Option, -} - -#[allow(dead_code)] -impl FileError { - /// Create a new `FileError` instance from a string location. - fn from_string(location: String, msg: Option) -> Self { - Self { location, msg } - } - - /// Create a new `FileError` instance from a path location. - fn from_path>(path: P, msg: Option) -> Self { - Self { - location: path.as_ref().display().to_string(), - msg, - } - } -} - -impl Display for FileError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let msg = format!("Cannot open or read file at {0}", self.location); - let err = self - .msg - .as_ref() - .map(|msg| format!(":\n{msg}")) - .unwrap_or_default(); - writeln!(f, "{msg}{err}",) - } -} - -#[allow(dead_code)] -impl PrivateKey { - /// Create new public key from file decoded in PEM format. - /// - /// # Errors - /// Returns an error if the file cannot be opened or read. - pub fn from_file>(path: P) -> anyhow::Result { - let str = std::fs::read_to_string(&path).map_err(|_| FileError::from_path(&path, None))?; - Ok(Self::from_str(&str).map_err(|err| FileError::from_path(&path, Some(err)))?) - } - - /// Get associated public key. - #[must_use] - pub fn public_key(&self) -> PublicKey { - PublicKey(self.0.verifying_key()) - } - - /// Sign the message with the current private key. - /// Returns the signature bytes. - #[must_use] - pub fn sign(&self, msg: &[u8]) -> Vec { - self.0.sign(msg).to_vec() - } -} - -impl FromStr for PrivateKey { - type Err = anyhow::Error; - - fn from_str(str: &str) -> Result { - let key = SigningKey::from_pkcs8_pem(str).map_err(|_| KeyPemDecodingError)?; - Ok(Self(key)) - } -} - -/// Ed25519 public key instance. -/// Wrapper over `ed25519_dalek::VerifyingKey`. -#[derive(Clone, Debug, PartialEq, Eq)] -#[wasm_bindgen] -pub struct PublicKey(VerifyingKey); - -#[allow(dead_code)] -impl PublicKey { - /// Create new public key from file decoded in PEM format. - /// - /// # Errors - /// Returns an error if the file cannot be opened or read. - pub fn from_file>(path: P) -> anyhow::Result { - let str = std::fs::read_to_string(&path).map_err(|_| FileError::from_path(&path, None))?; - Ok(Self::from_str(&str).map_err(|err| FileError::from_path(&path, Some(err)))?) - } - - /// Create new public key from raw bytes. - /// - /// # Errors - /// Returns an error if the provided bytes are not a valid public key. - pub fn from_bytes(bytes: &[u8]) -> anyhow::Result { - let key = VerifyingKey::from_bytes(bytes.try_into()?)?; - Ok(Self(key)) - } - - /// Convert public key to raw bytes. - #[must_use] - pub fn to_bytes(&self) -> Vec { - self.0.to_bytes().to_vec() - } - - /// Verify signature of the message with the current public key. - /// - /// # Errors - /// Returns an error if the signature is invalid. - pub fn verify(&self, msg: &[u8], signature_bytes: &[u8]) -> anyhow::Result<()> { - let signature_bytes = signature_bytes.try_into().map_err(|_| { - anyhow::anyhow!( - "Invalid signature bytes size: expected {}, provided {}.", - ed25519_dalek::Signature::BYTE_SIZE, - signature_bytes.len() - ) - })?; - let signature = ed25519_dalek::Signature::from_bytes(signature_bytes); - self.0.verify_strict(msg, &signature)?; - Ok(()) - } -} - -impl FromStr for PublicKey { - type Err = anyhow::Error; - - fn from_str(str: &str) -> Result { - let key = VerifyingKey::from_public_key_pem(str).map_err(|_| KeyPemDecodingError)?; - Ok(Self(key)) - } -} - -#[cfg(test)] -pub(crate) mod tests { - use std::env::temp_dir; - - use super::*; - - /// An Ed25519 private key in PEM format. - /// Generated with `openssl` tool: - /// ```shell - /// openssl genpkey -algorithm=ED25519 -out=private.pem -outpubkey=public.pem - /// ``` - pub(crate) fn private_key_str() -> String { - format!( - "{}\n{}\n{}", - "-----BEGIN PRIVATE KEY-----", - "MC4CAQAwBQYDK2VwBCIEIP1iI3LF7h89yY6QZmhDp4Y5FmTQ4oasbz2lEiaqqTzV", - "-----END PRIVATE KEY-----" - ) - } - - /// An Ed25519 public key in PEM format. - /// This public key is corresponding to the `private_key_str()` private key. - /// Generated with `openssl` tool: - /// ```shell - /// openssl genpkey -algorithm=ED25519 -out=private.pem -outpubkey=public.pem - /// ``` - pub(crate) fn public_key_str() -> String { - format!( - "{}\n{}\n{}", - "-----BEGIN PUBLIC KEY-----", - "MCowBQYDK2VwAyEAtFuCleJwHS28jUCT+ulLl5c1+MXhehhDz2SimOhmWaI=", - "-----END PUBLIC KEY-----" - ) - } - - #[test] - fn private_key_from_file_test() { - let dir = temp_dir(); - - let private_key_path = dir.as_path().join("private.pem"); - std::fs::write(&private_key_path, private_key_str()) - .expect("Cannot create private.pem file"); - - let _key = - PrivateKey::from_file(private_key_path).expect("Cannot create private key from file"); - } - - #[test] - fn public_private_key_test() { - let private_key = - PrivateKey::from_str(&private_key_str()).expect("Cannot create private key"); - let public_key = PublicKey::from_str(&public_key_str()).expect("Cannot create public key"); - - assert_eq!(private_key.public_key(), public_key); - } - - #[test] - fn sign_test() { - let private_key = - PrivateKey::from_str(&private_key_str()).expect("Cannot create private key"); - let public_key = PublicKey::from_str(&public_key_str()).expect("Cannot create public key"); - - let msg = b"test"; - - let signature = private_key.sign(msg); - assert!(public_key.verify(msg, &signature).is_ok()); - assert!( - public_key.verify(b"corrupted", &signature).is_err(), - "Provided msg is not actually signed." - ); - } -} diff --git a/catalyst-gateway-crates/c509-certificate/src/tables.rs b/catalyst-gateway-crates/c509-certificate/src/tables.rs deleted file mode 100644 index 67e83fde4fd..00000000000 --- a/catalyst-gateway-crates/c509-certificate/src/tables.rs +++ /dev/null @@ -1,78 +0,0 @@ -//! A bimap table for bidirectional lookup. - -use std::hash::Hash; - -use asn1_rs::Oid; -use bimap::BiMap; - -/// A trait that represents a table structure with key-value pairs. -/// -/// # Type Parameters -/// -/// * `K` - The type of the keys in the table. -/// * `V` - The type of the values in the table. -pub(crate) trait TableTrait { - /// Create new instance of the map table. - fn new() -> Self; - /// Add the key-value pair to the map table. - fn add(&mut self, k: K, v: V); - /// Get the bimap of the map table. - fn get_map(&self) -> &BiMap; -} - -// ----------------------------------------- - -/// A struct that represents a table mapping integers to any type that -/// implements `Eq` and `Hash`. -/// i16 is used because the int value in C509 certificate registry can be -256 to 255. -#[derive(Debug, Clone, PartialEq)] -pub(crate) struct IntTable { - /// A bimap table for bidirectional lookup where it map between i16 and other type. - map: BiMap, -} - -impl TableTrait for IntTable { - /// Create new instance of `IntTable`. - fn new() -> Self { - Self { map: BiMap::new() } - } - - /// Add the key-value pair to the map table. - fn add(&mut self, k: i16, v: T) { - self.map.insert(k, v); - } - - /// Get the bimap of the map table. - fn get_map(&self) -> &BiMap { - &self.map - } -} - -// ----------------------------------------- - -/// A struct represents a table of integer to OID. -#[derive(Debug, Clone, PartialEq)] -pub(crate) struct IntegerToOidTable { - /// A table of integer to OID, provide a bidirectional lookup. - table: IntTable>, -} - -#[allow(dead_code)] -impl IntegerToOidTable { - /// Create new instance of `IntegerToOidTable`. - pub(crate) fn new() -> Self { - Self { - table: IntTable::>::new(), - } - } - - /// Add the key-value pair to the map table. - pub(crate) fn add(&mut self, k: i16, v: Oid<'static>) { - self.table.add(k, v); - } - - /// Get the bimap of the map table. - pub(crate) fn get_map(&self) -> &BiMap> { - self.table.get_map() - } -} diff --git a/catalyst-gateway-crates/c509-certificate/src/tbs_cert.rs b/catalyst-gateway-crates/c509-certificate/src/tbs_cert.rs deleted file mode 100644 index 0154a5d40e1..00000000000 --- a/catalyst-gateway-crates/c509-certificate/src/tbs_cert.rs +++ /dev/null @@ -1,477 +0,0 @@ -//! To Be Sign Certificate (TBS Certificate) use to construct a C509 certificate. - -use minicbor::{encode::Write, Decode, Decoder, Encode, Encoder}; -use serde::{Deserialize, Serialize}; - -use crate::{ - c509_big_uint::UnwrappedBigUint, c509_extensions::Extensions, - c509_issuer_sig_algo::IssuerSignatureAlgorithm, c509_name::Name, - c509_subject_pub_key_algo::SubjectPubKeyAlgorithm, c509_time::Time, -}; - -/// A struct represents a To Be Signed Certificate (TBS Certificate). -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] -#[serde(rename_all = "snake_case")] -pub struct TbsCert { - /// Certificate type. - c509_certificate_type: u8, - /// Serial number of the certificate. - certificate_serial_number: UnwrappedBigUint, - /// Issuer - issuer: Name, - /// Validity not before. - validity_not_before: Time, - /// Validity not after. - validity_not_after: Time, - /// Subject - subject: Name, - /// Subject Public Key Algorithm - subject_public_key_algorithm: SubjectPubKeyAlgorithm, - /// Subject Public Key value - subject_public_key: Vec, - /// Extensions - extensions: Extensions, - /// Issuer Signature Algorithm - issuer_signature_algorithm: IssuerSignatureAlgorithm, -} - -impl TbsCert { - /// Create a new instance of TBS Certificate. - #[must_use] - #[allow(clippy::too_many_arguments)] - pub fn new( - c509_certificate_type: u8, certificate_serial_number: UnwrappedBigUint, issuer: Name, - validity_not_before: Time, validity_not_after: Time, subject: Name, - subject_public_key_algorithm: SubjectPubKeyAlgorithm, subject_public_key: Vec, - extensions: Extensions, issuer_signature_algorithm: IssuerSignatureAlgorithm, - ) -> Self { - Self { - c509_certificate_type, - certificate_serial_number, - issuer, - validity_not_before, - validity_not_after, - subject, - subject_public_key_algorithm, - subject_public_key, - extensions, - issuer_signature_algorithm, - } - } - - /// Get the certificate type. - #[must_use] - pub fn get_c509_certificate_type(&self) -> u8 { - self.c509_certificate_type - } - - /// Get the certificate serial number. - #[must_use] - pub fn get_certificate_serial_number(&self) -> &UnwrappedBigUint { - &self.certificate_serial_number - } - - /// Get the issuer. - #[must_use] - pub fn get_issuer(&self) -> &Name { - &self.issuer - } - - /// Get the validity not before. - #[must_use] - pub fn get_validity_not_before(&self) -> &Time { - &self.validity_not_before - } - - /// Get the validity not after. - #[must_use] - pub fn get_validity_not_after(&self) -> &Time { - &self.validity_not_after - } - - /// Get the subject. - #[must_use] - pub fn get_subject(&self) -> &Name { - &self.subject - } - - /// Get the subject public key algorithm. - #[must_use] - pub fn get_subject_public_key_algorithm(&self) -> &SubjectPubKeyAlgorithm { - &self.subject_public_key_algorithm - } - - /// Get the subject public key. - #[must_use] - pub fn get_subject_public_key(&self) -> &[u8] { - &self.subject_public_key - } - - /// Get the extensions. - #[must_use] - pub fn get_extensions(&self) -> &Extensions { - &self.extensions - } - - /// Get the issuer signature algorithm. - #[must_use] - pub fn get_issuer_signature_algorithm(&self) -> &IssuerSignatureAlgorithm { - &self.issuer_signature_algorithm - } -} - -impl Encode<()> for TbsCert { - fn encode( - &self, e: &mut Encoder, ctx: &mut (), - ) -> Result<(), minicbor::encode::Error> { - e.u8(self.c509_certificate_type)?; - self.certificate_serial_number.encode(e, ctx)?; - self.issuer.encode(e, ctx)?; - self.validity_not_before.encode(e, ctx)?; - self.validity_not_after.encode(e, ctx)?; - self.subject.encode(e, ctx)?; - self.subject_public_key_algorithm.encode(e, ctx)?; - e.bytes(&self.subject_public_key)?; - self.extensions.encode(e, ctx)?; - self.issuer_signature_algorithm.encode(e, ctx)?; - Ok(()) - } -} - -impl Decode<'_, ()> for TbsCert { - fn decode(d: &mut Decoder<'_>, ctx: &mut ()) -> Result { - let cert_type = d.u8()?; - let serial_number = UnwrappedBigUint::decode(d, ctx)?; - let issuer = Name::decode(d, ctx)?; - let not_before = Time::decode(d, ctx)?; - let not_after = Time::decode(d, ctx)?; - let subject = Name::decode(d, ctx)?; - let subject_public_key_algorithm = SubjectPubKeyAlgorithm::decode(d, ctx)?; - let subject_public_key = d.bytes()?; - let extensions = Extensions::decode(d, ctx)?; - let issuer_signature_algorithm = IssuerSignatureAlgorithm::decode(d, ctx)?; - - Ok(TbsCert::new( - cert_type, - serial_number, - issuer, - not_before, - not_after, - subject, - subject_public_key_algorithm, - subject_public_key.to_vec(), - extensions, - issuer_signature_algorithm, - )) - } -} - -// ------------------Test---------------------- - -// Notes -// - Test is modified to match the current encode and decode where `subject_public_key` -// doesn't support -// special case for rsaEncryption and id-ecPublicKey. -// - Currently support natively signed c509 certificate, so all text strings -// are UTF-8 encoded and all attributeType SHALL be non-negative -// - Some Extension values are not supported yet. - -#[cfg(test)] -pub(crate) mod test_tbs_cert { - use asn1_rs::oid; - - use super::*; - use crate::{ - c509_attributes::attribute::{Attribute, AttributeValue}, - c509_extensions::{ - alt_name::{AlternativeName, GeneralNamesOrText}, - extension::{Extension, ExtensionValue}, - }, - c509_general_names::{ - general_name::{GeneralName, GeneralNameTypeRegistry, GeneralNameValue}, - other_name_hw_module::OtherNameHardwareModuleName, - GeneralNames, - }, - c509_name::{ - rdn::RelativeDistinguishedName, - test_name::{name_cn_eui_mac, name_cn_text, names}, - NameValue, - }, - }; - - // Mnemonic: match mad promote group rival case - const PUBKEY: [u8; 8] = [0x88, 0xD0, 0xB6, 0xB0, 0xB3, 0x7B, 0xAA, 0x46]; - - // Test reference https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/09/ - // A.1. Example RFC 7925 profiled X.509 Certificate - // - // - // Certificate: - // Data: - // Version: 3 (0x2) - // Serial Number: 128269 (0x1f50d) - // Signature Algorithm: ecdsa-with-SHA256 - // Issuer: CN=RFC test CA - // Validity - // Not Before: Jan 1 00:00:00 2023 GMT - // Not After : Jan 1 00:00:00 2026 GMT - // Subject: CN=01-23-45-FF-FE-67-89-AB - // Subject Public Key Info: - // Public Key Algorithm: id-ecPublicKey - // Public-Key: (256 bit) - // pub: - // 04:b1:21:6a:b9:6e:5b:3b:33:40:f5:bd:f0:2e:69: - // 3f:16:21:3a:04:52:5e:d4:44:50:b1:01:9c:2d:fd: - // 38:38:ab:ac:4e:14:d8:6c:09:83:ed:5e:9e:ef:24: - // 48:c6:86:1c:c4:06:54:71:77:e6:02:60:30:d0:51: - // f7:79:2a:c2:06 - // ASN1 OID: prime256v1 - // NIST CURVE: P-256 - // X509v3 extensions: - // X509v3 Key Usage: - // Digital Signature - // Signature Algorithm: ecdsa-with-SHA256 - // 30:46:02:21:00:d4:32:0b:1d:68:49:e3:09:21:9d:30:03:7e: - // 13:81:66:f2:50:82:47:dd:da:e7:6c:ce:ea:55:05:3c:10:8e: - // 90:02:21:00:d5:51:f6:d6:01:06:f1:ab:b4:84:cf:be:62:56: - // c1:78:e4:ac:33:14:ea:19:19:1e:8b:60:7d:a5:ae:3b:da:16 - // - // 01 - // 43 01 F5 0D - // 6B 52 46 43 20 74 65 73 74 20 43 41 - // 1A 63 B0 CD 00 - // 1A 69 55 B9 00 - // 47 01 01 23 45 67 89 AB - // 01 - // 58 21 02 B1 21 6A B9 6E 5B 3B 33 40 F5 BD F0 2E 69 3F 16 21 3A 04 52 - // 5E D4 44 50 B1 01 9C 2D FD 38 38 AB - // 01 - // 00 - // 58 40 D4 32 0B 1D 68 49 E3 09 21 9D 30 03 7E 13 81 66 F2 50 82 47 DD - // DA E7 6C CE EA 55 05 3C 10 8E 90 D5 51 F6 D6 01 06 F1 AB B4 84 CF BE - // 62 56 C1 78 E4 AC 33 14 EA 19 19 1E 8B 60 7D A5 AE 3B DA 16 - - pub(crate) fn tbs() -> TbsCert { - fn extensions() -> Extensions { - let mut exts = Extensions::new(); - exts.add_ext(Extension::new( - oid!(2.5.29 .15), - ExtensionValue::Int(1), - false, - )); - exts - } - - TbsCert::new( - 1, - UnwrappedBigUint::new(128_269), - name_cn_text().0, - Time::new(1_672_531_200), - Time::new(1_767_225_600), - name_cn_eui_mac().0, - SubjectPubKeyAlgorithm::new(oid!(1.2.840 .10045 .2 .1), None), - PUBKEY.to_vec(), - extensions(), - IssuerSignatureAlgorithm::new(oid!(1.2.840 .10045 .4 .3 .2), None), - ) - } - - #[test] - fn encode_decode_tbs_cert() { - let tbs_cert = tbs(); - - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - tbs_cert - .encode(&mut encoder, &mut ()) - .expect("Failed to encode TBS Certificate"); - - // c509_certificate_type: 0x01 - // certificate_serial_number: 0x4301f50d - // issuer: 0x6b5246432074657374204341 - // validity_not_before: 0x1a63b0cd00 - // validity_not_after: 0x1a6955b900 - // subject: 0x47010123456789ab - // subject_public_key_algorithm: 0x01 - // subject_public_key: 0x4888d0b6b0b37baa46 - // extensions: 0x01 - // issuer_signature_algorithm: 0x00 - - assert_eq!( - hex::encode(buffer.clone()), - "014301f50d6b52464320746573742043411a63b0cd001a6955b90047010123456789ab014888d0b6b0b37baa460100" - ); - - let mut decoder = Decoder::new(&buffer); - let decoded_tbs = - TbsCert::decode(&mut decoder, &mut ()).expect("Failed to decode TBS Certificate"); - assert_eq!(decoded_tbs, tbs_cert); - } - - // Test reference https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/09/ - // A.2. Example IEEE 802.1AR profiled X.509 Certificate - // - // Certificate: - // Data: - // Version: 3 (0x2) - // Serial Number: 9112578475118446130 (0x7e7661d7b54e4632) - // Signature Algorithm: ecdsa-with-SHA256 - // Issuer: C=US, ST=CA, O=Example Inc, OU=certification, CN=802.1AR CA - // Validity - // Not Before: Jan 31 11:29:16 2019 GMT - // Not After : Dec 31 23:59:59 9999 GMT - // Subject: C=US, ST=CA, L=LA, O=example Inc, OU=IoT/serialNumber=Wt1234 - // Subject Public Key Info: - // Public Key Algorithm: id-ecPublicKey - // Public-Key: (256 bit) - // pub: - // 04:c8:b4:21:f1:1c:25:e4:7e:3a:c5:71:23:bf:2d: - // 9f:dc:49:4f:02:8b:c3:51:cc:80:c0:3f:15:0b:f5: - // 0c:ff:95:8d:75:41:9d:81:a6:a2:45:df:fa:e7:90: - // be:95:cf:75:f6:02:f9:15:26:18:f8:16:a2:b2:3b: - // 56:38:e5:9f:d9 - // ASN1 OID: prime256v1 - // NIST CURVE: P-256 - // X509v3 extensions: - // X509v3 Basic Constraints: - // CA:FALSE - // X509v3 Subject Key Identifier: - // 96:60:0D:87:16:BF:7F:D0:E7:52:D0:AC:76:07:77:AD:66:5D:02:A0 - // X509v3 Authority Key Identifier: - // 68:D1:65:51:F9:51:BF:C8:2A:43:1D:0D:9F:08:BC:2D:20:5B:11:60 - // X509v3 Key Usage: critical - // Digital Signature, Key Encipherment - // X509v3 Subject Alternative Name: - // otherName: - // type-id: 1.3.6.1.5.5.7.8.4 (id-on-hardwareModuleName) - // value: - // hwType: 1.3.6.1.4.1.6175.10.1 - // hwSerialNum: 01:02:03:04 - // Signature Algorithm: ecdsa-with-SHA256 - // Signature Value: - // 30:46:02:21:00:c0:d8:19:96:d2:50:7d:69:3f:3c:48:ea:a5: - // ee:94:91:bd:a6:db:21:40:99:d9:81:17:c6:3b:36:13:74:cd: - // 86:02:21:00:a7:74:98:9f:4c:32:1a:5c:f2:5d:83:2a:4d:33: - // 6a:08:ad:67:df:20:f1:50:64:21:18:8a:0a:de:6d:34:92:36 - // - // 01 48 7E 76 61 D7 B5 4E 46 32 8A 23 62 55 53 06 62 43 41 08 6B 45 78 - // 61 6D 70 6C 65 20 49 6E 63 09 6D 63 65 72 74 69 66 69 63 61 74 69 6F - // 6E 01 6A 38 30 32 2E 31 41 52 20 43 41 1A 5C 52 DC 0C F6 8C 23 62 55 - // 53 06 62 43 41 05 62 4C 41 08 6B 65 78 61 6D 70 6C 65 20 49 6E 63 09 - // 63 49 6F 54 22 66 57 74 31 32 33 34 01 58 21 03 C8 B4 21 F1 1C 25 E4 - // 7E 3A C5 71 23 BF 2D 9F DC 49 4F 02 8B C3 51 CC 80 C0 3F 15 0B F5 0C - // FF 95 8A 04 21 01 54 96 60 0D 87 16 BF 7F D0 E7 52 D0 AC 76 07 77 AD - // 66 5D 02 A0 07 54 68 D1 65 51 F9 51 BF C8 2A 43 1D 0D 9F 08 BC 2D 20 - // 5B 11 60 21 05 03 82 20 82 49 2B 06 01 04 01 B0 1F 0A 01 44 01 02 03 - // 04 00 58 40 C0 D8 19 96 D2 50 7D 69 3F 3C 48 EA A5 EE 94 91 BD A6 DB - // 21 40 99 D9 81 17 C6 3B 36 13 74 CD 86 A7 74 98 9F 4C 32 1A 5C F2 5D - // 83 2A 4D 33 6A 08 AD 67 DF 20 F1 50 64 21 18 8A 0A DE 6D 34 92 36 - - #[test] - fn tbs_cert2() { - // ---------helper---------- - // C=US, ST=CA, L=LA, O=example Inc, OU=IoT/serialNumber=Wt1234 - fn subject() -> Name { - let mut attr1 = Attribute::new(oid!(2.5.4 .6)); - attr1.add_value(AttributeValue::Text("US".to_string())); - let mut attr2 = Attribute::new(oid!(2.5.4 .8)); - attr2.add_value(AttributeValue::Text("CA".to_string())); - let mut attr3 = Attribute::new(oid!(2.5.4 .7)); - attr3.add_value(AttributeValue::Text("LA".to_string())); - let mut attr4 = Attribute::new(oid!(2.5.4 .10)); - attr4.add_value(AttributeValue::Text("example Inc".to_string())); - let mut attr5 = Attribute::new(oid!(2.5.4 .11)); - attr5.add_value(AttributeValue::Text("IoT".to_string())); - let mut attr6 = Attribute::new(oid!(2.5.4 .5)); - attr6.add_value(AttributeValue::Text("Wt1234".to_string())); - - let mut rdn = RelativeDistinguishedName::new(); - rdn.add_attr(attr1); - rdn.add_attr(attr2); - rdn.add_attr(attr3); - rdn.add_attr(attr4); - rdn.add_attr(attr5); - rdn.add_attr(attr6); - - Name::new(NameValue::RelativeDistinguishedName(rdn)) - } - - fn extensions() -> Extensions { - let mut exts = Extensions::new(); - exts.add_ext(Extension::new( - oid!(2.5.29 .19), - ExtensionValue::Int(-2), - false, - )); - exts.add_ext(Extension::new( - oid!(2.5.29 .14), - ExtensionValue::Bytes( - [ - 0x96, 0x60, 0x0D, 0x87, 0x16, 0xBF, 0x7F, 0xD0, 0xE7, 0x52, 0xD0, 0xAC, - 0x76, 0x07, 0x77, 0xAD, 0x66, 0x5D, 0x02, 0xA0, - ] - .to_vec(), - ), - false, - )); - exts.add_ext(Extension::new( - oid!(2.5.29 .15), - ExtensionValue::Int(5), - true, - )); - let mut gns = GeneralNames::new(); - let hw = OtherNameHardwareModuleName::new(oid!(1.3.6 .1 .4 .1 .6175 .10 .1), vec![ - 0x01, 0x02, 0x03, 0x04, - ]); - gns.add_gn(GeneralName::new( - GeneralNameTypeRegistry::OtherNameHardwareModuleName, - GeneralNameValue::OtherNameHWModuleName(hw), - )); - - exts.add_ext(Extension::new( - oid!(2.5.29 .17), - ExtensionValue::AlternativeName(AlternativeName::new( - GeneralNamesOrText::GeneralNames(gns), - )), - false, - )); - - exts - } - - let tbs_cert = TbsCert::new( - 1, - UnwrappedBigUint::new(9_112_578_475_118_446_130), - names().0, - Time::new(1_548_934_156), - Time::new(253_402_300_799), - subject(), - SubjectPubKeyAlgorithm::new(oid!(1.2.840 .10045 .2 .1), None), - PUBKEY.to_vec(), - extensions(), - IssuerSignatureAlgorithm::new(oid!(1.2.840 .10045 .4 .3 .2), None), - ); - - let mut buffer = Vec::new(); - let mut encoder = Encoder::new(&mut buffer); - tbs_cert - .encode(&mut encoder, &mut ()) - .expect("Failed to encode TBS Certificate"); - // c509_certificate_type: 0x01 - // certificate_serial_number: 0x487e7661d7b54e4632 - // issuer: 0x8a0462555306624341086b4578616d706c6520496e63096d63657274696669636174696f6e016a3830322e314152204341 - // validity_not_before: 0x1a5c52dc0c - // validity_not_after: 0xf6 - // subject: 0x8c046255530662434105624c41086b6578616d706c6520496e630963496f540366577431323334 - // subject_public_key_algorithm: 0x01 - // subject_public_key: 0x4888d0b6b0b37baa46 - // extensions: - // 0x840421015496600d8716bf7fd0e752d0ac760777ad665d02a0210503822082492b06010401b01f0a014401020304 - // issuer_signature_algorithm: 0x00 - assert_eq!(hex::encode(buffer.clone()), "01487e7661d7b54e46328a0462555306624341086b4578616d706c6520496e63096d63657274696669636174696f6e016a3830322e3141522043411a5c52dc0cf68c046255530662434105624c41086b6578616d706c6520496e630963496f540366577431323334014888d0b6b0b37baa46840421015496600d8716bf7fd0e752d0ac760777ad665d02a0210503822082492b06010401b01f0a01440102030400"); - let mut decoder = Decoder::new(&buffer); - let decoded_tbs = - TbsCert::decode(&mut decoder, &mut ()).expect("Failed to decode TBS Certificate"); - assert_eq!(decoded_tbs, tbs_cert); - } -} diff --git a/catalyst-gateway-crates/c509-certificate/src/wasm_binding.rs b/catalyst-gateway-crates/c509-certificate/src/wasm_binding.rs deleted file mode 100644 index 29aea24c59a..00000000000 --- a/catalyst-gateway-crates/c509-certificate/src/wasm_binding.rs +++ /dev/null @@ -1,76 +0,0 @@ -//! WASM binding wrapper for the C509 certificate crate. - -use std::str::FromStr; - -use minicbor::Decode; -use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; - -use crate::{ - signing::{PrivateKey, PublicKey}, - tbs_cert::TbsCert, -}; - -/// Wrapper for generate function. -/// -/// # Errors -/// Returns an error if the provided TbsCert JSValue cannot be converted `TbsCert` or C509 -/// cannot be generated. -#[wasm_bindgen] -// wasm_bindgen does not allowed ref passing unless it implement `RefFromWasmAbi`. -#[allow(clippy::needless_pass_by_value)] -pub fn generate(tbs_cert: JsValue, private_key: Option) -> Result { - let tbs_cert: TbsCert = serde_wasm_bindgen::from_value(tbs_cert)?; - let c509 = crate::generate(&tbs_cert, private_key.as_ref()) - .map_err(|e| JsValue::from(e.to_string()))?; - Ok(serde_wasm_bindgen::to_value(&c509)?) -} - -/// Wrapper for verify function. -/// -/// # Errors -/// Returns an error if the signature is invalid or the signature cannot be verified. -#[wasm_bindgen] -pub fn verify(c509: &[u8], public_key: &PublicKey) -> Result { - match crate::verify(c509, public_key) { - Ok(()) => Ok(JsValue::from("Signature verified")), - Err(e) => Err(JsValue::from(e.to_string())), - } -} - -/// Wrapper for decoding vector of C509 back to readable object. -/// -/// # Errors -/// Returns an error if the provided vector is not a valid C509 certificate. -#[wasm_bindgen] -pub fn decode(c509: &[u8]) -> Result { - let mut d = minicbor::Decoder::new(c509); - let c509 = crate::C509::decode(&mut d, &mut ()).map_err(|e| JsValue::from(e.to_string()))?; - Ok(serde_wasm_bindgen::to_value(&c509)?) -} - -#[wasm_bindgen] -impl PrivateKey { - /// Convert string to private key. - /// - /// # Errors - /// Returns an error if the provided string is not a valid private key. - #[wasm_bindgen] - pub fn str_to_sk(str: &str) -> Result { - FromStr::from_str(str).map_err(|_| { - JsValue::from("Cannot decode private key from string. Invalid PEM format.") - }) - } -} - -#[wasm_bindgen] -impl PublicKey { - /// Convert string to public key. - /// - /// # Errors - /// Returns an error if the provided string is not a valid public key. - #[wasm_bindgen] - pub fn str_to_pk(str: &str) -> Result { - FromStr::from_str(str) - .map_err(|_| JsValue::from("Cannot decode public key from string. Invalid PEM format.")) - } -} diff --git a/catalyst-gateway/Cargo.toml b/catalyst-gateway/Cargo.toml index ccb8356c2c3..e2fd64ec089 100644 --- a/catalyst-gateway/Cargo.toml +++ b/catalyst-gateway/Cargo.toml @@ -2,7 +2,6 @@ resolver = "2" members = [ "bin", - # "crates/", ] [workspace.package] @@ -15,55 +14,6 @@ homepage = "https://input-output-hk.github.io/catalyst-voices" repository = "https://github.com/input-output-hk/catalyst-voices" license = "MIT OR Apache-2.0" -[workspace.dependencies] -clap = "4.5.13" -tracing = "0.1.40" -tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } -serde = "1.0.204" -serde_json = "1.0.121" -poem = "3.0.4" -poem-openapi = "5.0.3" -prometheus = "0.13.4" -cryptoxide = "0.4.4" -uuid = "1.10.0" -panic-message = "0.3" -cpu-time = "1.0" -ulid = "1.1.3" -rust-embed = "8.5.0" -url = "2.5.2" -thiserror = "1.0.63" -chrono = "0.4.38" -async-trait = "0.1.81" -rust_decimal = "1.35.0" -bb8 = "0.8.5" -bb8-postgres = "0.8.1" -tokio-postgres = "0.7.11" -tokio = "1.39.2" -dotenvy = "0.15.7" -local-ip-address = "0.6.1" -gethostname = "0.5.0" -hex = "0.4.3" -handlebars = "6.0.0" -anyhow = "1.0.86" -cddl = "0.9.4" -ciborium = "0.2.2" -pallas = "0.29.0" -cardano-chain-follower = { git = "https://github.com/input-output-hk/hermes.git", branch = "feat/auto-sync-mithril", version="0.2.0" } -stringzilla = "3.8.4" -duration-string = "0.4.0" -build-info = "0.0.38" -build-info-build = "0.0.38" -ed25519-dalek = "2.1.1" -scylla = { version = "0.13.1", features = ["ssl", "full-serialization"]} -strum = { version = "0.26.3", features = ["derive"] } -strum_macros = "0.26.4" -openssl = { version = "0.10.66", features = ["vendored"] } -num-bigint = "0.4.6" -futures = "0.3.30" -rand = "0.8.5" -moka = { version = "0.12.8", features=["future"] } -crossbeam-skiplist = "0.1.3" - [workspace.lints.rust] warnings = "deny" missing_docs = "deny" diff --git a/catalyst-gateway/Earthfile b/catalyst-gateway/Earthfile index 2fe8da2e10e..2959fa6aada 100644 --- a/catalyst-gateway/Earthfile +++ b/catalyst-gateway/Earthfile @@ -1,7 +1,6 @@ VERSION 0.8 -IMPORT github.com/input-output-hk/catalyst-ci/earthly/rust:feat/faster-rust-tool-install AS rust-ci -IMPORT github.com/input-output-hk/catalyst-ci/earthly/mithril_snapshot:v3.1.21 AS mithril-snapshot-ci +IMPORT github.com/input-output-hk/catalyst-ci/earthly/rust:feat/cardano-chain-follower-changes AS rust-ci #cspell: words rustfmt toolsets USERARCH @@ -69,18 +68,6 @@ package-cat-gateway: ENTRYPOINT ./entry.sh SAVE IMAGE cat-gateway:$tag -# package-cat-gateway : Create a deployable container for catalyst-gateway -# And bundle a Mithril snapshot of cardano preprod -nightly-package-cat-gateway-with-preprod: - ARG tag="latest" - - FROM +package-cat-gateway - - # copy preprod mithril snapshot to /tmp/preprod dir - COPY mithril-snapshot-ci+preprod/snapshot /tmp/preprod - - SAVE IMAGE cat-gateway:$tag - # Publish packages if all integration tests have passed. (Failure to pass tests will prevent packages being published.) # publish: # FROM scratch diff --git a/catalyst-gateway/bin/Cargo.toml b/catalyst-gateway/bin/Cargo.toml index 7f43df1124c..59ab78cfd6e 100644 --- a/catalyst-gateway/bin/Cargo.toml +++ b/catalyst-gateway/bin/Cargo.toml @@ -4,10 +4,10 @@ description = "The Catalyst Data Gateway" keywords = ["cardano", "catalyst", "gateway"] categories = ["command-line-utilities"] version = "0.1.0" -authors.workspace = true -edition.workspace = true -license.workspace = true -repository.workspace = true +authors.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -15,32 +15,62 @@ repository.workspace = true workspace = true [dependencies] -build-info.workspace = true -bb8 = { workspace = true } -bb8-postgres = { workspace = true } -tokio-postgres = { workspace = true, features = [ +cardano-chain-follower = { version = "0.0.2", git = "https://github.com/input-output-hk/catalyst-libs.git", branch = "main" } + +pallas = { version = "0.30.1", git = "https://github.com/input-output-hk/catalyst-pallas.git", rev = "9b5183c8b90b90fe2cc319d986e933e9518957b3" } +pallas-traverse = { version = "0.30.1", git = "https://github.com/input-output-hk/catalyst-pallas.git", rev = "9b5183c8b90b90fe2cc319d986e933e9518957b3" } +#pallas-crypto = { version = "0.30.1", git = "https://github.com/input-output-hk/catalyst-pallas.git", rev = "9b5183c8b90b90fe2cc319d986e933e9518957b3" } + +clap = { version = "4.5.17", features = ["derive", "env"] } +tracing = { version = "0.1.40", features = ["log"] } +tracing-subscriber = { version = "0.3.18", features = [ + "fmt", + "json", + "registry", + "std", + "time", + "env-filter", +] } +serde = { version = "1.0.204", features = ["derive"] } +serde_json = "1.0.128" +thiserror = "1.0.63" +chrono = "0.4.38" +async-trait = "0.1.82" +bb8 = "0.8.5" +bb8-postgres = "0.8.1" +tokio-postgres = { version = "0.7.11", features = [ "with-chrono-0_4", "with-serde_json-1", "with-time-0_3", ] } -clap = { workspace = true, features = ["derive", "env"] } -tracing = { workspace = true, features = ["log"] } -tracing-subscriber = { workspace = true, features = ["fmt", "json", "registry", "std", "time"] } -serde = { workspace = true, features = ["derive"] } -serde_json = { workspace = true } -tokio = { workspace = true, features = ["rt", "macros", "rt-multi-thread"] } -thiserror = { workspace = true } -rust_decimal = { workspace = true, features = [ +tokio = { version = "1.39.2", features = ["rt", "macros", "rt-multi-thread"] } +dotenvy = "0.15.7" +local-ip-address = "0.6.2" +gethostname = "0.5.0" +hex = "0.4.3" +handlebars = "6.0.0" +anyhow = "1.0.86" +cddl = "0.9.4" +ciborium = "0.2.2" +stringzilla = "3.9.3" +duration-string = "0.4.0" +build-info = "0.0.38" +ed25519-dalek = "2.1.1" +scylla = { version = "0.14.0", features = ["ssl", "full-serialization"] } +strum = { version = "0.26.3", features = ["derive"] } +strum_macros = "0.26.4" +openssl = { version = "0.10.66", features = ["vendored"] } +num-bigint = "0.4.6" +futures = "0.3.30" +rand = "0.8.5" +moka = { version = "0.12.8", features = ["future"] } +crossbeam-skiplist = "0.1.3" +rust_decimal = { version = "1.36.0", features = [ "serde-with-float", "db-tokio-postgres", ] } -chrono = { workspace = true } -poem = { workspace = true, features = [ - "embed", - "prometheus", - "compression", -] } -poem-openapi = { workspace = true, features = [ +poem = { version = "3.0.4", features = ["embed", "prometheus", "compression"] } +poem-openapi = { version = "5.0.3", features = [ "openapi-explorer", "rapidoc", "redoc", @@ -49,38 +79,17 @@ poem-openapi = { workspace = true, features = [ "url", "chrono", ] } -prometheus = { workspace = true } -cryptoxide = { workspace = true } -uuid = { workspace = true, features = ["v4", "serde"] } -url = { workspace = true } -dotenvy = { workspace = true } -panic-message = { workspace = true } -cpu-time = { workspace = true } -ulid = { workspace = true, features = ["serde", "uuid"] } -rust-embed = { workspace = true } -local-ip-address = { workspace = true } -gethostname = { workspace = true } -hex = { workspace = true } -pallas = { workspace = true } -cardano-chain-follower= { workspace = true } -anyhow = { workspace = true } -handlebars = { workspace = true } -cddl = { workspace = true } -ciborium = { workspace = true } -ed25519-dalek.workspace = true -stringzilla = { workspace = true } -duration-string.workspace = true -scylla.workspace = true -strum.workspace = true -strum_macros.workspace = true -openssl.workspace = true -num-bigint.workspace = true -futures.workspace = true -rand.workspace = true -moka.workspace = true -crossbeam-skiplist.workspace = true +uuid = { version = "1.10.0", features = ["v4", "serde"] } +ulid = { version = "1.1.3", features = ["serde", "uuid"] } +cryptoxide = "0.4.4" # TODO: For blake2b replace with blake2b_simd. +url = "2.5.2" +panic-message = "0.3.0" +cpu-time = "1.0.0" +prometheus = "0.13.4" +rust-embed = "8.5.0" +num-traits = "0.2.19" base64 = "0.22.1" dashmap = "6.0.1" [build-dependencies] -build-info-build = { workspace = true } +build-info-build = "0.0.38" \ No newline at end of file diff --git a/catalyst-gateway/bin/src/cardano/util.rs b/catalyst-gateway/bin/src/cardano/util.rs index 75a9f48e289..7a18ac3dce9 100644 --- a/catalyst-gateway/bin/src/cardano/util.rs +++ b/catalyst-gateway/bin/src/cardano/util.rs @@ -1,10 +1,7 @@ //! Block stream parsing and filtering utils - -use std::collections::HashMap; - use cryptoxide::{blake2b::Blake2b, digest::Digest}; use pallas::ledger::{ - primitives::conway::{StakeCredential, VKeyWitness}, + primitives::conway::StakeCredential, traverse::{Era, MultiEraAsset, MultiEraCert, MultiEraPolicyAssets}, }; use serde::Serialize; @@ -58,11 +55,9 @@ pub struct PolicyAsset { pub(crate) fn parse_policy_assets(assets: &[MultiEraPolicyAssets<'_>]) -> Vec { assets .iter() - .map(|asset| { - PolicyAsset { - policy_hash: asset.policy().to_string(), - assets: parse_child_assets(&asset.assets()), - } + .map(|asset| PolicyAsset { + policy_hash: asset.policy().to_string(), + assets: parse_child_assets(&asset.assets()), }) .collect() } @@ -71,25 +66,21 @@ pub(crate) fn parse_policy_assets(assets: &[MultiEraPolicyAssets<'_>]) -> Vec Vec { assets .iter() - .filter_map(|asset| { - match asset { - MultiEraAsset::AlonzoCompatibleOutput(id, name, amount) => { - Some(Asset { - policy_id: id.to_string(), - name: name.to_string(), - amount: *amount, - }) - }, - MultiEraAsset::AlonzoCompatibleMint(id, name, amount) => { - let amount = u64::try_from(*amount).ok()?; - Some(Asset { - policy_id: id.to_string(), - name: name.to_string(), - amount, - }) - }, - _ => Some(Asset::default()), - } + .filter_map(|asset| match asset { + MultiEraAsset::AlonzoCompatibleOutput(id, name, amount) => Some(Asset { + policy_id: id.to_string(), + name: name.to_string(), + amount: *amount, + }), + MultiEraAsset::AlonzoCompatibleMint(id, name, amount) => { + let amount = u64::try_from(*amount).ok()?; + Some(Asset { + policy_id: id.to_string(), + name: name.to_string(), + amount, + }) + }, + _ => Some(Asset::default()), }) .collect() } @@ -113,13 +104,11 @@ pub fn extract_stake_credentials_from_certs( pallas::ledger::primitives::alonzo::Certificate::StakeDelegation( stake_credential, _, - ) => { - match stake_credential { - StakeCredential::AddrKeyhash(stake_credential) => { - stake_credentials.push(hex::encode(stake_credential.as_slice())); - }, - StakeCredential::Scripthash(_) => (), - } + ) => match stake_credential { + StakeCredential::AddrKeyhash(stake_credential) => { + stake_credentials.push(hex::encode(stake_credential.as_slice())); + }, + StakeCredential::Scripthash(_) => (), }, _ => continue, } @@ -129,33 +118,6 @@ pub fn extract_stake_credentials_from_certs( stake_credentials } -/// Get a Blake2b-224 (28 byte) hash of some bytes -pub(crate) fn blake2b_224(value: &[u8]) -> [u8; 28] { - let mut digest = [0u8; 28]; - let mut context = Blake2b::new(28); - context.input(value); - context.result(&mut digest); - digest -} - -/// A map of hashed witnesses. -pub(crate) type HashedWitnesses = HashMap<[u8; 28], Vec>; - -/// Extract witness pub keys and pair with blake2b hash of the pub key. -/// This converts raw Addresses to their hashes as used on Cardano (Blake2b-224). -/// And allows them to be easily cross referenced. -pub(crate) fn extract_hashed_witnesses(witnesses: &[VKeyWitness]) -> HashedWitnesses { - let mut hashed_witnesses = HashMap::new(); - for witness in witnesses { - let pub_key = witness.vkey.to_vec(); - let hash = blake2b_224(&pub_key); - - hashed_witnesses.insert(hash, pub_key); - } - - hashed_witnesses -} - /// Match hashed witness pub keys with hashed stake credentials from the TX certificates /// to identify the correct stake credential key. #[allow(dead_code)] diff --git a/catalyst-gateway/bin/src/db/index/block.rs b/catalyst-gateway/bin/src/db/index/block.rs index 72b895a658b..69704dd6e49 100644 --- a/catalyst-gateway/bin/src/db/index/block.rs +++ b/catalyst-gateway/bin/src/db/index/block.rs @@ -7,7 +7,6 @@ use super::{ index_certs::CertInsertQuery, index_txi::TxiInsertQuery, index_txo::TxoInsertQuery, queries::FallibleQueryTasks, session::CassandraSession, }; -use crate::cardano::util::extract_hashed_witnesses; /// Convert a usize to an i16 and saturate at `i16::MAX` pub(crate) fn usize_to_i16(value: usize) -> i16 { @@ -35,9 +34,6 @@ pub(crate) async fn index_block(block: &MultiEraBlock) -> anyhow::Result<()> { let txn_hash = txs.hash().to_vec(); - // Hash all the witnesses for easy lookup. - let witnesses = extract_hashed_witnesses(txs.vkey_witnesses()); - // Index the TXIs. txi_index.index(txs, slot_no); @@ -47,7 +43,7 @@ pub(crate) async fn index_block(block: &MultiEraBlock) -> anyhow::Result<()> { // TODO: Index Metadata. // Index Certificates inside the transaction. - cert_index.index(txs, slot_no, txn, &witnesses); + cert_index.index(txs, slot_no, txn, block); // Index the TXOs. txo_index.index(txs, slot_no, &txn_hash, txn); diff --git a/catalyst-gateway/bin/src/db/index/index_certs.rs b/catalyst-gateway/bin/src/db/index/index_certs.rs index b39cf8fdace..1b2f06f821b 100644 --- a/catalyst-gateway/bin/src/db/index/index_certs.rs +++ b/catalyst-gateway/bin/src/db/index/index_certs.rs @@ -2,6 +2,7 @@ use std::sync::Arc; +use cardano_chain_follower::MultiEraBlock; use pallas::ledger::primitives::{alonzo, conway}; use scylla::{frame::value::MaybeUnset, SerializeRow, Session}; use tracing::error; @@ -10,7 +11,7 @@ use super::{ queries::{FallibleQueryTasks, PreparedQueries, PreparedQuery, SizedBatch}, session::CassandraSession, }; -use crate::{cardano::util::HashedWitnesses, settings::CassandraEnvVars}; +use crate::{service::utilities::convert::u16_from_saturating, settings::CassandraEnvVars}; /// Insert TXI Query and Parameters #[derive(SerializeRow)] @@ -121,12 +122,15 @@ impl CertInsertQuery { #[allow(clippy::too_many_arguments)] fn stake_address( &mut self, cred: &alonzo::StakeCredential, slot_no: u64, txn: i16, register: bool, - deregister: bool, delegation: Option>, witnesses: &HashedWitnesses, + deregister: bool, delegation: Option>, block: &MultiEraBlock, ) { let default_addr = Vec::new(); let (key_hash, pubkey, script) = match cred { pallas::ledger::primitives::conway::StakeCredential::AddrKeyhash(cred) => { - let addr = witnesses.get(cred.as_ref()).unwrap_or(&default_addr); + let addr = block + .witness_for_tx(cred, u16_from_saturating(txn)) + .unwrap_or(default_addr); + //let addr = witnesses.get(cred.as_ref()).unwrap_or(&default_addr); // Note: it is totally possible for the Registration Certificate to not be // witnessed. (cred.to_vec(), addr.clone(), false) @@ -158,27 +162,19 @@ impl CertInsertQuery { /// Index an Alonzo Era certificate into the database. fn index_alonzo_cert( - &mut self, cert: &alonzo::Certificate, slot_no: u64, txn: i16, witnesses: &HashedWitnesses, + &mut self, cert: &alonzo::Certificate, slot_no: u64, txn: i16, block: &MultiEraBlock, ) { #[allow(clippy::match_same_arms)] match cert { pallas::ledger::primitives::alonzo::Certificate::StakeRegistration(cred) => { // This may not be witnessed, its normal but disappointing. - self.stake_address(cred, slot_no, txn, true, false, None, witnesses); + self.stake_address(cred, slot_no, txn, true, false, None, block); }, pallas::ledger::primitives::alonzo::Certificate::StakeDeregistration(cred) => { - self.stake_address(cred, slot_no, txn, false, true, None, witnesses); + self.stake_address(cred, slot_no, txn, false, true, None, block); }, pallas::ledger::primitives::alonzo::Certificate::StakeDelegation(cred, pool) => { - self.stake_address( - cred, - slot_no, - txn, - false, - false, - Some(pool.to_vec()), - witnesses, - ); + self.stake_address(cred, slot_no, txn, false, false, Some(pool.to_vec()), block); }, pallas::ledger::primitives::alonzo::Certificate::PoolRegistration { .. } => {}, pallas::ledger::primitives::alonzo::Certificate::PoolRetirement(..) => {}, @@ -189,27 +185,19 @@ impl CertInsertQuery { /// Index a certificate from a conway transaction. fn index_conway_cert( - &mut self, cert: &conway::Certificate, slot_no: u64, txn: i16, witnesses: &HashedWitnesses, + &mut self, cert: &conway::Certificate, slot_no: u64, txn: i16, block: &MultiEraBlock, ) { #[allow(clippy::match_same_arms)] match cert { pallas::ledger::primitives::conway::Certificate::StakeRegistration(cred) => { // This may not be witnessed, its normal but disappointing. - self.stake_address(cred, slot_no, txn, true, false, None, witnesses); + self.stake_address(cred, slot_no, txn, true, false, None, block); }, pallas::ledger::primitives::conway::Certificate::StakeDeregistration(cred) => { - self.stake_address(cred, slot_no, txn, false, true, None, witnesses); + self.stake_address(cred, slot_no, txn, false, true, None, block); }, pallas::ledger::primitives::conway::Certificate::StakeDelegation(cred, pool) => { - self.stake_address( - cred, - slot_no, - txn, - false, - false, - Some(pool.to_vec()), - witnesses, - ); + self.stake_address(cred, slot_no, txn, false, false, Some(pool.to_vec()), block); }, pallas::ledger::primitives::conway::Certificate::PoolRegistration { .. } => {}, pallas::ledger::primitives::conway::Certificate::PoolRetirement(..) => {}, @@ -231,20 +219,18 @@ impl CertInsertQuery { /// Index the certificates in a transaction. pub(crate) fn index( &mut self, txs: &pallas::ledger::traverse::MultiEraTx<'_>, slot_no: u64, txn: i16, - witnesses: &HashedWitnesses, + block: &MultiEraBlock, ) { #[allow(clippy::match_same_arms)] - txs.certs().iter().for_each(|cert| { - match cert { - pallas::ledger::traverse::MultiEraCert::NotApplicable => {}, - pallas::ledger::traverse::MultiEraCert::AlonzoCompatible(cert) => { - self.index_alonzo_cert(cert, slot_no, txn, witnesses); - }, - pallas::ledger::traverse::MultiEraCert::Conway(cert) => { - self.index_conway_cert(cert, slot_no, txn, witnesses); - }, - _ => {}, - } + txs.certs().iter().for_each(|cert| match cert { + pallas::ledger::traverse::MultiEraCert::NotApplicable => {}, + pallas::ledger::traverse::MultiEraCert::AlonzoCompatible(cert) => { + self.index_alonzo_cert(cert, slot_no, txn, block); + }, + pallas::ledger::traverse::MultiEraCert::Conway(cert) => { + self.index_conway_cert(cert, slot_no, txn, block); + }, + _ => {}, }); } diff --git a/catalyst-gateway/bin/src/db/index/index_txi.rs b/catalyst-gateway/bin/src/db/index/index_txi.rs index 20d527cfce6..7680dafab5e 100644 --- a/catalyst-gateway/bin/src/db/index/index_txi.rs +++ b/catalyst-gateway/bin/src/db/index/index_txi.rs @@ -72,7 +72,7 @@ impl TxiInsertQuery { } /// Index the transaction Inputs. - pub(crate) fn index(&mut self, txs: &pallas::ledger::traverse::MultiEraTx<'_>, slot_no: u64) { + pub(crate) fn index(&mut self, txs: &pallas_traverse::MultiEraTx<'_>, slot_no: u64) { // Index the TXI's. for txi in txs.inputs() { let txn_hash = txi.hash().to_vec(); diff --git a/catalyst-gateway/bin/src/db/index/schema.rs b/catalyst-gateway/bin/src/db/index/schema.rs index f854fc7329a..01980d608b3 100644 --- a/catalyst-gateway/bin/src/db/index/schema.rs +++ b/catalyst-gateway/bin/src/db/index/schema.rs @@ -72,7 +72,7 @@ async fn create_namespace( // Create the Keyspace if it doesn't exist already. let stmt = session.prepare(query).await?; - session.execute(&stmt, ()).await?; + session.execute_unpaged(&stmt, ()).await?; // Wait for the Schema to be ready. session.await_schema_agreement().await?; @@ -98,7 +98,7 @@ pub(crate) async fn create_schema( .context(format!("{} : Prepared", schema.1))?; session - .execute(&stmt, ()) + .execute_unpaged(&stmt, ()) .await .context(format!("{} : Executed", schema.1))?; } diff --git a/catalyst-gateway/bin/src/service/utilities/convert.rs b/catalyst-gateway/bin/src/service/utilities/convert.rs new file mode 100644 index 00000000000..f5733f1360e --- /dev/null +++ b/catalyst-gateway/bin/src/service/utilities/convert.rs @@ -0,0 +1,94 @@ +//! Simple general purpose utility functions. + +/// Convert T to an i16. (saturate if out of range.) +#[allow(dead_code)] // Its OK if we don't use this general utility function. +pub(crate) fn i16_from_saturating>(value: T) -> i16 { + match value.try_into() { + Ok(value) => value, + Err(_) => i16::MAX, + } +} + +/// Convert an `` to `u16`. (saturate if out of range.) +#[allow(dead_code)] // Its OK if we don't use this general utility function. +pub(crate) fn u16_from_saturating< + T: Copy + + TryInto + + std::ops::Sub + + std::cmp::PartialOrd + + num_traits::identities::Zero, +>( + value: T, +) -> u16 { + if value < T::zero() { + u16::MIN + } else { + match value.try_into() { + Ok(value) => value, + Err(_) => u16::MAX, + } + } +} + +/// Convert an `` to `usize`. (saturate if out of range.) +#[allow(dead_code)] // Its OK if we don't use this general utility function. +pub(crate) fn usize_from_saturating< + T: Copy + + TryInto + + std::ops::Sub + + std::cmp::PartialOrd + + num_traits::identities::Zero, +>( + value: T, +) -> usize { + if value < T::zero() { + usize::MIN + } else { + match value.try_into() { + Ok(value) => value, + Err(_) => usize::MAX, + } + } +} + +/// Convert an `` to `u32`. (saturate if out of range.) +#[allow(dead_code)] // Its OK if we don't use this general utility function. +pub(crate) fn u32_from_saturating< + T: Copy + + TryInto + + std::ops::Sub + + std::cmp::PartialOrd + + num_traits::identities::Zero, +>( + value: T, +) -> u32 { + if value < T::zero() { + u32::MIN + } else { + match value.try_into() { + Ok(converted) => converted, + Err(_) => u32::MAX, + } + } +} + +/// Convert an `` to `u64`. (saturate if out of range.) +#[allow(dead_code)] // Its OK if we don't use this general utility function. +pub(crate) fn u64_from_saturating< + T: Copy + + TryInto + + std::ops::Sub + + std::cmp::PartialOrd + + num_traits::identities::Zero, +>( + value: T, +) -> u64 { + if value < T::zero() { + u64::MIN + } else { + match value.try_into() { + Ok(converted) => converted, + Err(_) => u64::MAX, + } + } +} diff --git a/catalyst-gateway/bin/src/service/utilities/mod.rs b/catalyst-gateway/bin/src/service/utilities/mod.rs index c19d7daccfc..796aca69224 100644 --- a/catalyst-gateway/bin/src/service/utilities/mod.rs +++ b/catalyst-gateway/bin/src/service/utilities/mod.rs @@ -1,5 +1,6 @@ //! `API` Utility operations pub(crate) mod catch_panic; +pub(crate) mod convert; pub(crate) mod middleware; pub(crate) mod net; diff --git a/catalyst-gateway/crates/README.md b/catalyst-gateway/crates/README.md deleted file mode 100644 index 5ee79b849de..00000000000 --- a/catalyst-gateway/crates/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# Catalyst Data Gateway - Crates - -These are fully re-usable generalized `rust` crates that the Catalyst Gateway uses and are developed with it. -They are also able to be used stand-alone in other projects and can be published separately. diff --git a/catalyst-gateway/tests/api_tests/Earthfile b/catalyst-gateway/tests/api_tests/Earthfile index 4cbe641d7bb..6612c2d4a5c 100644 --- a/catalyst-gateway/tests/api_tests/Earthfile +++ b/catalyst-gateway/tests/api_tests/Earthfile @@ -38,7 +38,7 @@ nightly-test: WITH DOCKER \ --compose docker-compose.yml \ --load event-db:latest=(../../event-db+build) \ - --load cat-gateway:latest=(../../+nightly-package-cat-gateway-with-preprod) \ + --load cat-gateway:latest=(../../+package-cat-gateway) \ --service cat-gateway \ --allow-privileged RUN poetry run pytest -s -m nightly --junitxml=junit-report.xml --cov=api_tests --cov-report lcov diff --git a/catalyst_voices/Earthfile b/catalyst_voices/Earthfile index 8117ddd554f..bb57578f266 100644 --- a/catalyst_voices/Earthfile +++ b/catalyst_voices/Earthfile @@ -1,7 +1,7 @@ VERSION 0.8 IMPORT ../catalyst-gateway AS catalyst-gateway -IMPORT github.com/input-output-hk/catalyst-ci/earthly/flutter:v3.1.21 AS flutter-ci +IMPORT github.com/input-output-hk/catalyst-ci/earthly/flutter:v3.1.26 AS flutter-ci # Copy all the necessary files and running bootstrap builder: @@ -52,14 +52,6 @@ check-package-publishing: FROM +builder DO flutter-ci+PUBLISH_DRY_RUN -# Build web version of Catalyst Voices without arguments. -# This is used for development purposes to validate build on PRs. -build-web-on-pr: - FROM +builder - - ARG WORKDIR=/frontend/catalyst_voices - DO flutter-ci+BUILD_WEB --TARGET=lib/configs/main_web.dart --WORKDIR=$WORKDIR - # Run unit tests test-unit: FROM +builder @@ -68,39 +60,29 @@ test-unit: # Build web version of Catalyst Voices build-web: FROM +builder - ARG SECRETS_ARE_AVAILABLE=false + ARG RUN_ON_PR=true ARG SENTRY_DSN - IF [$SECRETS_ARE_AVAILABLE] - ARG WORKDIR=/frontend/catalyst_voices - DO flutter-ci+BUILD_WEB --TARGET=lib/configs/main_web.dart --dart-define SENTRY_DSN=$SENTRY_DSN --WORKDIR=$WORKDIR - SAVE ARTIFACT web + ARG WORKDIR=/frontend/catalyst_voices + IF [ $RUN_ON_PR = true ] + DO flutter-ci+BUILD_WEB --WORKDIR=$WORKDIR --BUILD_MODE='--profile' --TARGET=lib/configs/main_web.dart ELSE - RUN echo "No secrets are available in Earthly yet." + DO flutter-ci+BUILD_WEB --WORKDIR=$WORKDIR --TARGET=lib/configs/main_web.dart --SENTRY_DSN=$SENTRY_DSN + SAVE ARTIFACT web END package: FROM nginx:alpine3.18 - ARG SECRETS_ARE_AVAILABLE=false ARG tag='latest' - IF [$SECRETS_ARE_AVAILABLE] - COPY +build-web/web /app - COPY ./nginx.conf /etc/nginx/nginx.conf - EXPOSE 80 + COPY +build-web/web /app + COPY ./nginx.conf /etc/nginx/nginx.conf + EXPOSE 80 - SAVE IMAGE voices-frontend:$tag - ELSE - RUN echo "No secrets are available in Earthly yet." - END + SAVE IMAGE voices-frontend:$tag -#publish: -# FROM +package -# ARG SECRETS_ARE_AVAILABLE=false -# ARG tag='latest' +publish: + FROM +package + ARG tag='latest' -# IF [$SECRETS_ARE_AVAILABLE] -# SAVE IMAGE voices-frontend:$tag -# ELSE -# RUN echo "No secrets are available in Earthly yet." -# END \ No newline at end of file + SAVE IMAGE voices-frontend:$tag \ No newline at end of file diff --git a/catalyst_voices/README.md b/catalyst_voices/README.md index 784ed608ea9..82ee43c3326 100644 --- a/catalyst_voices/README.md +++ b/catalyst_voices/README.md @@ -16,8 +16,8 @@ This repository contains the Catalyst Voices app and packages. ## Requirements -* flutter: 3.22.1+ -* Dart: 3.3.4+ +* flutter: 3.24.1+ +* Dart: 3.5.0+ * Ruby: 2.5+ * Xcode: 15.0+ * Android Studio: Android Studio Electric Eel | 2022.1.1 + diff --git a/catalyst_voices/analysis_options.yaml b/catalyst_voices/analysis_options.yaml index d5015346bf1..eb691624826 100644 --- a/catalyst_voices/analysis_options.yaml +++ b/catalyst_voices/analysis_options.yaml @@ -1,4 +1,4 @@ -include: package:catalyst_analysis/analysis_options.1.0.0.yaml +include: package:catalyst_analysis/analysis_options.yaml analyzer: exclude: [ diff --git a/catalyst_voices/ios/Podfile.lock b/catalyst_voices/ios/Podfile.lock index 3fccb576522..3b33371d558 100644 --- a/catalyst_voices/ios/Podfile.lock +++ b/catalyst_voices/ios/Podfile.lock @@ -4,9 +4,16 @@ PODS: - Flutter - integration_test (0.0.1): - Flutter + - package_info_plus (0.4.5): + - Flutter - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS + - Sentry/HybridSDK (8.35.1) + - sentry_flutter (8.8.0): + - Flutter + - FlutterMacOS + - Sentry/HybridSDK (= 8.35.1) - url_launcher_ios (0.0.1): - Flutter @@ -14,9 +21,15 @@ DEPENDENCIES: - Flutter (from `Flutter`) - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) - integration_test (from `.symlinks/plugins/integration_test/ios`) + - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) + - sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) +SPEC REPOS: + trunk: + - Sentry + EXTERNAL SOURCES: Flutter: :path: Flutter @@ -24,17 +37,24 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/flutter_secure_storage/ios" integration_test: :path: ".symlinks/plugins/integration_test/ios" + package_info_plus: + :path: ".symlinks/plugins/package_info_plus/ios" path_provider_foundation: :path: ".symlinks/plugins/path_provider_foundation/darwin" + sentry_flutter: + :path: ".symlinks/plugins/sentry_flutter/ios" url_launcher_ios: :path: ".symlinks/plugins/url_launcher_ios/ios" SPEC CHECKSUMS: Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be - integration_test: ce0a3ffa1de96d1a89ca0ac26fca7ea18a749ef4 - path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c - url_launcher_ios: 6116280ddcfe98ab8820085d8d76ae7449447586 + flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12 + integration_test: 252f60fa39af5e17c3aa9899d35d908a0721b573 + package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 + Sentry: 1fe34e9c2cbba1e347623610d26db121dcb569f1 + sentry_flutter: a39c2a2d67d5e5b9cb0b94a4985c76dd5b3fc737 + url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe PODFILE CHECKSUM: ff9ae414ffbc80ad6f9d2058e299051a15f6eca7 diff --git a/catalyst_voices/ios/Runner/AppDelegate.swift b/catalyst_voices/ios/Runner/AppDelegate.swift index 70693e4a8c1..b6363034812 100644 --- a/catalyst_voices/ios/Runner/AppDelegate.swift +++ b/catalyst_voices/ios/Runner/AppDelegate.swift @@ -1,7 +1,7 @@ import UIKit import Flutter -@UIApplicationMain +@main @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, diff --git a/catalyst_voices/lib/app/app.dart b/catalyst_voices/lib/app/app.dart index f23ab3c8645..f3b0b10ca74 100644 --- a/catalyst_voices/lib/app/app.dart +++ b/catalyst_voices/lib/app/app.dart @@ -1 +1,2 @@ export 'view/app.dart'; +export 'view/app_content.dart'; diff --git a/catalyst_voices/lib/app/view/app.dart b/catalyst_voices/lib/app/view/app.dart index 88ed5d37259..ccc71424b5a 100644 --- a/catalyst_voices/lib/app/view/app.dart +++ b/catalyst_voices/lib/app/view/app.dart @@ -1,2 +1,41 @@ -export 'app_content.dart'; -export 'app_page.dart'; +import 'package:catalyst_voices/app/view/app_content.dart'; +import 'package:catalyst_voices/dependency/dependencies.dart'; +import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +final class App extends StatelessWidget { + final RouterConfig routerConfig; + + const App({ + super.key, + required this.routerConfig, + }); + + @override + Widget build(BuildContext context) { + return MultiBlocProvider( + providers: _multiBlocProviders(), + child: AppContent( + routerConfig: routerConfig, + ), + ); + } + + List _multiBlocProviders() { + return [ + BlocProvider( + create: (_) => Dependencies.instance.get(), + ), + BlocProvider( + create: (_) => Dependencies.instance.get(), + ), + BlocProvider( + create: (_) => Dependencies.instance.get(), + ), + BlocProvider( + create: (_) => Dependencies.instance.get(), + ), + ]; + } +} diff --git a/catalyst_voices/lib/app/view/app_content.dart b/catalyst_voices/lib/app/view/app_content.dart index 1d3a12874cc..8884915404a 100644 --- a/catalyst_voices/lib/app/view/app_content.dart +++ b/catalyst_voices/lib/app/view/app_content.dart @@ -1,16 +1,18 @@ -import 'package:catalyst_voices/routes/routes.dart' show AppRouter; -import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; +import 'package:catalyst_voices/app/view/app_precache_image_assets.dart'; import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_localized_locales/flutter_localized_locales.dart'; -import 'package:go_router/go_router.dart'; const _restorationScopeId = 'rootVoices'; final class AppContent extends StatelessWidget { - const AppContent({super.key}); + final RouterConfig routerConfig; + + const AppContent({ + super.key, + required this.routerConfig, + }); List> get _localizationsDelegates { return const [ @@ -21,23 +23,19 @@ final class AppContent extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocListener( - listener: (context, state) {}, - child: MaterialApp.router( - restorationScopeId: _restorationScopeId, - localizationsDelegates: _localizationsDelegates, - supportedLocales: VoicesLocalizations.supportedLocales, - localeListResolutionCallback: basicLocaleListResolution, - routerConfig: _routeConfig(context), - theme: ThemeBuilder.buildTheme(BrandKey.catalyst), - darkTheme: ThemeBuilder.buildDarkTheme(BrandKey.catalyst), - ), - ); - } - - GoRouter _routeConfig(BuildContext context) { - return AppRouter.init( - authenticationBloc: context.read(), + return MaterialApp.router( + restorationScopeId: _restorationScopeId, + localizationsDelegates: _localizationsDelegates, + supportedLocales: VoicesLocalizations.supportedLocales, + localeListResolutionCallback: basicLocaleListResolution, + routerConfig: routerConfig, + theme: ThemeBuilder.buildTheme(BrandKey.catalyst), + darkTheme: ThemeBuilder.buildDarkTheme(BrandKey.catalyst), + builder: (context, child) { + return GlobalPrecacheImages( + child: child ?? SizedBox.shrink(), + ); + }, ); } } diff --git a/catalyst_voices/lib/app/view/app_page.dart b/catalyst_voices/lib/app/view/app_page.dart deleted file mode 100644 index 503bdfe6129..00000000000 --- a/catalyst_voices/lib/app/view/app_page.dart +++ /dev/null @@ -1,62 +0,0 @@ -// ignore_for_file: discarded_futures - -import 'package:catalyst_voices/app/view/app_content.dart'; -import 'package:catalyst_voices/dependency/dependencies.dart'; -import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; - -final class App extends StatefulWidget { - const App({super.key}); - - @override - State createState() => _AppState(); -} - -final class _AppState extends State { - late final Future _initFuture; - - @override - Widget build(BuildContext context) { - return FutureBuilder( - future: _initFuture, - builder: (context, snapshot) { - return MultiBlocProvider( - providers: _multiBlocProviders(), - child: const AppContent(), - ); - }, - ); - } - - @override - void initState() { - super.initState(); - _initFuture = _init(); - } - - Future _init() async { - try { - await Dependencies.instance.init(); - } catch (error, stackTrace) { - // TODO(dtscalac): FutureBuilder that uses this future silences all - // errors, replace it here with proper logging solution. - FlutterError.dumpErrorToConsole( - FlutterErrorDetails(exception: error, stack: stackTrace), - ); - - rethrow; - } - } - - List _multiBlocProviders() { - return [ - BlocProvider( - create: (_) => Dependencies.instance.get(), - ), - BlocProvider( - create: (_) => Dependencies.instance.get(), - ), - ]; - } -} diff --git a/catalyst_voices/lib/app/view/app_precache_image_assets.dart b/catalyst_voices/lib/app/view/app_precache_image_assets.dart new file mode 100644 index 00000000000..2a0b4b9d587 --- /dev/null +++ b/catalyst_voices/lib/app/view/app_precache_image_assets.dart @@ -0,0 +1,158 @@ +import 'package:catalyst_voices/widgets/indicators/voices_circular_progress_indicator.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +class GlobalPrecacheImages extends StatelessWidget { + final Widget child; + + const GlobalPrecacheImages({ + super.key, + required this.child, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + return AppPrecacheImageAssets( + svgs: [ + theme.brandAssets.logo, + theme.brandAssets.logoIcon, + ], + assets: [ + VoicesAssets.images.comingSoonBkg, + ], + child: child, + ); + } +} + +/// A widget that pre-caches SVG and image assets before displaying its +/// child widget. +/// +/// This widget is useful for improving the perceived performance of your +/// app by pre-loading any necessary image assets before they are displayed on +/// the screen. +/// This can help to avoid stuttering or delays when the user navigates to a +/// new screen that requires those images. +/// +/// [AppPrecacheImageAssets] depends on [Theme] and is trying to make +/// as little work as possible when [Theme] is changing by caching +/// previously loaded assets. +class AppPrecacheImageAssets extends StatefulWidget { + /// List of [SvgGenImage] which should be cached. + final List svgs; + + /// List of [AssetGenImage] which should be cached. + final List assets; + + /// The child widget to be displayed once the images have been pre-cached. + final Widget child; + + const AppPrecacheImageAssets({ + super.key, + this.svgs = const [], + this.assets = const [], + required this.child, + }); + + @override + State createState() => _AppPrecacheImageAssetsState(); +} + +class _AppPrecacheImageAssetsState extends State { + final _svgs = []; + final _assets = []; + + bool _hadImages = false; + + late Future _cacheFuture; + + @override + void didUpdateWidget(covariant AppPrecacheImageAssets oldWidget) { + super.didUpdateWidget(oldWidget); + + if (_areImagesDifferent()) { + _updateImagesCache(); + } + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + + // Caching depends on context. Rebuild when changes. + _updateImagesCache(); + } + + @override + Widget build(BuildContext context) { + return FutureBuilder( + future: _cacheFuture, + builder: (context, snapshot) { + return switch (snapshot.connectionState) { + // Only blocking when does did not have images eg. First run. + // Do not block when theme mode is changing because it will + // cause blinking. + ConnectionState.active || + ConnectionState.waiting when !_hadImages => + _ProgressIndicator(), + ConnectionState.none || + ConnectionState.waiting || + ConnectionState.active || + ConnectionState.done => + widget.child, + }; + }, + ); + } + + void _updateImagesCache() { + _hadImages = _svgs.isNotEmpty || _assets.isNotEmpty; + + _svgs + ..clear() + ..addAll(widget.svgs); + _assets + ..clear() + ..addAll(widget.assets); + + _cacheFuture = _buildCacheFuture(svgs: _svgs, assets: _assets); + } + + bool _areImagesDifferent() { + final old = [ + ..._svgs.map((e) => e.path), + ..._assets.map((e) => e.path), + ]; + final current = [ + ...widget.svgs.map((e) => e.path), + ...widget.assets.map((e) => e.path), + ]; + + return !listEquals(old, current); + } + + Future _buildCacheFuture({ + List svgs = const [], + List assets = const [], + }) { + final futures = >[ + ...svgs.map((e) => e.cache(context: context)), + ...assets.map((e) => e.cache(context: context)), + ]; + + return Future.wait(futures); + } +} + +class _ProgressIndicator extends StatelessWidget { + const _ProgressIndicator(); + + @override + Widget build(BuildContext context) { + return Center(child: VoicesCircularProgressIndicator()); + } +} diff --git a/catalyst_voices/lib/common/formatters/date_formatter.dart b/catalyst_voices/lib/common/formatters/date_formatter.dart new file mode 100644 index 00000000000..51180beadb8 --- /dev/null +++ b/catalyst_voices/lib/common/formatters/date_formatter.dart @@ -0,0 +1,30 @@ +import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; +import 'package:catalyst_voices_shared/src/utils/date_time_ext.dart'; +import 'package:intl/intl.dart'; + +/// A [DateTime] formatter. +abstract class DateFormatter { + /// Formats recent dates like: + /// - Today + /// - Tomorrow + /// - Yesterday + /// - 2 days ago + /// - Other cases: yMMMMd date format. + static String formatRecentDate(VoicesLocalizations l10n, DateTime dateTime) { + final now = DateTimeExt.now(); + + final today = DateTime(now.year, now.month, now.day, 12); + if (dateTime.isSameDateAs(today)) return l10n.today; + + final tomorrow = today.plusDays(1); + if (dateTime.isSameDateAs(tomorrow)) return l10n.tomorrow; + + final yesterday = today.minusDays(1); + if (dateTime.isSameDateAs(yesterday)) return l10n.yesterday; + + final twoDaysAgo = today.minusDays(2); + if (dateTime.isSameDateAs(twoDaysAgo)) return l10n.twoDaysAgo; + + return DateFormat.yMMMMd().format(dateTime); + } +} diff --git a/catalyst_voices/lib/configs/bootstrap.dart b/catalyst_voices/lib/configs/bootstrap.dart index 57611bb41ca..dbf2dbbf1ad 100644 --- a/catalyst_voices/lib/configs/bootstrap.dart +++ b/catalyst_voices/lib/configs/bootstrap.dart @@ -1,15 +1,35 @@ import 'dart:async'; import 'dart:developer'; +import 'package:catalyst_voices/app/app.dart'; import 'package:catalyst_voices/configs/app_bloc_observer.dart'; import 'package:catalyst_voices/configs/sentry_service.dart'; +import 'package:catalyst_voices/dependency/dependencies.dart'; +import 'package:catalyst_voices/routes/guards/milestone_guard.dart'; +import 'package:catalyst_voices/routes/routes.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import 'package:url_strategy/url_strategy.dart'; -Future bootstrap(FutureOr Function() builder) async { +typedef BootstrapWidgetBuilder = FutureOr Function(BootstrapArgs args); + +final class BootstrapArgs { + final RouterConfig routerConfig; + + BootstrapArgs({ + required this.routerConfig, + }); +} + +// TODO(damian-molinski): Add PlatformDispatcher.instance.onError +// TODO(damian-molinski): Add Isolate.current.addErrorListener +// TODO(damian-molinski): Add runZonedGuarded +// TODO(damian-molinski): Add Global try-catch +Future bootstrap([ + BootstrapWidgetBuilder builder = _appBuilder, +]) async { // There's no need to call WidgetsFlutterBinding.ensureInitialized() // since this is already done internally by SentryFlutter.init() // More info here: https://github.com/getsentry/sentry-dart/issues/2063 @@ -17,9 +37,6 @@ Future bootstrap(FutureOr Function() builder) async { WidgetsFlutterBinding.ensureInitialized(); } - GoRouter.optionURLReflectsImperativeAPIs = true; - setPathUrlStrategy(); - FlutterError.onError = (details) { log( details.exceptionAsString(), @@ -27,9 +44,22 @@ Future bootstrap(FutureOr Function() builder) async { ); }; + GoRouter.optionURLReflectsImperativeAPIs = true; + setPathUrlStrategy(); + + final router = AppRouter.init( + guards: const [ + MilestoneGuard(), + ], + ); + Bloc.observer = AppBlocObserver(); - await _runApp(await builder()); + await Dependencies.instance.init(); + + final args = BootstrapArgs(routerConfig: router); + + await _runApp(await builder(args)); } Future _runApp(Widget app) async { @@ -39,3 +69,9 @@ Future _runApp(Widget app) async { runApp(app); } } + +Widget _appBuilder(BootstrapArgs args) { + return App( + routerConfig: args.routerConfig, + ); +} diff --git a/catalyst_voices/lib/configs/main_dev.dart b/catalyst_voices/lib/configs/main_dev.dart index b169becdb68..f24ad0bd1c3 100644 --- a/catalyst_voices/lib/configs/main_dev.dart +++ b/catalyst_voices/lib/configs/main_dev.dart @@ -1,6 +1,5 @@ -import 'package:catalyst_voices/app/app.dart'; import 'package:catalyst_voices/configs/bootstrap.dart'; void main() async { - await bootstrap(() => const App()); + await bootstrap(); } diff --git a/catalyst_voices/lib/configs/main_preprod.dart b/catalyst_voices/lib/configs/main_preprod.dart index b169becdb68..f24ad0bd1c3 100644 --- a/catalyst_voices/lib/configs/main_preprod.dart +++ b/catalyst_voices/lib/configs/main_preprod.dart @@ -1,6 +1,5 @@ -import 'package:catalyst_voices/app/app.dart'; import 'package:catalyst_voices/configs/bootstrap.dart'; void main() async { - await bootstrap(() => const App()); + await bootstrap(); } diff --git a/catalyst_voices/lib/configs/main_prod.dart b/catalyst_voices/lib/configs/main_prod.dart index b169becdb68..f24ad0bd1c3 100644 --- a/catalyst_voices/lib/configs/main_prod.dart +++ b/catalyst_voices/lib/configs/main_prod.dart @@ -1,6 +1,5 @@ -import 'package:catalyst_voices/app/app.dart'; import 'package:catalyst_voices/configs/bootstrap.dart'; void main() async { - await bootstrap(() => const App()); + await bootstrap(); } diff --git a/catalyst_voices/lib/configs/main_qa.dart b/catalyst_voices/lib/configs/main_qa.dart index b169becdb68..f24ad0bd1c3 100644 --- a/catalyst_voices/lib/configs/main_qa.dart +++ b/catalyst_voices/lib/configs/main_qa.dart @@ -1,6 +1,5 @@ -import 'package:catalyst_voices/app/app.dart'; import 'package:catalyst_voices/configs/bootstrap.dart'; void main() async { - await bootstrap(() => const App()); + await bootstrap(); } diff --git a/catalyst_voices/lib/configs/main_web.dart b/catalyst_voices/lib/configs/main_web.dart index b169becdb68..f24ad0bd1c3 100644 --- a/catalyst_voices/lib/configs/main_web.dart +++ b/catalyst_voices/lib/configs/main_web.dart @@ -1,6 +1,5 @@ -import 'package:catalyst_voices/app/app.dart'; import 'package:catalyst_voices/configs/bootstrap.dart'; void main() async { - await bootstrap(() => const App()); + await bootstrap(); } diff --git a/catalyst_voices/lib/dependency/dependencies.dart b/catalyst_voices/lib/dependency/dependencies.dart index c49ae081733..2fb658298d7 100644 --- a/catalyst_voices/lib/dependency/dependencies.dart +++ b/catalyst_voices/lib/dependency/dependencies.dart @@ -26,7 +26,9 @@ final class Dependencies extends DependencyProvider { () => LoginBloc( authenticationRepository: get(), ), - ); + ) + ..registerLazySingleton(() => SessionBloc()) + ..registerLazySingleton(() => UserProfileBloc()); } void _registerRepositories() { diff --git a/catalyst_voices/lib/pages/coming_soon/coming_soon.dart b/catalyst_voices/lib/pages/coming_soon/coming_soon.dart index 7c4b28710d8..5fe21ea1268 100644 --- a/catalyst_voices/lib/pages/coming_soon/coming_soon.dart +++ b/catalyst_voices/lib/pages/coming_soon/coming_soon.dart @@ -1,53 +1 @@ -import 'package:catalyst_voices/pages/coming_soon/description.dart'; -import 'package:catalyst_voices/pages/coming_soon/logo.dart'; -import 'package:catalyst_voices/pages/coming_soon/title.dart'; -import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; -import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; -import 'package:flutter/material.dart'; - -final class ComingSoonPage extends StatelessWidget { - static const comingSoonPageKey = Key('ComingSoon'); - - const ComingSoonPage({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - key: comingSoonPageKey, - body: Container( - constraints: const BoxConstraints.expand(), - decoration: BoxDecoration( - image: DecorationImage( - image: CatalystImage.asset( - VoicesAssets.images.comingSoonBkg.path, - ).image, - fit: BoxFit.cover, - ), - ), - child: ResponsivePadding( - xs: const EdgeInsets.only(left: 17), - sm: const EdgeInsets.only(left: 119), - md: const EdgeInsets.only(left: 150), - lg: const EdgeInsets.only(left: 150), - other: const EdgeInsets.only(left: 150), - child: Align( - alignment: Alignment.centerLeft, - child: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 356), - child: const Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ComingSoonLogo(), - ComingSoonTitle(), - ComingSoonDescription(), - ], - ), - ), - ), - ), - ), - ); - } -} +export 'coming_soon_page.dart'; diff --git a/catalyst_voices/lib/pages/coming_soon/coming_soon_page.dart b/catalyst_voices/lib/pages/coming_soon/coming_soon_page.dart new file mode 100644 index 00000000000..7c4b28710d8 --- /dev/null +++ b/catalyst_voices/lib/pages/coming_soon/coming_soon_page.dart @@ -0,0 +1,53 @@ +import 'package:catalyst_voices/pages/coming_soon/description.dart'; +import 'package:catalyst_voices/pages/coming_soon/logo.dart'; +import 'package:catalyst_voices/pages/coming_soon/title.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; +import 'package:flutter/material.dart'; + +final class ComingSoonPage extends StatelessWidget { + static const comingSoonPageKey = Key('ComingSoon'); + + const ComingSoonPage({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + key: comingSoonPageKey, + body: Container( + constraints: const BoxConstraints.expand(), + decoration: BoxDecoration( + image: DecorationImage( + image: CatalystImage.asset( + VoicesAssets.images.comingSoonBkg.path, + ).image, + fit: BoxFit.cover, + ), + ), + child: ResponsivePadding( + xs: const EdgeInsets.only(left: 17), + sm: const EdgeInsets.only(left: 119), + md: const EdgeInsets.only(left: 150), + lg: const EdgeInsets.only(left: 150), + other: const EdgeInsets.only(left: 150), + child: Align( + alignment: Alignment.centerLeft, + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 356), + child: const Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ComingSoonLogo(), + ComingSoonTitle(), + ComingSoonDescription(), + ], + ), + ), + ), + ), + ), + ); + } +} diff --git a/catalyst_voices/lib/pages/discovery/discovery.dart b/catalyst_voices/lib/pages/discovery/discovery.dart new file mode 100644 index 00000000000..b2b5ff6a939 --- /dev/null +++ b/catalyst_voices/lib/pages/discovery/discovery.dart @@ -0,0 +1 @@ +export 'discovery_page.dart'; diff --git a/catalyst_voices/lib/pages/discovery/discovery_page.dart b/catalyst_voices/lib/pages/discovery/discovery_page.dart new file mode 100644 index 00000000000..3a0330dcc9f --- /dev/null +++ b/catalyst_voices/lib/pages/discovery/discovery_page.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; + +class DiscoveryPage extends StatelessWidget { + const DiscoveryPage({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return Container( + color: Colors.green, + alignment: Alignment.center, + child: Text( + 'Discovery', + style: Theme.of(context).textTheme.titleLarge, + ), + ); + } +} diff --git a/catalyst_voices/lib/pages/funded_projects/funded_projects.dart b/catalyst_voices/lib/pages/funded_projects/funded_projects.dart new file mode 100644 index 00000000000..df525e25373 --- /dev/null +++ b/catalyst_voices/lib/pages/funded_projects/funded_projects.dart @@ -0,0 +1 @@ +export 'funded_projects_page.dart'; diff --git a/catalyst_voices/lib/pages/funded_projects/funded_projects_page.dart b/catalyst_voices/lib/pages/funded_projects/funded_projects_page.dart new file mode 100644 index 00000000000..e4456300844 --- /dev/null +++ b/catalyst_voices/lib/pages/funded_projects/funded_projects_page.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; + +class FundedProjectsPage extends StatelessWidget { + const FundedProjectsPage({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + color: Colors.deepOrangeAccent, + alignment: Alignment.center, + child: Text( + 'Funded Projects', + style: Theme.of(context).textTheme.titleLarge, + ), + ); + } +} diff --git a/catalyst_voices/lib/pages/home/home_page.dart b/catalyst_voices/lib/pages/home/home_page.dart deleted file mode 100644 index ebaec830fca..00000000000 --- a/catalyst_voices/lib/pages/home/home_page.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; -import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; -import 'package:flutter/material.dart'; - -final class HomePage extends StatelessWidget { - static const homePageKey = Key('HomePage'); - - const HomePage({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - key: homePageKey, - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - context.l10n.homeScreenText, - style: const TextStyle( - color: VoicesColors.lightPrimary, - fontFamily: VoicesFonts.sFPro, - fontSize: 32, - ), - ), - const SizedBox(height: 20), - SizedBox( - height: 200, - width: 200, - child: CatalystImage.asset( - VoicesAssets.images.dummyCatalystVoices.path, - ), - ), - ], - ), - ), - ); - } -} diff --git a/catalyst_voices/lib/pages/spaces/spaces.dart b/catalyst_voices/lib/pages/spaces/spaces.dart new file mode 100644 index 00000000000..89fd40fe7bd --- /dev/null +++ b/catalyst_voices/lib/pages/spaces/spaces.dart @@ -0,0 +1 @@ +export 'spaces_shell_page.dart'; diff --git a/catalyst_voices/lib/pages/spaces/spaces_shell_page.dart b/catalyst_voices/lib/pages/spaces/spaces_shell_page.dart new file mode 100644 index 00000000000..aaf098869e7 --- /dev/null +++ b/catalyst_voices/lib/pages/spaces/spaces_shell_page.dart @@ -0,0 +1,64 @@ +import 'package:catalyst_voices/routes/routes.dart'; +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:flutter/material.dart'; + +class SpacesShellPage extends StatelessWidget { + final Space space; + final Widget child; + + const SpacesShellPage({ + super.key, + required this.space, + required this.child, + }); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: VoicesAppBar(), + drawer: _SpacesDrawer(space: space), + body: child, + ); + } +} + +class _SpacesDrawer extends StatelessWidget { + final Space space; + + const _SpacesDrawer({ + required this.space, + }); + + @override + Widget build(BuildContext context) { + return VoicesDrawer( + children: [], + bottom: VoicesDrawerSpaceChooser( + currentSpace: space, + onChanged: (space) { + Scaffold.of(context).closeDrawer(); + _goTo(context, space: space); + }, + ), + ); + } + + void _goTo( + BuildContext context, { + required Space space, + }) { + switch (space) { + case Space.treasury: + TreasuryRoute().go(context); + case Space.discovery: + DiscoveryRoute().go(context); + case Space.workspace: + WorkspaceRoute().go(context); + case Space.voting: + VotingRoute().go(context); + case Space.fundedProjects: + FundedProjectsRoute().go(context); + } + } +} diff --git a/catalyst_voices/lib/pages/treasury/treasury.dart b/catalyst_voices/lib/pages/treasury/treasury.dart new file mode 100644 index 00000000000..47c4b4927f0 --- /dev/null +++ b/catalyst_voices/lib/pages/treasury/treasury.dart @@ -0,0 +1 @@ +export 'treasury_page.dart'; diff --git a/catalyst_voices/lib/pages/treasury/treasury_page.dart b/catalyst_voices/lib/pages/treasury/treasury_page.dart new file mode 100644 index 00000000000..d8684e7a0fb --- /dev/null +++ b/catalyst_voices/lib/pages/treasury/treasury_page.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; + +class TreasuryPage extends StatelessWidget { + const TreasuryPage({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return Container( + color: Colors.yellow, + alignment: Alignment.center, + child: Text( + 'Treasury', + style: Theme.of(context).textTheme.titleLarge, + ), + ); + } +} diff --git a/catalyst_voices/lib/pages/voting/voting.dart b/catalyst_voices/lib/pages/voting/voting.dart new file mode 100644 index 00000000000..930ecee5118 --- /dev/null +++ b/catalyst_voices/lib/pages/voting/voting.dart @@ -0,0 +1 @@ +export 'voting_page.dart'; diff --git a/catalyst_voices/lib/pages/voting/voting_page.dart b/catalyst_voices/lib/pages/voting/voting_page.dart new file mode 100644 index 00000000000..e9e68669de2 --- /dev/null +++ b/catalyst_voices/lib/pages/voting/voting_page.dart @@ -0,0 +1,188 @@ +import 'package:catalyst_cardano_serialization/catalyst_cardano_serialization.dart'; +import 'package:catalyst_voices/widgets/cards/pending_proposal_card.dart'; +import 'package:catalyst_voices/widgets/common/tab_bar_stack_view.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; +import 'package:flutter/material.dart'; + +final _proposalDescription = """ +Zanzibar is becoming one of the hotspots for DID's through +World Mobile and PRISM, but its potential is only barely exploited. +Zanzibar is becoming one of the hotspots for DID's through World Mobile +and PRISM, but its potential is only barely exploited. +""" + .replaceAll('\n', ' '); + +final _proposals = [ + PendingProposal( + id: 'f14/0', + fund: 'F14', + category: 'Cardano Use Cases / MVP', + title: 'Proposal Title that rocks the world', + lastUpdateDate: DateTime.now().minusDays(2), + fundsRequested: Coin.fromAda(100000), + commentsCount: 0, + description: _proposalDescription, + completedSegments: 0, + totalSegments: 13, + ), + PendingProposal( + id: 'f14/1', + fund: 'F14', + category: 'Cardano Use Cases / MVP', + title: 'Proposal Title that rocks the world', + lastUpdateDate: DateTime.now().minusDays(2), + fundsRequested: Coin.fromAda(100000), + commentsCount: 0, + description: _proposalDescription, + completedSegments: 7, + totalSegments: 13, + ), + PendingProposal( + id: 'f14/2', + fund: 'F14', + category: 'Cardano Use Cases / MVP', + title: 'Proposal Title that rocks the world', + lastUpdateDate: DateTime.now().minusDays(2), + fundsRequested: Coin.fromAda(100000), + commentsCount: 0, + description: _proposalDescription, + completedSegments: 13, + totalSegments: 13, + ), +]; + +final _favoriteProposals = ValueNotifier>([]); + +class VotingPage extends StatelessWidget { + const VotingPage({super.key}); + + @override + Widget build(BuildContext context) { + return ListView( + padding: const EdgeInsets.symmetric(horizontal: 32), + children: [ + const SizedBox(height: 44), + Text( + context.l10n.activeVotingRound, + style: Theme.of(context).textTheme.headlineLarge, + ), + const SizedBox(height: 44), + _Tabs(), + ], + ); + } +} + +class _Tabs extends StatelessWidget { + const _Tabs(); + + @override + Widget build(BuildContext context) { + return DefaultTabController( + length: 2, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TabBar( + isScrollable: true, + tabAlignment: TabAlignment.start, + tabs: [ + Tab( + text: context.l10n.noOfAllProposals(_proposals.length), + ), + Tab( + child: Row( + children: [ + Icon(CatalystVoicesIcons.star), + const SizedBox(width: 8), + Text(context.l10n.favorites), + ], + ), + ), + ], + ), + const SizedBox(height: 24), + TabBarStackView( + children: [ + _AllProposals(), + _FavoriteProposals(), + ], + ), + const SizedBox(height: 12), + ], + ), + ); + } +} + +class _AllProposals extends StatelessWidget { + const _AllProposals(); + + @override + Widget build(BuildContext context) { + return ValueListenableBuilder>( + valueListenable: _favoriteProposals, + builder: (context, favoriteProposals, child) { + return Wrap( + spacing: 16, + runSpacing: 16, + children: [ + for (final (index, proposal) in _proposals.indexed) + PendingProposalCard( + image: index.isEven + ? VoicesAssets.images.proposalBackground1 + : VoicesAssets.images.proposalBackground2, + proposal: proposal, + isFavorite: favoriteProposals.contains(proposal), + onFavoriteChanged: (isFavorite) => + _onFavoriteChanged(proposal, isFavorite), + ), + ], + ); + }, + ); + } +} + +class _FavoriteProposals extends StatelessWidget { + const _FavoriteProposals(); + + @override + Widget build(BuildContext context) { + return ValueListenableBuilder>( + valueListenable: _favoriteProposals, + builder: (context, favoriteProposals, child) { + return Wrap( + spacing: 16, + runSpacing: 16, + children: [ + for (final (index, proposal) in favoriteProposals.indexed) + PendingProposalCard( + image: index.isEven + ? VoicesAssets.images.proposalBackground1 + : VoicesAssets.images.proposalBackground2, + proposal: proposal, + isFavorite: true, + onFavoriteChanged: (isFavorite) => + _onFavoriteChanged(proposal, isFavorite), + ), + ], + ); + }, + ); + } +} + +void _onFavoriteChanged(PendingProposal proposal, bool isFavorite) { + final proposals = Set.of(_favoriteProposals.value); + if (isFavorite) { + proposals.add(proposal); + } else { + proposals.remove(proposal); + } + _favoriteProposals.value = proposals.toList(); +} diff --git a/catalyst_voices/lib/pages/workspace/workspace.dart b/catalyst_voices/lib/pages/workspace/workspace.dart new file mode 100644 index 00000000000..6c0e8f942b5 --- /dev/null +++ b/catalyst_voices/lib/pages/workspace/workspace.dart @@ -0,0 +1 @@ +export 'workspace_page.dart'; diff --git a/catalyst_voices/lib/pages/workspace/workspace_page.dart b/catalyst_voices/lib/pages/workspace/workspace_page.dart new file mode 100644 index 00000000000..ee8312603a2 --- /dev/null +++ b/catalyst_voices/lib/pages/workspace/workspace_page.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; + +class WorkspacePage extends StatelessWidget { + const WorkspacePage({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + color: Colors.teal, + alignment: Alignment.center, + child: Text( + 'Workspace', + style: Theme.of(context).textTheme.titleLarge, + ), + ); + } +} diff --git a/catalyst_voices/lib/routes/app_router.dart b/catalyst_voices/lib/routes/app_router.dart index a81914ef754..9e43ba20b9b 100644 --- a/catalyst_voices/lib/routes/app_router.dart +++ b/catalyst_voices/lib/routes/app_router.dart @@ -1,53 +1,44 @@ -import 'package:catalyst_voices/routes/home_page_route.dart' as home_route; -import 'package:catalyst_voices/routes/login_page_route.dart' as login_route; -import 'package:catalyst_voices/routes/routes.dart'; -import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; -import 'package:flutter/foundation.dart'; +import 'dart:async'; + +import 'package:catalyst_voices/routes/guards/route_guard.dart'; +import 'package:catalyst_voices/routes/routing/routes.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; -final class AppRouter { +abstract final class AppRouter { static final _rootNavigatorKey = GlobalKey( debugLabel: 'rootNavigatorKey', ); static GoRouter init({ - required AuthenticationBloc authenticationBloc, + List guards = const [], }) { return GoRouter( debugLogDiagnostics: true, navigatorKey: _rootNavigatorKey, - initialLocation: _isWeb(), - refreshListenable: AppRouterRefreshStream(authenticationBloc.stream), - redirect: (context, state) => _guard(authenticationBloc, state), + initialLocation: Routes.initialLocation, + redirect: (context, state) => _guard(context, state, guards), observers: [ SentryNavigatorObserver(), ], - routes: _routes(), + routes: Routes.routes, ); } - static String? _guard( - AuthenticationBloc authenticationBloc, + static FutureOr _guard( + BuildContext context, GoRouterState state, - ) { - // Always return home route that defaults to coming soon page. - return home_route.homePath; - } + List guards, + ) async { + for (final guard in guards) { + final redirect = await guard.redirect(context, state); - static String? _isWeb() { - if (kIsWeb) { - return Uri.base.toString().replaceFirst(Uri.base.origin, ''); - } else { - return null; + if (redirect != null) { + return redirect; + } } - } - static List _routes() { - return [ - ...login_route.$appRoutes, - ...home_route.$appRoutes, - ]; + return null; } } diff --git a/catalyst_voices/lib/routes/app_router_refresh_stream.dart b/catalyst_voices/lib/routes/app_router_refresh_stream.dart deleted file mode 100644 index e748de521a4..00000000000 --- a/catalyst_voices/lib/routes/app_router_refresh_stream.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/material.dart'; - -final class AppRouterRefreshStream extends ChangeNotifier { - late final StreamSubscription _subscription; - - AppRouterRefreshStream(Stream stream) { - notifyListeners(); - _subscription = stream.asBroadcastStream().listen((_) => notifyListeners()); - } - - @override - Future dispose() async { - await _subscription.cancel(); - super.dispose(); - } -} diff --git a/catalyst_voices/lib/routes/guards/milestone_guard.dart b/catalyst_voices/lib/routes/guards/milestone_guard.dart new file mode 100644 index 00000000000..81019a157ad --- /dev/null +++ b/catalyst_voices/lib/routes/guards/milestone_guard.dart @@ -0,0 +1,27 @@ +import 'dart:async'; + +import 'package:catalyst_voices/pages/coming_soon/coming_soon.dart'; +import 'package:catalyst_voices/routes/guards/route_guard.dart'; +import 'package:catalyst_voices/routes/routing/routing.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:go_router/src/state.dart'; + +/// Always redirects to [ComingSoonPage] except for milestone sub pages. +final class MilestoneGuard implements RouteGuard { + const MilestoneGuard(); + + @override + FutureOr redirect(BuildContext context, GoRouterState state) { + // allow milestone sub pages + if (state.uri.toString().startsWith('/${Routes.currentMilestone}')) { + return null; + } + + // if already at destination skip redirect + if (state.uri.toString() == const ComingSoonRoute().location) { + return null; + } + + return const ComingSoonRoute().location; + } +} diff --git a/catalyst_voices/lib/routes/guards/route_guard.dart b/catalyst_voices/lib/routes/guards/route_guard.dart new file mode 100644 index 00000000000..ec96bbd8ae2 --- /dev/null +++ b/catalyst_voices/lib/routes/guards/route_guard.dart @@ -0,0 +1,58 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +/// Interface defining a route guard. +/// +/// A route guard is a mechanism to control access to specific routes within a navigation system. +/// It provides a way to intercept navigation requests and decide whether to allow or redirect +/// the user to a different route based on certain conditions. +/// +/// **Purpose:** +/// +/// - **Authentication:** Ensure that only authenticated users can access protected routes. +/// - **Authorization:** Verify that users have the necessary permissions to access specific routes. +/// - **Conditional access:** Implement custom logic to determine whether a user can proceed to a route. +/// - **Redirection:** Redirect users to appropriate routes based on their authentication status or other criteria. +/// +/// **Usage:** +/// +/// 1. **Implement the `RouteGuard` interface:** Create a class that implements the `RouteGuard` interface. +/// 2. **Override the `redirect` method:** In the `redirect` method, implement your custom logic to decide whether to allow navigation or redirect the user. +/// 3. **Register the route guard:** Associate the route guard with specific routes in your navigation system. +/// +/// **Example:** +/// +/// ```dart +/// class AuthenticationGuard implements RouteGuard { +/// @override +/// FutureOr redirect(BuildContext context, GoRouterState state) async { +/// if (!context.read().isAuthenticated) { +/// return const LoginRoute().location; // Redirect to login page if user is not authenticated +/// } +/// return null; // Allow navigation +/// } +/// } +/// ``` +/// +/// **Note:** +/// +/// - The `redirect` method returns a `FutureOr`. If a `String` is returned, it indicates the path to redirect to. If `null` is returned, navigation is allowed to proceed. +/// - The `BuildContext` and `GoRouterState` parameters provide information about the current navigation context. +abstract interface class RouteGuard { + /// Determines whether to allow navigation or redirect the user to a different route. + /// + /// **Parameters:** + /// + /// - `context`: The current build context. + /// - `state`: The current `GoRouterState` representing the navigation request. + /// + /// **Returns:** + /// + /// - A `FutureOr` indicating the path to redirect to, or `null` if navigation is allowed. + FutureOr redirect( + BuildContext context, + GoRouterState state, + ); +} diff --git a/catalyst_voices/lib/routes/routes.dart b/catalyst_voices/lib/routes/routes.dart index 52454815c79..64153772694 100644 --- a/catalyst_voices/lib/routes/routes.dart +++ b/catalyst_voices/lib/routes/routes.dart @@ -1,3 +1,3 @@ export 'app_router.dart'; -export 'app_router_refresh_stream.dart'; export 'app_scaffold.dart'; +export 'routing/routing.dart'; diff --git a/catalyst_voices/lib/routes/home_page_route.dart b/catalyst_voices/lib/routes/routing/coming_soon_route.dart similarity index 63% rename from catalyst_voices/lib/routes/home_page_route.dart rename to catalyst_voices/lib/routes/routing/coming_soon_route.dart index c69d7aa56e8..cc511330a5f 100644 --- a/catalyst_voices/lib/routes/home_page_route.dart +++ b/catalyst_voices/lib/routes/routing/coming_soon_route.dart @@ -2,12 +2,12 @@ import 'package:catalyst_voices/pages/coming_soon/coming_soon.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; -part 'home_page_route.g.dart'; +part 'coming_soon_route.g.dart'; -const homePath = '/'; +@TypedGoRoute(path: '/') +final class ComingSoonRoute extends GoRouteData { + const ComingSoonRoute(); -@TypedGoRoute(path: homePath) -final class HomeRoute extends GoRouteData { @override Widget build(BuildContext context, GoRouterState state) { return const ComingSoonPage(); diff --git a/catalyst_voices/lib/routes/home_page_route.g.dart b/catalyst_voices/lib/routes/routing/coming_soon_route.g.dart similarity index 67% rename from catalyst_voices/lib/routes/home_page_route.g.dart rename to catalyst_voices/lib/routes/routing/coming_soon_route.g.dart index 3ec8ca4cf9a..8c843d09165 100644 --- a/catalyst_voices/lib/routes/home_page_route.g.dart +++ b/catalyst_voices/lib/routes/routing/coming_soon_route.g.dart @@ -1,22 +1,23 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'home_page_route.dart'; +part of 'coming_soon_route.dart'; // ************************************************************************** // GoRouterGenerator // ************************************************************************** List get $appRoutes => [ - $homeRoute, + $comingSoonRoute, ]; -RouteBase get $homeRoute => GoRouteData.$route( +RouteBase get $comingSoonRoute => GoRouteData.$route( path: '/', - factory: $HomeRouteExtension._fromState, + factory: $ComingSoonRouteExtension._fromState, ); -extension $HomeRouteExtension on HomeRoute { - static HomeRoute _fromState(GoRouterState state) => HomeRoute(); +extension $ComingSoonRouteExtension on ComingSoonRoute { + static ComingSoonRoute _fromState(GoRouterState state) => + const ComingSoonRoute(); String get location => GoRouteData.$location( '/', diff --git a/catalyst_voices/lib/routes/login_page_route.dart b/catalyst_voices/lib/routes/routing/login_route.dart similarity index 74% rename from catalyst_voices/lib/routes/login_page_route.dart rename to catalyst_voices/lib/routes/routing/login_route.dart index e1773759a60..4bec9dce343 100644 --- a/catalyst_voices/lib/routes/login_page_route.dart +++ b/catalyst_voices/lib/routes/routing/login_route.dart @@ -2,12 +2,12 @@ import 'package:catalyst_voices/pages/login/login.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; -part 'login_page_route.g.dart'; +part 'login_route.g.dart'; -const loginPath = '/login'; - -@TypedGoRoute(path: loginPath) +@TypedGoRoute(path: '/login') final class LoginRoute extends GoRouteData { + const LoginRoute(); + @override Widget build(BuildContext context, GoRouterState state) { return const LoginPage(); diff --git a/catalyst_voices/lib/routes/login_page_route.g.dart b/catalyst_voices/lib/routes/routing/login_route.g.dart similarity index 88% rename from catalyst_voices/lib/routes/login_page_route.g.dart rename to catalyst_voices/lib/routes/routing/login_route.g.dart index a81d22d9fa9..5d87880c7a8 100644 --- a/catalyst_voices/lib/routes/login_page_route.g.dart +++ b/catalyst_voices/lib/routes/routing/login_route.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'login_page_route.dart'; +part of 'login_route.dart'; // ************************************************************************** // GoRouterGenerator @@ -16,7 +16,7 @@ RouteBase get $loginRoute => GoRouteData.$route( ); extension $LoginRouteExtension on LoginRoute { - static LoginRoute _fromState(GoRouterState state) => LoginRoute(); + static LoginRoute _fromState(GoRouterState state) => const LoginRoute(); String get location => GoRouteData.$location( '/login', diff --git a/catalyst_voices/lib/routes/routing/routes.dart b/catalyst_voices/lib/routes/routing/routes.dart new file mode 100644 index 00000000000..9f50c9166a4 --- /dev/null +++ b/catalyst_voices/lib/routes/routing/routes.dart @@ -0,0 +1,21 @@ +import 'package:catalyst_voices/routes/routing/coming_soon_route.dart' + as coming_soon; +import 'package:catalyst_voices/routes/routing/login_route.dart' as login; +import 'package:catalyst_voices/routes/routing/spaces_route.dart' as spaces; +import 'package:go_router/go_router.dart'; + +/// Semantic anchor for generated routes so only this class +/// knows how to work with them. +abstract final class Routes { + static const currentMilestone = 'm4'; + + static String get initialLocation { + return const coming_soon.ComingSoonRoute().location; + } + + static List get routes => [ + ...coming_soon.$appRoutes, + ...login.$appRoutes, + ...spaces.$appRoutes, + ]; +} diff --git a/catalyst_voices/lib/routes/routing/routing.dart b/catalyst_voices/lib/routes/routing/routing.dart new file mode 100644 index 00000000000..9fb12421dd1 --- /dev/null +++ b/catalyst_voices/lib/routes/routing/routing.dart @@ -0,0 +1,4 @@ +export 'coming_soon_route.dart' hide $appRoutes; +export 'login_route.dart' hide $appRoutes; +export 'routes.dart'; +export 'spaces_route.dart' hide $appRoutes; diff --git a/catalyst_voices/lib/routes/routing/spaces_route.dart b/catalyst_voices/lib/routes/routing/spaces_route.dart new file mode 100644 index 00000000000..ee13a93ba20 --- /dev/null +++ b/catalyst_voices/lib/routes/routing/spaces_route.dart @@ -0,0 +1,106 @@ +import 'package:catalyst_voices/pages/discovery/discovery.dart'; +import 'package:catalyst_voices/pages/funded_projects/funded_projects_page.dart'; +import 'package:catalyst_voices/pages/spaces/spaces.dart'; +import 'package:catalyst_voices/pages/treasury/treasury.dart'; +import 'package:catalyst_voices/pages/voting/voting_page.dart'; +import 'package:catalyst_voices/pages/workspace/workspace_page.dart'; +import 'package:catalyst_voices/routes/routing/routes.dart'; +import 'package:catalyst_voices/routes/routing/transitions/transitions.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +part 'spaces_route.g.dart'; + +@TypedShellRoute( + routes: >[ + TypedGoRoute(path: '/${Routes.currentMilestone}/treasury'), + TypedGoRoute(path: '/${Routes.currentMilestone}/discovery'), + TypedGoRoute(path: '/${Routes.currentMilestone}/workspace'), + TypedGoRoute(path: '/${Routes.currentMilestone}/voting'), + TypedGoRoute( + path: '/${Routes.currentMilestone}/funded_projects', + ), + ], +) +final class SpacesShellRouteData extends ShellRouteData { + static const _spacePathMapping = { + 'treasury': Space.treasury, + 'discovery': Space.discovery, + 'workspace': Space.workspace, + 'voting': Space.voting, + 'funded_projects': Space.fundedProjects, + }; + + const SpacesShellRouteData(); + + @override + Widget builder( + BuildContext context, + GoRouterState state, + Widget navigator, + ) { + final spacePath = state.uri.pathSegments + .skipWhile((value) => value == Routes.currentMilestone) + .first; + + final space = _spacePathMapping[spacePath]; + + assert( + space != null, + 'Space path[$spacePath] is not defined. ' + 'Make sure to match all routes to spaces', + ); + + return SpacesShellPage( + space: space!, + child: navigator, + ); + } +} + +final class TreasuryRoute extends GoRouteData with FadePageTransitionMixin { + const TreasuryRoute(); + + @override + Widget build(BuildContext context, GoRouterState state) { + return const TreasuryPage(); + } +} + +final class DiscoveryRoute extends GoRouteData with FadePageTransitionMixin { + const DiscoveryRoute(); + + @override + Widget build(BuildContext context, GoRouterState state) { + return const DiscoveryPage(); + } +} + +final class WorkspaceRoute extends GoRouteData with FadePageTransitionMixin { + const WorkspaceRoute(); + + @override + Widget build(BuildContext context, GoRouterState state) { + return const WorkspacePage(); + } +} + +final class VotingRoute extends GoRouteData with FadePageTransitionMixin { + const VotingRoute(); + + @override + Widget build(BuildContext context, GoRouterState state) { + return const VotingPage(); + } +} + +final class FundedProjectsRoute extends GoRouteData + with FadePageTransitionMixin { + const FundedProjectsRoute(); + + @override + Widget build(BuildContext context, GoRouterState state) { + return const FundedProjectsPage(); + } +} diff --git a/catalyst_voices/lib/routes/routing/spaces_route.g.dart b/catalyst_voices/lib/routes/routing/spaces_route.g.dart new file mode 100644 index 00000000000..dc54a81a2a8 --- /dev/null +++ b/catalyst_voices/lib/routes/routing/spaces_route.g.dart @@ -0,0 +1,130 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'spaces_route.dart'; + +// ************************************************************************** +// GoRouterGenerator +// ************************************************************************** + +List get $appRoutes => [ + $spacesShellRouteData, + ]; + +RouteBase get $spacesShellRouteData => ShellRouteData.$route( + factory: $SpacesShellRouteDataExtension._fromState, + routes: [ + GoRouteData.$route( + path: '/m4/treasury', + factory: $TreasuryRouteExtension._fromState, + ), + GoRouteData.$route( + path: '/m4/discovery', + factory: $DiscoveryRouteExtension._fromState, + ), + GoRouteData.$route( + path: '/m4/workspace', + factory: $WorkspaceRouteExtension._fromState, + ), + GoRouteData.$route( + path: '/m4/voting', + factory: $VotingRouteExtension._fromState, + ), + GoRouteData.$route( + path: '/m4/funded_projects', + factory: $FundedProjectsRouteExtension._fromState, + ), + ], + ); + +extension $SpacesShellRouteDataExtension on SpacesShellRouteData { + static SpacesShellRouteData _fromState(GoRouterState state) => + const SpacesShellRouteData(); +} + +extension $TreasuryRouteExtension on TreasuryRoute { + static TreasuryRoute _fromState(GoRouterState state) => const TreasuryRoute(); + + String get location => GoRouteData.$location( + '/m4/treasury', + ); + + void go(BuildContext context) => context.go(location); + + Future push(BuildContext context) => context.push(location); + + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + void replace(BuildContext context) => context.replace(location); +} + +extension $DiscoveryRouteExtension on DiscoveryRoute { + static DiscoveryRoute _fromState(GoRouterState state) => + const DiscoveryRoute(); + + String get location => GoRouteData.$location( + '/m4/discovery', + ); + + void go(BuildContext context) => context.go(location); + + Future push(BuildContext context) => context.push(location); + + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + void replace(BuildContext context) => context.replace(location); +} + +extension $WorkspaceRouteExtension on WorkspaceRoute { + static WorkspaceRoute _fromState(GoRouterState state) => + const WorkspaceRoute(); + + String get location => GoRouteData.$location( + '/m4/workspace', + ); + + void go(BuildContext context) => context.go(location); + + Future push(BuildContext context) => context.push(location); + + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + void replace(BuildContext context) => context.replace(location); +} + +extension $VotingRouteExtension on VotingRoute { + static VotingRoute _fromState(GoRouterState state) => const VotingRoute(); + + String get location => GoRouteData.$location( + '/m4/voting', + ); + + void go(BuildContext context) => context.go(location); + + Future push(BuildContext context) => context.push(location); + + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + void replace(BuildContext context) => context.replace(location); +} + +extension $FundedProjectsRouteExtension on FundedProjectsRoute { + static FundedProjectsRoute _fromState(GoRouterState state) => + const FundedProjectsRoute(); + + String get location => GoRouteData.$location( + '/m4/funded_projects', + ); + + void go(BuildContext context) => context.go(location); + + Future push(BuildContext context) => context.push(location); + + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + void replace(BuildContext context) => context.replace(location); +} diff --git a/catalyst_voices/lib/routes/routing/transitions/fade_page_transition_mixin.dart b/catalyst_voices/lib/routes/routing/transitions/fade_page_transition_mixin.dart new file mode 100644 index 00000000000..9c6b897b7d0 --- /dev/null +++ b/catalyst_voices/lib/routes/routing/transitions/fade_page_transition_mixin.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +mixin FadePageTransitionMixin on GoRouteData { + @override + Page buildPage(BuildContext context, GoRouterState state) { + return CustomTransitionPage( + key: state.pageKey, + name: state.name ?? state.path, + arguments: { + ...state.pathParameters, + ...state.uri.queryParameters + }, + restorationId: state.pageKey.value, + child: build(context, state), + transitionsBuilder: (context, animation, secondaryAnimation, child) { + return FadeTransition( + opacity: animation, + child: child, + ); + }, + ); + } +} diff --git a/catalyst_voices/lib/routes/routing/transitions/transitions.dart b/catalyst_voices/lib/routes/routing/transitions/transitions.dart new file mode 100644 index 00000000000..e8ed9afd9ff --- /dev/null +++ b/catalyst_voices/lib/routes/routing/transitions/transitions.dart @@ -0,0 +1 @@ +export 'fade_page_transition_mixin.dart'; diff --git a/catalyst_voices/lib/widgets/app_bar/actions/user_profile_button.dart b/catalyst_voices/lib/widgets/app_bar/actions/user_profile_button.dart index b2611977b4b..a0c909dc81e 100644 --- a/catalyst_voices/lib/widgets/app_bar/actions/user_profile_button.dart +++ b/catalyst_voices/lib/widgets/app_bar/actions/user_profile_button.dart @@ -22,8 +22,8 @@ class UserProfileButton extends StatelessWidget { } else { return Chip( shape: const RoundedRectangleBorder( - // This is a custom chip that simulates a button look&feel, so visual - // property are specified. + // This is a custom chip that simulates a button look&feel, so visual + // property are specified. borderRadius: BorderRadius.all(Radius.circular(30)), ), label: Text(context.l10n.userProfileGuestLabelText), diff --git a/catalyst_voices/lib/widgets/app_bar/voices_app_bar.dart b/catalyst_voices/lib/widgets/app_bar/voices_app_bar.dart index 7b6dbd1b163..6b62f97abbb 100644 --- a/catalyst_voices/lib/widgets/app_bar/voices_app_bar.dart +++ b/catalyst_voices/lib/widgets/app_bar/voices_app_bar.dart @@ -1,4 +1,5 @@ -import 'package:catalyst_voices/widgets/app_bar/actions/voices_app_bar_actions.dart'; +import 'package:catalyst_voices/widgets/app_bar/actions/search_button.dart'; +import 'package:catalyst_voices/widgets/buttons/voices_buttons.dart'; import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; @@ -10,122 +11,184 @@ import 'package:flutter/material.dart'; /// The [VoicesAppBar] implements the [PreferredSizeWidget] interface, making /// it suitable for use as an app bar in a [Scaffold]. /// -/// The [isNavVisible] parameter determines whether the navigation icon -/// (menu button) is visible. The [actions] parameter is a list of widgets to +/// If has [Scaffold] parent with defined drawer automatically adds leading +/// toggle button. The [actions] parameter is a list of widgets to /// display as actions in the app bar. /// /// A set of possible actions widget are available in the actions subfolder. class VoicesAppBar extends StatelessWidget implements PreferredSizeWidget { - final bool isNavVisible; + final Widget? leading; final List actions; + final bool showSearch; + final bool automaticallyImplyLeading; + final Color? backgroundColor; const VoicesAppBar({ super.key, - this.isNavVisible = true, + this.leading, this.actions = const [], + this.showSearch = false, + this.automaticallyImplyLeading = true, + this.backgroundColor, }); + @override Widget build(BuildContext context) { - return ResponsiveBuilder( - xs: 8, - other: 24, - builder: (context, data) => AppBar( - titleSpacing: data, - toolbarHeight: 64, - leadingWidth: 48 + data!, - leading: isNavVisible - ? Padding( - padding: EdgeInsets.only(top: 8, bottom: 8, left: data), - child: IconButton( - color: Theme.of(context).colors.iconsForeground, - onPressed: () { - context - .findAncestorStateOfType() - ?.openDrawer(); - }, - icon: const Icon(CatalystVoicesIcons.menu), - ), - ) - : null, - title: Container( - height: 64, - alignment: Alignment.centerLeft, - child: ResponsiveBuilder<({List widgets, double itemGap})>( - builder: (context, data) => ListView.separated( - shrinkWrap: true, - itemBuilder: (context, index) => data.widgets[index], - separatorBuilder: (context, index) => SizedBox( - width: data.itemGap, - ), - itemCount: data!.widgets.length, - scrollDirection: Axis.horizontal, - ), - xs: ( - widgets: [ - CatalystSvgPicture.asset( - Theme.of(context).brandAssets.logoIcon.path, - ), - ], - itemGap: 8 - ), - sm: ( - widgets: [ - CatalystSvgPicture.asset( - Theme.of(context).brandAssets.logo.path, - ), - SearchButton( - onPressed: () {}, - ), - ], - itemGap: 16 - ), - other: ( - widgets: [ - CatalystSvgPicture.asset( - Theme.of(context).brandAssets.logo.path, - ), - SearchButton( - onPressed: () {}, - ), - ], - itemGap: 24 - ), + return _Theme( + child: ResponsiveBuilder( + xs: 8, + other: 16, + builder: (context, spacing) { + return AppBar( + titleSpacing: spacing, + toolbarHeight: preferredSize.height, + leading: _buildLeading(context), + leadingWidth: 48.0 + spacing, + automaticallyImplyLeading: false, + backgroundColor: backgroundColor, + title: _Title(showSearch), + actions: [ + _Actions(children: actions), + ], + ); + }, + ), + ); + } + + // Has to be nullable, that's why this is a function. + Widget? _buildLeading(BuildContext context) { + final canImplyDrawerToggleButton = automaticallyImplyLeading && + (Scaffold.maybeOf(context)?.hasDrawer ?? false); + + final canImplyPopButton = automaticallyImplyLeading && + (Navigator.maybeOf(context)?.canPop() ?? false); + + final child = leading ?? + (canImplyDrawerToggleButton ? const DrawerToggleButton() : null) ?? + (canImplyPopButton ? const NavigationPopButton() : null); + + if (child == null) { + return null; + } + + return Align( + alignment: Alignment.centerRight, + child: child, + ); + } + + @override + Size get preferredSize => const Size.fromHeight(64); +} + +class _Theme extends StatelessWidget { + final Widget child; + + const _Theme({ + required this.child, + }); + + @override + Widget build(BuildContext context) { + return IconButtonTheme( + data: const IconButtonThemeData( + style: ButtonStyle( + fixedSize: WidgetStatePropertyAll(Size.square(48)), + ), + ), + child: child, + ); + } +} + +class _Title extends StatelessWidget { + final bool showSearch; + + const _Title(this.showSearch); + + @override + Widget build(BuildContext context) { + return Container( + height: 64, + alignment: Alignment.centerLeft, + child: ResponsiveBuilder<({List widgets, double itemGap})>( + builder: (context, data) => ListView.separated( + shrinkWrap: true, + itemBuilder: (context, index) => data.widgets[index], + separatorBuilder: (context, index) => SizedBox( + width: data.itemGap, ), + itemCount: data.widgets.length, + scrollDirection: Axis.horizontal, + ), + xs: ( + widgets: [ + Theme.of(context).brandAssets.logoIcon.buildPicture(), + ], + itemGap: 8 ), - actions: [ - ResponsiveBuilder<({EdgeInsets wrapperPadding, double itemGap})>( - xs: const ( - wrapperPadding: EdgeInsets.only(right: 8), - itemGap: 6, - ), - sm: const ( - wrapperPadding: EdgeInsets.only(right: 16), - itemGap: 6, - ), - other: const ( - wrapperPadding: EdgeInsets.only(right: 24), - itemGap: 12, - ), - builder: (context, data) => Container( - alignment: Alignment.centerRight, - padding: data!.wrapperPadding, - child: ListView.separated( - shrinkWrap: true, - padding: const EdgeInsets.symmetric(vertical: 12), - itemBuilder: (context, index) => actions[index], - separatorBuilder: (context, index) => SizedBox( - width: data.itemGap, - ), - itemCount: actions.length, - scrollDirection: Axis.horizontal, + sm: ( + widgets: [ + Theme.of(context).brandAssets.logo.buildPicture(), + if (showSearch) + SearchButton( + onPressed: () {}, ), - ), - ), - ], + ], + itemGap: 16 + ), + other: ( + widgets: [ + Theme.of(context).brandAssets.logo.buildPicture(), + if (showSearch) + SearchButton( + onPressed: () {}, + ), + ], + itemGap: 24 + ), ), ); } +} + +class _Actions extends StatelessWidget { + final List children; + + const _Actions({ + required this.children, + }); @override - Size get preferredSize => const Size.fromHeight(64); + Widget build(BuildContext context) { + return ResponsiveBuilder<({EdgeInsets wrapperPadding, double itemGap})>( + xs: const ( + wrapperPadding: EdgeInsets.only(right: 8), + itemGap: 6, + ), + sm: const ( + wrapperPadding: EdgeInsets.only(right: 16), + itemGap: 6, + ), + other: const ( + wrapperPadding: EdgeInsets.only(right: 24), + itemGap: 12, + ), + builder: (context, data) => Container( + alignment: Alignment.centerRight, + padding: data.wrapperPadding, + child: ListView.separated( + shrinkWrap: true, + padding: const EdgeInsets.symmetric(vertical: 12), + itemBuilder: (context, index) => children[index], + separatorBuilder: (context, index) => SizedBox( + width: data.itemGap, + ), + itemCount: children.length, + scrollDirection: Axis.horizontal, + ), + ), + ); + } } diff --git a/catalyst_voices/lib/widgets/avatars/voices_avatar.dart b/catalyst_voices/lib/widgets/avatars/voices_avatar.dart new file mode 100644 index 00000000000..f75da4608fe --- /dev/null +++ b/catalyst_voices/lib/widgets/avatars/voices_avatar.dart @@ -0,0 +1,73 @@ +import 'package:flutter/material.dart'; + +/// A circular icon on a background. Usually represents a user profile. +/// +/// [icon] by default will be spaced at 24 logical pixels +/// unless it provides it's own size. +class VoicesAvatar extends StatelessWidget { + /// The main content of the chip, usually an [Icon] or [Text] widget. + final Widget icon; + + /// The color of the foreground [icon], [ColorScheme.primary] by default + final Color? foregroundColor; + + /// The color of the background, + /// [ColorScheme.primaryContainer] by default. + final Color? backgroundColor; + + /// The internal padding from the widget borders to the internal content. + final EdgeInsets padding; + + /// The size of the avatar, expressed as the radius (half the diameter). + final double radius; + + /// The callback called when the widget is tapped. + final VoidCallback? onTap; + + /// The default constructor for the [VoicesAvatar]. + const VoicesAvatar({ + super.key, + required this.icon, + this.foregroundColor, + this.backgroundColor, + this.padding = const EdgeInsets.all(8), + this.radius = 20, + this.onTap, + }); + + @override + Widget build(BuildContext context) { + return CircleAvatar( + radius: radius, + backgroundColor: + backgroundColor ?? Theme.of(context).colorScheme.primaryContainer, + child: Material( + type: MaterialType.transparency, + child: InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(radius * 2), + child: Center( + child: Padding( + padding: padding, + child: IconTheme( + data: IconTheme.of(context).copyWith( + size: 24, + color: + foregroundColor ?? Theme.of(context).colorScheme.primary, + ), + child: DefaultTextStyle( + style: Theme.of(context).textTheme.bodyLarge!.copyWith( + fontSize: 18, + color: foregroundColor ?? + Theme.of(context).colorScheme.primary, + ), + child: icon, + ), + ), + ), + ), + ), + ), + ); + } +} diff --git a/catalyst_voices/lib/widgets/buttons/voices_buttons.dart b/catalyst_voices/lib/widgets/buttons/voices_buttons.dart new file mode 100644 index 00000000000..a096d1e930a --- /dev/null +++ b/catalyst_voices/lib/widgets/buttons/voices_buttons.dart @@ -0,0 +1,131 @@ +import 'dart:async'; + +import 'package:catalyst_voices/widgets/buttons/voices_icon_button.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:flutter/material.dart'; + +class DrawerToggleButton extends StatelessWidget { + const DrawerToggleButton({super.key}); + + @override + Widget build(BuildContext context) { + return VoicesIconButton( + onTap: () => Scaffold.maybeOf(context)?.openDrawer(), + child: const Icon(CatalystVoicesIcons.menu), + ); + } +} + +class LeftArrowButton extends StatelessWidget { + final VoidCallback? onTap; + + const LeftArrowButton({ + super.key, + this.onTap, + }); + + @override + Widget build(BuildContext context) { + return VoicesIconButton( + onTap: onTap, + child: const Icon(CatalystVoicesIcons.arrow_narrow_left), + ); + } +} + +class RightArrowButton extends StatelessWidget { + final VoidCallback? onTap; + + const RightArrowButton({ + super.key, + this.onTap, + }); + + @override + Widget build(BuildContext context) { + return VoicesIconButton( + onTap: onTap, + child: const Icon(CatalystVoicesIcons.arrow_narrow_right), + ); + } +} + +class ChevronDownButton extends StatelessWidget { + final VoidCallback? onTap; + + const ChevronDownButton({ + super.key, + this.onTap, + }); + + @override + Widget build(BuildContext context) { + return VoicesIconButton( + onTap: onTap, + child: const Icon(CatalystVoicesIcons.chevron_down), + ); + } +} + +class ChevronRightButton extends StatelessWidget { + final VoidCallback? onTap; + + const ChevronRightButton({ + super.key, + this.onTap, + }); + + @override + Widget build(BuildContext context) { + return VoicesIconButton( + onTap: onTap, + child: const Icon(CatalystVoicesIcons.chevron_right), + ); + } +} + +class ChevronExpandButton extends StatelessWidget { + final bool isExpanded; + final VoidCallback? onTap; + + const ChevronExpandButton({ + super.key, + this.isExpanded = true, + this.onTap, + }); + + @override + Widget build(BuildContext context) { + return isExpanded + ? ChevronDownButton(onTap: onTap) + : ChevronRightButton(onTap: onTap); + } +} + +class NavigationPopButton extends StatelessWidget { + const NavigationPopButton({super.key}); + + @override + Widget build(BuildContext context) { + return LeftArrowButton( + onTap: () => unawaited(Navigator.maybeOf(context)?.maybePop()), + ); + } +} + +class XButton extends StatelessWidget { + final VoidCallback? onTap; + + const XButton({ + super.key, + this.onTap, + }); + + @override + Widget build(BuildContext context) { + return VoicesIconButton( + onTap: onTap, + child: const Icon(CatalystVoicesIcons.x), + ); + } +} diff --git a/catalyst_voices/lib/widgets/cards/funded_proposal_card.dart b/catalyst_voices/lib/widgets/cards/funded_proposal_card.dart new file mode 100644 index 00000000000..46d6edd0b51 --- /dev/null +++ b/catalyst_voices/lib/widgets/cards/funded_proposal_card.dart @@ -0,0 +1,248 @@ +import 'package:catalyst_cardano_serialization/catalyst_cardano_serialization.dart'; +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; +import 'package:flutter/material.dart'; + +/// Displays a proposal in funded state on a card. +class FundedProposalCard extends StatelessWidget { + final AssetGenImage image; + final FundedProposal proposal; + final bool isFavorite; + final ValueChanged? onFavoriteChanged; + + const FundedProposalCard({ + super.key, + required this.image, + required this.proposal, + this.isFavorite = false, + this.onFavoriteChanged, + }); + + @override + Widget build(BuildContext context) { + return Container( + width: 326, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + borderRadius: BorderRadius.circular(12), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + _Header( + image: image, + isFavorite: isFavorite, + onFavoriteChanged: onFavoriteChanged, + ), + Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _FundCategory( + fund: proposal.fund, + category: proposal.category, + ), + const SizedBox(height: 4), + _Title(text: proposal.title), + const SizedBox(height: 4), + _FundedDate(dateTime: proposal.fundedDate), + const SizedBox(height: 24), + _FundsAndComments( + funds: proposal.fundsRequested, + commentsCount: proposal.commentsCount, + ), + const SizedBox(height: 24), + _Description(text: proposal.description), + ], + ), + ), + ], + ), + ); + } +} + +class _Header extends StatelessWidget { + final AssetGenImage image; + final bool isFavorite; + final ValueChanged? onFavoriteChanged; + + const _Header({ + required this.image, + required this.isFavorite, + required this.onFavoriteChanged, + }); + + @override + Widget build(BuildContext context) { + return SizedBox( + height: 168, + child: Stack( + children: [ + Positioned.fill( + child: CatalystImage.asset( + image.path, + fit: BoxFit.cover, + ), + ), + if (onFavoriteChanged != null) + Positioned( + top: 2, + right: 2, + child: IconButton( + padding: EdgeInsets.zero, + onPressed: () => onFavoriteChanged?.call(!isFavorite), + icon: Icon( + isFavorite ? Icons.favorite : CatalystVoicesIcons.star, + size: 20, + color: Theme.of(context).colors.iconsOnImage, + ), + ), + ), + Positioned( + left: 12, + bottom: 12, + child: VoicesChip.rectangular( + padding: const EdgeInsets.fromLTRB(10, 6, 10, 4), + leading: Icon( + CatalystVoicesIcons.briefcase, + color: Theme.of(context).colorScheme.primary, + ), + content: Text(context.l10n.fundedProposal), + backgroundColor: Theme.of(context).colors.primary98, + ), + ), + ], + ), + ); + } +} + +class _FundCategory extends StatelessWidget { + final String fund; + final String category; + + const _FundCategory({ + required this.fund, + required this.category, + }); + + @override + Widget build(BuildContext context) { + return Text.rich( + TextSpan( + text: fund + ' / ', + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: Theme.of(context).colors.textDisabled, + ), + children: [ + TextSpan( + text: category, + style: Theme.of(context).textTheme.bodyMedium, + ), + ], + ), + ); + } +} + +class _Title extends StatelessWidget { + final String text; + + const _Title({required this.text}); + + @override + Widget build(BuildContext context) { + return Text( + text, + style: Theme.of(context).textTheme.titleLarge, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ); + } +} + +class _FundedDate extends StatelessWidget { + final DateTime dateTime; + + const _FundedDate({required this.dateTime}); + + @override + Widget build(BuildContext context) { + return Text( + context.l10n.fundedProposalDate(dateTime), + style: Theme.of(context).textTheme.bodySmall, + ); + } +} + +class _FundsAndComments extends StatelessWidget { + final Coin funds; + final int commentsCount; + + const _FundsAndComments({ + required this.funds, + required this.commentsCount, + }); + + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.symmetric(horizontal: 16, vertical: 6), + decoration: BoxDecoration( + color: Theme.of(context).colors.success?.withOpacity(0.08), + borderRadius: BorderRadius.circular(8), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + children: [ + Text( + CryptocurrencyFormatter.formatAmount(funds), + style: Theme.of(context).textTheme.titleLarge, + ), + Text( + context.l10n.fundsRequested, + style: Theme.of(context).textTheme.bodySmall, + ), + ], + ), + VoicesChip.rectangular( + padding: const EdgeInsets.fromLTRB(8, 6, 12, 6), + leading: Icon( + CatalystVoicesIcons.check_circle, + color: Theme.of(context).colors.success, + ), + content: Text(context.l10n.noOfComments(commentsCount)), + backgroundColor: Theme.of(context).colors.successContainer, + ), + ], + ), + ); + } +} + +class _Description extends StatelessWidget { + final String text; + + const _Description({required this.text}); + + @override + Widget build(BuildContext context) { + return Text( + text, + style: Theme.of(context).textTheme.bodyLarge?.copyWith( + color: Theme.of(context).colors.textOnPrimary, + ), + maxLines: 4, + overflow: TextOverflow.ellipsis, + ); + } +} diff --git a/catalyst_voices/lib/widgets/cards/pending_proposal_card.dart b/catalyst_voices/lib/widgets/cards/pending_proposal_card.dart new file mode 100644 index 00000000000..c6fab02604f --- /dev/null +++ b/catalyst_voices/lib/widgets/cards/pending_proposal_card.dart @@ -0,0 +1,297 @@ +import 'package:catalyst_cardano_serialization/catalyst_cardano_serialization.dart'; +import 'package:catalyst_voices/common/formatters/date_formatter.dart'; +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; +import 'package:flutter/material.dart'; + +/// Displays a proposal in pending state on a card. +class PendingProposalCard extends StatelessWidget { + final AssetGenImage image; + final PendingProposal proposal; + final bool isFavorite; + final ValueChanged? onFavoriteChanged; + + const PendingProposalCard({ + super.key, + required this.image, + required this.proposal, + this.isFavorite = false, + this.onFavoriteChanged, + }); + + @override + Widget build(BuildContext context) { + return Container( + width: 326, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + borderRadius: BorderRadius.circular(12), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + _Header( + image: image, + isFavorite: isFavorite, + onFavoriteChanged: onFavoriteChanged, + ), + Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _FundCategory( + fund: proposal.fund, + category: proposal.category, + ), + const SizedBox(height: 4), + _Title(text: proposal.title), + const SizedBox(height: 4), + _LastUpdateDate(dateTime: proposal.lastUpdateDate), + const SizedBox(height: 24), + _FundsAndComments( + funds: proposal.fundsRequested, + commentsCount: proposal.commentsCount, + ), + const SizedBox(height: 24), + _Description(text: proposal.description), + ], + ), + ), + _CompletedSegments( + completed: proposal.completedSegments, + total: proposal.totalSegments, + ), + ], + ), + ); + } +} + +class _Header extends StatelessWidget { + final AssetGenImage image; + final bool isFavorite; + final ValueChanged? onFavoriteChanged; + + const _Header({ + required this.image, + required this.isFavorite, + required this.onFavoriteChanged, + }); + + @override + Widget build(BuildContext context) { + return SizedBox( + height: 168, + child: Stack( + children: [ + Positioned.fill( + child: CatalystImage.asset( + image.path, + fit: BoxFit.cover, + ), + ), + if (onFavoriteChanged != null) + Positioned( + top: 2, + right: 2, + child: IconButton( + padding: EdgeInsets.zero, + onPressed: () => onFavoriteChanged?.call(!isFavorite), + icon: Icon( + isFavorite ? Icons.favorite : CatalystVoicesIcons.star, + size: 20, + color: Theme.of(context).colors.iconsOnImage, + ), + ), + ), + Positioned( + left: 12, + bottom: 12, + child: VoicesChip.rectangular( + padding: const EdgeInsets.fromLTRB(10, 6, 10, 4), + leading: Icon( + CatalystVoicesIcons.briefcase, + color: Theme.of(context).colorScheme.primary, + ), + content: Text(context.l10n.publishedProposal), + backgroundColor: Theme.of(context).colors.primary98, + ), + ), + ], + ), + ); + } +} + +class _FundCategory extends StatelessWidget { + final String fund; + final String category; + + const _FundCategory({ + required this.fund, + required this.category, + }); + + @override + Widget build(BuildContext context) { + return Text.rich( + TextSpan( + text: fund + ' / ', + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: Theme.of(context).colors.textDisabled, + ), + children: [ + TextSpan( + text: category, + style: Theme.of(context).textTheme.bodyMedium, + ), + ], + ), + ); + } +} + +class _Title extends StatelessWidget { + final String text; + + const _Title({required this.text}); + + @override + Widget build(BuildContext context) { + return Text( + text, + style: Theme.of(context).textTheme.titleLarge, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ); + } +} + +class _LastUpdateDate extends StatelessWidget { + final DateTime dateTime; + + const _LastUpdateDate({required this.dateTime}); + + @override + Widget build(BuildContext context) { + return Text( + context.l10n.lastUpdateDate( + DateFormatter.formatRecentDate(context.l10n, dateTime), + ), + style: Theme.of(context).textTheme.bodySmall, + ); + } +} + +class _FundsAndComments extends StatelessWidget { + final Coin funds; + final int commentsCount; + + const _FundsAndComments({ + required this.funds, + required this.commentsCount, + }); + + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.symmetric(horizontal: 16, vertical: 6), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.primary.withOpacity(0.12), + borderRadius: BorderRadius.circular(8), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + children: [ + Text( + CryptocurrencyFormatter.formatAmount(funds), + style: Theme.of(context).textTheme.titleLarge, + ), + Text( + context.l10n.fundsRequested, + style: Theme.of(context).textTheme.bodySmall, + ), + ], + ), + VoicesChip.rectangular( + padding: const EdgeInsets.fromLTRB(8, 6, 12, 6), + leading: Icon( + CatalystVoicesIcons.check_circle, + color: Theme.of(context).colorScheme.primary, + ), + content: Text( + context.l10n.noOfComments(commentsCount), + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + ), + backgroundColor: Theme.of(context).colors.onSurfaceNeutralOpaqueLv1, + ), + ], + ), + ); + } +} + +class _Description extends StatelessWidget { + final String text; + + const _Description({required this.text}); + + @override + Widget build(BuildContext context) { + return Text( + text, + style: Theme.of(context).textTheme.bodyLarge?.copyWith( + color: Theme.of(context).colors.textOnPrimary, + ), + maxLines: 4, + overflow: TextOverflow.ellipsis, + ); + } +} + +class _CompletedSegments extends StatelessWidget { + final int completed; + final int total; + + const _CompletedSegments({ + required this.completed, + required this.total, + }); + + @override + Widget build(BuildContext context) { + return Container( + color: Theme.of(context).colorScheme.primary.withOpacity(0.12), + padding: EdgeInsets.all(16), + child: Row( + children: [ + Icon( + CatalystVoicesIcons.clipboard_check, + size: 18, + color: Theme.of(context).colorScheme.primary, + ), + const SizedBox(width: 8), + Flexible( + child: Text( + context.l10n.noOfSegmentsCompleted( + completed, + total, + (completed / total * 100).round(), + ), + ), + ), + ], + ), + ); + } +} diff --git a/catalyst_voices/lib/widgets/common/columns_row.dart b/catalyst_voices/lib/widgets/common/columns_row.dart new file mode 100644 index 00000000000..28f16653b0a --- /dev/null +++ b/catalyst_voices/lib/widgets/common/columns_row.dart @@ -0,0 +1,77 @@ +import 'package:catalyst_voices/widgets/seed_phrase/seed_phrases_completer.dart'; +import 'package:catalyst_voices/widgets/seed_phrase/seed_phrases_picker.dart'; +import 'package:catalyst_voices/widgets/seed_phrase/seed_phrases_viewer.dart'; +import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; +import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; + +/// A widget that arranges children into columns within a row. +/// +/// This widget divides its children into columns based on the [columnsCount] +/// property. Each column is then laid out vertically with optional spacing +/// between children. +/// +/// The [mainAxisSpacing] property controls the horizontal spacing between +/// columns, while the [crossAxisSpacing] property controls the vertical +/// spacing within each column. +/// +/// See following widgets for examples of usage. +/// - [SeedPhrasesViewer], [SeedPhrasesPicker] and [SeedPhrasesCompleter] +class ColumnsRow extends StatelessWidget { + /// The number of columns to create. + final int columnsCount; + + /// The horizontal spacing between columns. Defaults to 0.0. + final double mainAxisSpacing; + + /// The vertical spacing between children within each column. Defaults to 0.0. + final double crossAxisSpacing; + + /// The children to be arranged in columns. + final List children; + + /// Creates a [ColumnsRow] widget. + /// + /// The [columnsCount] argument must be positive. + const ColumnsRow({ + super.key, + required this.columnsCount, + this.mainAxisSpacing = 0.0, + this.crossAxisSpacing = 0.0, + required this.children, + }) : assert(columnsCount >= 0, 'Columns count can not be zero or negative'); + + @override + Widget build(BuildContext context) { + final columnCount = (children.length / columnsCount).ceil(); + final columns = children.slices(columnCount).toList(); + + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: columns + .map((e) => _Column(spacing: crossAxisSpacing, children: e)) + .map((e) => Expanded(child: e)) + .separatedBy(SizedBox(width: mainAxisSpacing)) + .toList(), + ); + } +} + +/// A helper widget that arranges children vertically with optional spacing. +class _Column extends StatelessWidget { + final double spacing; + final List children; + + const _Column({ + this.spacing = 0.0, + required this.children, + }); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: children.separatedBy(SizedBox(height: spacing)).toList(), + ); + } +} diff --git a/catalyst_voices/lib/widgets/common/link_text.dart b/catalyst_voices/lib/widgets/common/link_text.dart new file mode 100644 index 00000000000..4910de5a836 --- /dev/null +++ b/catalyst_voices/lib/widgets/common/link_text.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; + +/// A widget that displays text with an underline that acts as a link. +/// +/// This widget takes three arguments: +/// * `data`: The text to be displayed. (Required) +/// * `style`: An optional TextStyle to customize the appearance of the text. +/// * `onTap`: An optional callback function that will be executed when the +/// user taps on the text. +class LinkText extends StatelessWidget { + /// The text to be displayed. + final String data; + + /// An optional TextStyle to customize the appearance of the text. + final TextStyle? style; + + /// An optional callback function that will be executed when the user taps + /// on the text. + final VoidCallback? onTap; + + const LinkText( + this.data, { + super.key, + this.style, + this.onTap, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final color = theme.colorScheme.primary; + + final effectiveStyle = (style ?? const TextStyle()).copyWith( + color: color, + decoration: TextDecoration.underline, + decorationColor: color, + decorationStyle: TextDecorationStyle.solid, + ); + + return GestureDetector( + onTap: onTap, + child: DefaultTextStyle.merge( + style: style, + child: Text( + data, + style: effectiveStyle, + ), + ), + ); + } +} diff --git a/catalyst_voices/lib/widgets/common/navigation_location.dart b/catalyst_voices/lib/widgets/common/navigation_location.dart new file mode 100644 index 00000000000..23b469f40ac --- /dev/null +++ b/catalyst_voices/lib/widgets/common/navigation_location.dart @@ -0,0 +1,70 @@ +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; +import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; + +/// A widget that displays a navigation location with breadcrumb style. +/// +/// This widget renders a [Container] with the provided `padding` and +/// `constraints`. +/// +/// It displays the navigation location as a [RichText] with each part of the +/// location separated by a forward slash (`/`). +/// The first part is rendered in bold text, while subsequent parts use the +/// regular style. +/// +/// Consider using this widget for breadcrumbs in your navigation bar or +/// other locations where you want to show a user's current location within +/// the application hierarchy. +// Note. Maybe we can introduce RouterNavigationLocation which depends on +// GoRouter.of(context) +class NavigationLocation extends StatelessWidget { + /// A list of strings representing the parts of the navigation location. + final List parts; + + /// The padding to apply to the container. Defaults to a symmetric horizontal + /// padding of 40. + final EdgeInsetsGeometry padding; + + /// The constraints applied to the container. Defaults to a tight constraint + /// with a height of 56. + final BoxConstraints constraints; + + const NavigationLocation({ + super.key, + required this.parts, + this.padding = const EdgeInsets.symmetric(horizontal: 40), + this.constraints = const BoxConstraints.tightFor(height: 56), + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + return Container( + padding: padding, + constraints: constraints, + alignment: Alignment.centerLeft, + child: RichText( + text: TextSpan( + style: theme.textTheme.bodyMedium?.copyWith( + color: theme.colors.textOnPrimary, + ), + children: parts + .mapIndexed( + (index, part) { + return TextSpan( + text: part, + style: index == 0 + ? const TextStyle(fontWeight: FontWeight.bold) + : null, + ); + }, + ) + .separatedBy(const TextSpan(text: ' / ')) + .toList(), + ), + ), + ); + } +} diff --git a/catalyst_voices/lib/widgets/common/proposal_status_container.dart b/catalyst_voices/lib/widgets/common/proposal_status_container.dart new file mode 100644 index 00000000000..919d540b5a8 --- /dev/null +++ b/catalyst_voices/lib/widgets/common/proposal_status_container.dart @@ -0,0 +1,90 @@ +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:flutter/material.dart'; + +class ProposalStatusContainer extends StatelessWidget { + final ProposalStatus type; + + const ProposalStatusContainer({ + super.key, + required this.type, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final config = type._config(context); + + final iconTheme = IconThemeData( + size: 18, + color: config.iconColor, + ); + + final textStyle = (theme.textTheme.labelLarge ?? TextStyle()).copyWith( + color: config.textColor, + ); + + return IconTheme( + data: iconTheme, + child: DefaultTextStyle( + style: textStyle, + child: Container( + padding: EdgeInsets.all(8).add(EdgeInsets.only(right: 4)), + decoration: BoxDecoration( + color: config.backgroundColor, + borderRadius: BorderRadius.circular(8), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(config.iconData), + SizedBox(width: 8), + Text(config.text) + ], + ), + ), + ), + ); + } +} + +final class _ProposalStatusContainerConfig { + final IconData iconData; + final Color? iconColor; + final String text; + final Color? textColor; + final Color? backgroundColor; + + _ProposalStatusContainerConfig({ + required this.iconData, + this.iconColor, + required this.text, + this.textColor, + this.backgroundColor, + }); +} + +extension _ProposalStatusExt on ProposalStatus { + _ProposalStatusContainerConfig _config(BuildContext context) { + final colors = Theme.of(context).colors; + + return switch (this) { + ProposalStatus.ready => _ProposalStatusContainerConfig( + iconData: CatalystVoicesIcons.check, + iconColor: colors.iconsSuccess, + text: context.l10n.proposalStatusReady, + textColor: colors.textPrimary, + backgroundColor: colors.successContainer, + ), + ProposalStatus.draft => _ProposalStatusContainerConfig( + iconData: CatalystVoicesIcons.pencil_alt, + iconColor: colors.iconsForeground, + text: context.l10n.proposalStatusDraft, + textColor: colors.textPrimary, + backgroundColor: colors.onSurfaceNeutralOpaqueLv1, + ), + }; + } +} diff --git a/catalyst_voices/lib/widgets/common/proposal_status_indicator.dart b/catalyst_voices/lib/widgets/common/proposal_status_indicator.dart new file mode 100644 index 00000000000..baef0dad6fc --- /dev/null +++ b/catalyst_voices/lib/widgets/common/proposal_status_indicator.dart @@ -0,0 +1,90 @@ +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:flutter/material.dart'; + +class ProposalStatusIndicator extends StatelessWidget { + final ProposalStatus type; + + const ProposalStatusIndicator({ + super.key, + required this.type, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final config = type._config(context); + + final iconTheme = IconThemeData( + size: 18, + color: config.iconColor, + ); + + final textStyle = (theme.textTheme.labelLarge ?? TextStyle()).copyWith( + color: config.textColor, + ); + + return IconTheme( + data: iconTheme, + child: DefaultTextStyle( + style: textStyle, + child: Container( + padding: EdgeInsets.all(8).add(EdgeInsets.only(right: 4)), + decoration: BoxDecoration( + color: config.backgroundColor, + borderRadius: BorderRadius.circular(8), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(config.iconData), + SizedBox(width: 8), + Text(config.text) + ], + ), + ), + ), + ); + } +} + +final class _ProposalStatusIndicatorConfig { + final IconData iconData; + final Color? iconColor; + final String text; + final Color? textColor; + final Color? backgroundColor; + + _ProposalStatusIndicatorConfig({ + required this.iconData, + this.iconColor, + required this.text, + this.textColor, + this.backgroundColor, + }); +} + +extension _ProposalStatusExt on ProposalStatus { + _ProposalStatusIndicatorConfig _config(BuildContext context) { + final colors = Theme.of(context).colors; + + return switch (this) { + ProposalStatus.ready => _ProposalStatusIndicatorConfig( + iconData: CatalystVoicesIcons.check, + iconColor: colors.iconsSuccess, + text: context.l10n.proposalStatusReady, + textColor: colors.textPrimary, + backgroundColor: colors.successContainer, + ), + ProposalStatus.draft => _ProposalStatusIndicatorConfig( + iconData: CatalystVoicesIcons.pencil_alt, + iconColor: colors.iconsForeground, + text: context.l10n.proposalStatusDraft, + textColor: colors.textPrimary, + backgroundColor: colors.onSurfaceNeutralOpaqueLv1, + ), + }; + } +} diff --git a/catalyst_voices/lib/widgets/common/resizable_box_parent.dart b/catalyst_voices/lib/widgets/common/resizable_box_parent.dart new file mode 100644 index 00000000000..c435b863487 --- /dev/null +++ b/catalyst_voices/lib/widgets/common/resizable_box_parent.dart @@ -0,0 +1,122 @@ +import 'dart:math'; + +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:flutter/widgets.dart'; + +/// A parent component that adds ability to resize its child +class ResizableBoxParent extends StatelessWidget { + final bool resizableVertically; + final bool resizableHorizontally; + final Widget child; + final double minWidth; + final double minHeight; + + const ResizableBoxParent({ + required this.resizableVertically, + required this.resizableHorizontally, + required this.child, + this.minWidth = 40, + this.minHeight = 40, + }); + + @override + Widget build(BuildContext context) { + if (!resizableVertically && !resizableHorizontally) { + return child; + } + + return LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return _ResizableBox( + constraints: constraints, + child: child, + minWidth: minWidth, + minHeight: minHeight, + resizableHorizontally: resizableHorizontally, + resizableVertically: resizableVertically, + ); + }, + ); + } +} + +class _ResizableBox extends StatefulWidget { + final BoxConstraints constraints; + final Widget child; + final double minWidth; + final double minHeight; + final bool resizableVertically; + final bool resizableHorizontally; + + const _ResizableBox({ + required this.constraints, + required this.child, + required this.minWidth, + required this.minHeight, + required this.resizableVertically, + required this.resizableHorizontally, + }); + + @override + State<_ResizableBox> createState() => _ResizableBoxState(); +} + +class _ResizableBoxState extends State<_ResizableBox> { + late double _width; + late double _height; + + @override + void initState() { + super.initState(); + + if (widget.resizableHorizontally) { + _width = widget.constraints.maxWidth != double.infinity + ? widget.constraints.maxWidth + : widget.constraints.constrainWidth(widget.minWidth); + } else { + _width = double.infinity; + } + + _height = max(widget.constraints.minHeight, widget.minHeight); + } + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + SizedBox( + width: _width, + height: _height, + child: widget.child, + ), + Positioned( + bottom: 0, + right: 0, + child: MouseRegion( + cursor: SystemMouseCursors.resizeDownRight, + child: GestureDetector( + onPanUpdate: (details) { + setState(() { + if (widget.resizableHorizontally) { + _width = (_width + details.delta.dx).clamp( + widget.minWidth, + widget.constraints.maxWidth, + ); + } + + if (widget.resizableVertically) { + _height = (_height + details.delta.dy).clamp( + widget.minHeight, + widget.constraints.maxHeight, + ); + } + }); + }, + child: VoicesAssets.images.dragger.buildIcon(size: 15), + ), + ), + ), + ], + ); + } +} diff --git a/catalyst_voices/lib/widgets/common/simple_tree_view.dart b/catalyst_voices/lib/widgets/common/simple_tree_view.dart new file mode 100644 index 00000000000..d9b4009c3c4 --- /dev/null +++ b/catalyst_voices/lib/widgets/common/simple_tree_view.dart @@ -0,0 +1,203 @@ +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; +import 'package:flutter/material.dart'; + +// Note: +// This widget does not support multi-level nesting yet because +// it was too complex to implement while we'll have +// option from google (https://pub.dev/packages/two_dimensional_scrollables) +// which will be hopefully fixed by time we'll need more nesting. +class SimpleTreeView extends StatelessWidget { + final Widget root; + final List children; + final bool isExpanded; + + const SimpleTreeView({ + super.key, + this.isExpanded = false, + required this.root, + this.children = const [], + }); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + root, + if (isExpanded) ...children, + ], + ); + } +} + +class SimpleTreeViewRootRow extends StatelessWidget { + final List leading; + final VoidCallback? onTap; + final Widget child; + + const SimpleTreeViewRootRow({ + super.key, + this.leading = const [], + this.onTap, + required this.child, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final textTheme = theme.textTheme; + final color = theme.colorScheme.primary; + + final textStyle = (textTheme.titleSmall ?? const TextStyle()).copyWith( + color: color, + ); + + final iconTheme = IconThemeData(size: 24, color: color); + + return DefaultTextStyle( + style: textStyle, + maxLines: 1, + overflow: TextOverflow.ellipsis, + child: IconTheme( + data: iconTheme, + child: ConstrainedBox( + constraints: const BoxConstraints.tightFor(height: 32), + child: Material( + type: MaterialType.transparency, + child: InkWell( + onTap: onTap, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Row( + children: [ + ...leading, + Flexible(child: child), + ].separatedBy(const SizedBox(width: 4)).toList(), + ), + ), + ), + ), + ), + ), + ); + } +} + +class SimpleTreeViewChildRow extends StatelessWidget { + final bool hasNext; + final bool isSelected; + final VoidCallback? onTap; + final Widget child; + + const SimpleTreeViewChildRow({ + super.key, + this.hasNext = true, + this.isSelected = false, + this.onTap, + required this.child, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final textTheme = theme.textTheme; + + final backgroundColor = isSelected ? theme.colors.primaryContainer : null; + final foregroundColor = isSelected + ? theme.colors.onPrimaryContainer + : theme.colorScheme.onSurface; + + final textStyle = (textTheme.labelLarge ?? const TextStyle()).copyWith( + color: foregroundColor, + ); + + final dividerTheme = DividerThemeData( + color: foregroundColor, + ); + + return DefaultTextStyle( + style: textStyle, + maxLines: 1, + overflow: TextOverflow.ellipsis, + child: DividerTheme( + data: dividerTheme, + child: ConstrainedBox( + constraints: const BoxConstraints.tightFor(height: 40), + child: Material( + type: backgroundColor != null + ? MaterialType.canvas + : MaterialType.transparency, + color: backgroundColor, + child: InkWell( + onTap: onTap, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Row( + children: [ + _SimpleTreeViewIndent(showBottomJoint: hasNext), + Flexible(child: child), + ], + ), + ), + ), + ), + ), + ), + ); + } +} + +class _SimpleTreeViewIndent extends StatelessWidget { + final bool showBottomJoint; + + const _SimpleTreeViewIndent({ + this.showBottomJoint = true, + }); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 24) + .add(const EdgeInsets.symmetric(horizontal: 4)), + child: SizedBox( + width: 24, + child: _SimpleTreeViewIndentJoint(showBottom: showBottomJoint), + ), + ); + } +} + +class _SimpleTreeViewIndentJoint extends StatelessWidget { + final bool showBottom; + + const _SimpleTreeViewIndentJoint({ + this.showBottom = true, + }); + + @override + Widget build(BuildContext context) { + return Stack( + alignment: Alignment.center, + children: [ + Column( + children: [ + const Expanded(child: VerticalDivider()), + Expanded( + child: Offstage( + offstage: !showBottom, + child: const VerticalDivider(), + ), + ), + ], + ), + const Row( + children: [ + Spacer(), + Expanded(child: Divider(endIndent: 3)), + ], + ), + ], + ); + } +} diff --git a/catalyst_voices/lib/widgets/common/tab_bar_stack_view.dart b/catalyst_voices/lib/widgets/common/tab_bar_stack_view.dart new file mode 100644 index 00000000000..baeab1b47af --- /dev/null +++ b/catalyst_voices/lib/widgets/common/tab_bar_stack_view.dart @@ -0,0 +1,148 @@ +import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; + +/// This widget is very similar to [TabBarView], and is meant to be used +/// together with [TabBar], but displays children in non scrollable fashion +/// thanks to [IndexedStack]. +class TabBarStackView extends StatefulWidget { + /// This widget's selection and animation state. + /// + /// If [TabController] is not provided, then the value of + /// [DefaultTabController.of] will be used. + final TabController? controller; + + /// One widget per tab. + /// + /// Its length must match the length of the [TabBar.tabs] + /// list, as well as the [controller]'s [TabController.length]. + final List children; + + const TabBarStackView({ + super.key, + this.controller, + required this.children, + }); + + @override + State createState() => _TabBarStackViewState(); +} + +class _TabBarStackViewState extends State { + TabController? _controller; + + int? _currentIndex; + + // If the TabBarView is rebuilt with a new tab controller, the caller should + // dispose the old one. In that case the old controller's animation will be + // null and should not be accessed. + bool get _controllerIsValid => _controller?.animation != null; + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + _updateTabController(); + _currentIndex = _controller!.index; + } + + @override + void didUpdateWidget(TabBarStackView oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.controller != oldWidget.controller) { + _updateTabController(); + _currentIndex = _controller!.index; + } + } + + @override + void dispose() { + if (_controllerIsValid) { + _controller!.animation!.removeListener(_handleTabControllerAnimationTick); + } + _controller = null; + // We don't own the _controller Animation, so it's not disposed here. + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return _TabsBodyContainer( + currentIndex: _currentIndex, + children: widget.children, + ); + } + + void _updateTabController() { + final oldController = _controller; + final newController = + widget.controller ?? DefaultTabController.maybeOf(context); + + assert( + newController != null, + 'No TabController for ${widget.runtimeType}.\n' + 'When creating a ${widget.runtimeType}, you must either provide an ' + 'explicit ' + 'TabController using the "controller" property, or you must ensure that ' + 'there ' + 'is a DefaultTabController above the ${widget.runtimeType}.\n' + 'In this case, there was neither an explicit controller nor a default ' + 'controller.', + ); + + if (newController == oldController) { + return; + } + + if (_controllerIsValid) { + oldController!.animation! + .removeListener(_handleTabControllerAnimationTick); + } + + _controller = newController; + + if (newController != null) { + newController.animation!.addListener(_handleTabControllerAnimationTick); + } + } + + void _handleTabControllerAnimationTick() { + if (!_controller!.indexIsChanging) { + return; + } + + if (_controller!.index != _currentIndex) { + setState(() { + _currentIndex = _controller!.index; + }); + } + } +} + +class _TabsBodyContainer extends StatelessWidget { + final int? currentIndex; + final List children; + + const _TabsBodyContainer({ + this.currentIndex, + required this.children, + }); + + @override + Widget build(BuildContext context) { + return IndexedStack( + index: currentIndex, + children: children.mapIndexed( + (index, child) { + final isCurrent = index == currentIndex; + + return Offstage( + offstage: !isCurrent, + child: TickerMode( + enabled: isCurrent, + child: child, + ), + ); + }, + ).toList(), + ); + } +} diff --git a/catalyst_voices/lib/widgets/common/tree_view_column.dart b/catalyst_voices/lib/widgets/common/tree_view_column.dart new file mode 100644 index 00000000000..e69de29bb2d diff --git a/catalyst_voices/lib/widgets/containers/sidebar_scaffold.dart b/catalyst_voices/lib/widgets/containers/sidebar_scaffold.dart new file mode 100644 index 00000000000..14ed50f26c7 --- /dev/null +++ b/catalyst_voices/lib/widgets/containers/sidebar_scaffold.dart @@ -0,0 +1,51 @@ +import 'package:catalyst_voices/widgets/containers/space_side_panel.dart'; +import 'package:flutter/material.dart'; + +/// Widget that creates 'rails' screen structure where two side panels +/// can be added with main content between them. +/// +/// |panel | content |panel|. +/// +/// Usually [leftRail] and [rightRail] are used with [SpaceSidePanel]. +/// +/// At the moment we do not have defined behaviour when we don't have +/// sufficient screen width for all content. Medium and small screens behaviour +/// will be implemented later. Right now only desktops are focused. +class SidebarScaffold extends StatelessWidget { + final Widget leftRail; + final Widget rightRail; + final double railWidth; + final double railsGap; + final double childMaxWidth; + final Widget child; + + const SidebarScaffold({ + super.key, + this.leftRail = const SizedBox(), + this.rightRail = const SizedBox(), + this.railWidth = 326, + this.railsGap = 56, + this.childMaxWidth = 612, + required this.child, + }); + + @override + Widget build(BuildContext context) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ConstrainedBox( + constraints: BoxConstraints.tightFor(width: railWidth), + child: leftRail, + ), + SizedBox(width: railsGap), + Expanded(child: child), + SizedBox(width: railsGap), + ConstrainedBox( + constraints: BoxConstraints.tightFor(width: railWidth), + child: rightRail, + ), + ], + ); + } +} diff --git a/catalyst_voices/lib/widgets/containers/space_scaffold.dart b/catalyst_voices/lib/widgets/containers/space_scaffold.dart new file mode 100644 index 00000000000..8d209f4d4da --- /dev/null +++ b/catalyst_voices/lib/widgets/containers/space_scaffold.dart @@ -0,0 +1,37 @@ +import 'package:catalyst_voices/widgets/containers/sidebar_scaffold.dart'; +import 'package:catalyst_voices/widgets/containers/space_side_panel.dart'; +import 'package:flutter/material.dart'; + +/// Space screen structure implementation. This widget +/// does not require any specific child types but +/// is common to use [SpaceSidePanel] as [left] and [right]. +/// +/// Only difference from [SidebarScaffold] is that main content, [child], +/// has [maxWidth] so it does not expand indefinitely but spacing +/// between [child] and [left],[right] does. +class SpaceScaffold extends StatelessWidget { + final Widget left; + final Widget right; + final Widget child; + + const SpaceScaffold({ + super.key, + required this.left, + required this.right, + required this.child, + }); + + @override + Widget build(BuildContext context) { + return SidebarScaffold( + leftRail: left, + rightRail: right, + child: Center( + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 612), + child: child, + ), + ), + ); + } +} diff --git a/catalyst_voices/lib/widgets/containers/space_side_panel.dart b/catalyst_voices/lib/widgets/containers/space_side_panel.dart new file mode 100644 index 00000000000..7e5607e64b7 --- /dev/null +++ b/catalyst_voices/lib/widgets/containers/space_side_panel.dart @@ -0,0 +1,145 @@ +import 'package:catalyst_voices/widgets/buttons/voices_buttons.dart'; +import 'package:catalyst_voices/widgets/common/tab_bar_stack_view.dart'; +import 'package:catalyst_voices/widgets/containers/space_scaffold.dart'; +import 'package:catalyst_voices/widgets/headers/section_header.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:flutter/material.dart'; + +/// Defines [Tab] inside [SpaceSidePanel]. +class SpaceSidePanelTab { + /// Displayed label for this tab. + final String name; + + /// What is shown when this tab is selected. + // Note. This maybe should be [WidgetBuilder]. + final Widget body; + + SpaceSidePanelTab({ + required this.name, + required this.body, + }); +} + +/// Defines usual space panel. This widget is opinionated and should +/// be used together with [SpaceScaffold]. +/// +/// Always have [name], [tabs] and tabs content [SpaceSidePanelTab.body]. +class SpaceSidePanel extends StatelessWidget { + final bool isLeft; + final String name; + final VoidCallback? onCollapseTap; + final TabController? tabController; + final List tabs; + final EdgeInsetsGeometry margin; + + const SpaceSidePanel({ + super.key, + required this.isLeft, + required this.name, + this.onCollapseTap, + this.tabController, + required this.tabs, + this.margin = const EdgeInsets.only(top: 10), + }); + + @override + Widget build(BuildContext context) { + return _Container( + margin: margin, + borderRadius: isLeft + ? const BorderRadius.horizontal(right: Radius.circular(16)) + : const BorderRadius.horizontal(left: Radius.circular(16)), + child: DefaultTabController( + length: tabs.length, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + _Header( + name: name, + onCollapseTap: onCollapseTap, + isLeft: isLeft, + ), + _Tabs( + tabs, + controller: tabController, + ), + const SizedBox(height: 12), + TabBarStackView( + controller: tabController, + children: tabs.map((e) => e.body).toList(), + ), + const SizedBox(height: 12), + ], + ), + ), + ); + } +} + +class _Container extends StatelessWidget { + final EdgeInsetsGeometry margin; + final BorderRadiusGeometry borderRadius; + final Widget child; + + const _Container({ + required this.margin, + required this.borderRadius, + required this.child, + }); + + @override + Widget build(BuildContext context) { + return Container( + margin: margin, + decoration: BoxDecoration( + color: Theme.of(context).colors.onSurfaceNeutralOpaqueLv0, + borderRadius: borderRadius, + ), + padding: const EdgeInsets.all(12), + child: child, + ); + } +} + +class _Header extends StatelessWidget { + final String name; + final VoidCallback? onCollapseTap; + final bool isLeft; + + const _Header({ + required this.name, + this.onCollapseTap, + required this.isLeft, + }); + + @override + Widget build(BuildContext context) { + return SectionHeader( + leading: isLeft ? LeftArrowButton(onTap: onCollapseTap) : null, + title: Text(name), + trailing: [ + if (!isLeft) RightArrowButton(onTap: onCollapseTap), + ], + ); + } +} + +class _Tabs extends StatelessWidget { + final TabController? controller; + final List tabs; + + const _Tabs( + this.tabs, { + this.controller, + }); + + @override + Widget build(BuildContext context) { + return TabBar( + controller: controller, + isScrollable: true, + tabAlignment: TabAlignment.start, + tabs: tabs.map((e) => Tab(text: e.name)).toList(), + ); + } +} diff --git a/catalyst_voices/lib/widgets/containers/workspace_tile_container.dart b/catalyst_voices/lib/widgets/containers/workspace_tile_container.dart new file mode 100644 index 00000000000..e07e6c9e348 --- /dev/null +++ b/catalyst_voices/lib/widgets/containers/workspace_tile_container.dart @@ -0,0 +1,121 @@ +import 'package:catalyst_voices/widgets/headers/segment_header.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:flutter/material.dart'; + +/// Opinionated container usual used inside space main body. +class WorkspaceTileContainer extends StatelessWidget { + final bool isSelected; + final String name; + final List headerActions; + final Widget content; + final Widget? footer; + + const WorkspaceTileContainer({ + super.key, + this.isSelected = false, + required this.name, + this.headerActions = const [], + required this.content, + this.footer, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + return AnimatedContainer( + duration: kThemeChangeDuration, + decoration: BoxDecoration( + color: theme.colorScheme.onPrimary, + borderRadius: BorderRadius.horizontal( + left: isSelected ? Radius.zero : Radius.circular(28), + right: Radius.circular(28), + ), + boxShadow: theme.brightness == Brightness.light + ? [ + BoxShadow( + color: Color(0x1F123CD3), + offset: Offset(0, 1), + blurRadius: 8, + ), + ] + : null, + ), + foregroundDecoration: BoxDecoration( + border: Border( + left: isSelected + ? BorderSide(color: theme.colorScheme.primary, width: 5) + : BorderSide.none, + ), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _Header(name, headerActions), + _Content(child: content), + _Footer(child: footer), + ], + ), + ); + } +} + +class _Header extends StatelessWidget { + final String name; + final List actions; + + _Header(this.name, this.actions); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 4), + child: SegmentHeader( + name: name, + actions: actions, + ), + ); + } +} + +class _Content extends StatelessWidget { + final Widget child; + + const _Content({ + required this.child, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + final style = (theme.textTheme.bodyMedium ?? TextStyle()).copyWith( + color: theme.colors.textOnPrimary, + ); + + return DefaultTextStyle( + style: style, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), + child: child, + ), + ); + } +} + +class _Footer extends StatelessWidget { + final Widget? child; + + const _Footer({ + this.child, + }); + + @override + Widget build(BuildContext context) { + return ConstrainedBox( + constraints: BoxConstraints(minHeight: 16), + child: child, + ); + } +} diff --git a/catalyst_voices/lib/widgets/drawer/voices_drawer.dart b/catalyst_voices/lib/widgets/drawer/voices_drawer.dart index c46471c1e92..8f336a4b5b5 100644 --- a/catalyst_voices/lib/widgets/drawer/voices_drawer.dart +++ b/catalyst_voices/lib/widgets/drawer/voices_drawer.dart @@ -81,9 +81,7 @@ class _Header extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - CatalystSvgPicture.asset( - Theme.of(context).brandAssets.logo.path, - ), + Theme.of(context).brandAssets.logo.buildPicture(), IconButton( onPressed: Navigator.of(context).pop, icon: const Icon(CatalystVoicesIcons.x, size: 22), diff --git a/catalyst_voices/lib/widgets/drawer/voices_drawer_nav_item.dart b/catalyst_voices/lib/widgets/drawer/voices_drawer_nav_item.dart new file mode 100644 index 00000000000..3dc17d2c6c0 --- /dev/null +++ b/catalyst_voices/lib/widgets/drawer/voices_drawer_nav_item.dart @@ -0,0 +1,57 @@ +import 'package:catalyst_voices/widgets/buttons/voices_icon_button.dart'; +import 'package:catalyst_voices/widgets/common/proposal_status_container.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; +import 'package:flutter/material.dart'; + +class VoicesDrawerNavItem extends StatelessWidget { + final String name; + final ProposalStatus status; + + const VoicesDrawerNavItem({ + super.key, + required this.name, + required this.status, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + final iconButtonStyle = ButtonStyle( + fixedSize: WidgetStatePropertyAll(Size.square(48)), + ); + + final nameTextStyle = theme.textTheme.labelLarge?.copyWith( + color: theme.colors.textPrimary, + ); + + return IconButtonTheme( + data: IconButtonThemeData(style: iconButtonStyle), + child: Container( + constraints: BoxConstraints(minHeight: 56), + padding: EdgeInsets.only(left: 16), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Text( + name, + style: nameTextStyle, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ), + ProposalStatusContainer(type: status), + VoicesIconButton( + child: Icon(CatalystVoicesIcons.dots_vertical), + onTap: () {}, + ), + ].separatedBy(SizedBox(width: 12)).toList(), + ), + ), + ); + } +} diff --git a/catalyst_voices/lib/widgets/drawer/voices_drawer_space_chooser.dart b/catalyst_voices/lib/widgets/drawer/voices_drawer_space_chooser.dart new file mode 100644 index 00000000000..3ae36fb488d --- /dev/null +++ b/catalyst_voices/lib/widgets/drawer/voices_drawer_space_chooser.dart @@ -0,0 +1,94 @@ +import 'package:catalyst_voices/widgets/drawer/voices_drawer.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:flutter/material.dart'; + +class VoicesDrawerSpaceChooser extends StatelessWidget { + final Space currentSpace; + final ValueChanged onChanged; + + const VoicesDrawerSpaceChooser({ + super.key, + required this.currentSpace, + required this.onChanged, + }); + + @override + Widget build(BuildContext context) { + return VoicesDrawerChooser( + items: Space.values, + selectedItem: currentSpace, + onSelected: onChanged, + itemBuilder: _itemBuilder, + leading: IconButton( + icon: const Icon(CatalystVoicesIcons.all_spaces_menu, size: 20), + onPressed: () {}, + ), + ); + } + + Widget _itemBuilder({ + required BuildContext context, + required Space item, + required bool isSelected, + }) { + if (isSelected) { + final config = item._config(context); + + return VoicesDrawerChooserItem( + icon: config.iconData, + foregroundColor: config.foregroundColor, + backgroundColor: config.backgroundColor, + ); + } else { + return const VoicesDrawerChooserItemPlaceholder(); + } + } +} + +final class _VoicesDrawerSpaceChooserConfig { + final IconData iconData; + final Color backgroundColor; + final Color foregroundColor; + + _VoicesDrawerSpaceChooserConfig({ + required this.iconData, + required this.backgroundColor, + required this.foregroundColor, + }); +} + +extension _SpaceExt on Space { + _VoicesDrawerSpaceChooserConfig _config(BuildContext context) { + final theme = Theme.of(context); + + return switch (this) { + Space.treasury => _VoicesDrawerSpaceChooserConfig( + iconData: CatalystVoicesIcons.fund, + backgroundColor: theme.colors.successContainer!, + foregroundColor: theme.colors.iconsSuccess!, + ), + Space.discovery => _VoicesDrawerSpaceChooserConfig( + iconData: CatalystVoicesIcons.light_bulb, + backgroundColor: theme.colors.iconsSecondary!.withOpacity(0.16), + foregroundColor: theme.colors.iconsSecondary!, + ), + Space.workspace => _VoicesDrawerSpaceChooserConfig( + iconData: CatalystVoicesIcons.briefcase, + backgroundColor: theme.colorScheme.primaryContainer, + foregroundColor: theme.colorScheme.primary, + ), + Space.voting => _VoicesDrawerSpaceChooserConfig( + iconData: CatalystVoicesIcons.vote, + backgroundColor: theme.colors.warningContainer!, + foregroundColor: theme.colors.iconsWarning!, + ), + Space.fundedProjects => _VoicesDrawerSpaceChooserConfig( + iconData: CatalystVoicesIcons.flag, + backgroundColor: theme.colors.iconsSecondary!.withOpacity(0.16), + foregroundColor: theme.colors.iconsSecondary!, + ), + }; + } +} diff --git a/catalyst_voices/lib/widgets/footers/links_page_footer.dart b/catalyst_voices/lib/widgets/footers/links_page_footer.dart new file mode 100644 index 00000000000..0d138c84504 --- /dev/null +++ b/catalyst_voices/lib/widgets/footers/links_page_footer.dart @@ -0,0 +1,65 @@ +import 'package:catalyst_voices/widgets/footers/page_footer.dart'; +import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; +import 'package:flutter/material.dart'; + +class LinksPageFooter extends StatelessWidget { + final List upperChildren; + final List lowerChildren; + + const LinksPageFooter({ + super.key, + required this.upperChildren, + required this.lowerChildren, + }) : assert( + upperChildren.length > 0 || lowerChildren.length > 0, + 'Make sure links page footer is not empty!', + ); + + @override + Widget build(BuildContext context) { + return DefaultTextStyle( + style: Theme.of(context).textTheme.bodyLarge ?? const TextStyle(), + child: IconButtonTheme( + data: const IconButtonThemeData( + style: ButtonStyle( + fixedSize: WidgetStatePropertyAll(Size.square(48)), + ), + ), + child: PageFooter( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Wrap( + alignment: WrapAlignment.center, + runSpacing: 8, + children: + upperChildren.separatedBy(const _ResponsiveGap()).toList(), + ), + const SizedBox(height: 24), + Wrap( + alignment: WrapAlignment.center, + runSpacing: 8, + children: + lowerChildren.separatedBy(const _ResponsiveGap()).toList(), + ), + ], + ), + ), + ), + ); + } +} + +class _ResponsiveGap extends StatelessWidget { + const _ResponsiveGap(); + + @override + Widget build(BuildContext context) { + return ResponsiveBuilder( + builder: (context, data) => SizedBox(width: data), + lg: 32, + other: 16, + ); + } +} diff --git a/catalyst_voices/lib/widgets/footers/page_footer.dart b/catalyst_voices/lib/widgets/footers/page_footer.dart new file mode 100644 index 00000000000..2fd0c1515f4 --- /dev/null +++ b/catalyst_voices/lib/widgets/footers/page_footer.dart @@ -0,0 +1,23 @@ +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:flutter/material.dart'; + +class PageFooter extends StatelessWidget { + final Widget child; + + const PageFooter({ + super.key, + required this.child, + }); + + @override + Widget build(BuildContext context) { + return Container( + constraints: const BoxConstraints(minHeight: 160), + decoration: BoxDecoration( + color: Theme.of(context).colors.onSurfaceNeutralOpaqueLv2, + ), + padding: const EdgeInsets.symmetric(vertical: 32, horizontal: 16), + child: child, + ); + } +} diff --git a/catalyst_voices/lib/widgets/footers/standard_links_page_footer.dart b/catalyst_voices/lib/widgets/footers/standard_links_page_footer.dart new file mode 100644 index 00000000000..e5f982340d1 --- /dev/null +++ b/catalyst_voices/lib/widgets/footers/standard_links_page_footer.dart @@ -0,0 +1,69 @@ +import 'package:catalyst_voices/widgets/buttons/voices_icon_button.dart'; +import 'package:catalyst_voices/widgets/common/link_text.dart'; +import 'package:catalyst_voices/widgets/footers/links_page_footer.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:flutter/material.dart'; + +class StandardLinksPageFooter extends StatelessWidget { + const StandardLinksPageFooter({super.key}); + + @override + Widget build(BuildContext context) { + // TODO(damian): implement proper routing actions once we have them + return LinksPageFooter( + upperChildren: [ + LinkText( + 'About us', + onTap: () { + ScaffoldMessenger.of(context) + .showSnackBar(const SnackBar(content: Text('About us'))); + }, + ), + LinkText( + 'Funds', + onTap: () { + ScaffoldMessenger.of(context) + .showSnackBar(const SnackBar(content: Text('Funds'))); + }, + ), + LinkText( + 'Documentation', + onTap: () { + ScaffoldMessenger.of(context) + .showSnackBar(const SnackBar(content: Text('Documentation'))); + }, + ), + LinkText( + 'Contact us', + onTap: () { + ScaffoldMessenger.of(context) + .showSnackBar(const SnackBar(content: Text('Contact us'))); + }, + ), + ], + lowerChildren: [ + VoicesIconButton( + child: VoicesAssets.images.facebookMono.buildIcon(), + onTap: () { + ScaffoldMessenger.of(context) + .showSnackBar(const SnackBar(content: Text('Facebook'))); + }, + ), + VoicesIconButton( + child: VoicesAssets.images.linkedinMono.buildIcon(), + onTap: () { + ScaffoldMessenger.of(context) + .showSnackBar(const SnackBar(content: Text('LinkedIn'))); + }, + ), + VoicesIconButton( + child: VoicesAssets.images.xMono.buildIcon(), + onTap: () { + ScaffoldMessenger.of(context) + .showSnackBar(const SnackBar(content: Text('X'))); + }, + ), + ], + ); + } +} diff --git a/catalyst_voices/lib/widgets/headers/section_header.dart b/catalyst_voices/lib/widgets/headers/section_header.dart new file mode 100644 index 00000000000..77c7a4e1229 --- /dev/null +++ b/catalyst_voices/lib/widgets/headers/section_header.dart @@ -0,0 +1,59 @@ +import 'package:catalyst_voices/widgets/buttons/voices_icon_button.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:flutter/material.dart'; + +/// Reusable header widget. Common use case it to have [Text] as [title] +/// and [VoicesIconButton] as [leading] or [trailing]. +class SectionHeader extends StatelessWidget { + /// Leading widget, typically a button or icon. + final Widget? leading; + + /// Title of the section. + final Widget title; + + /// Creates a new SectionHeader widget. + final List trailing; + + const SectionHeader({ + super.key, + this.leading, + required this.title, + this.trailing = const [], + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + const iconButtonStyle = ButtonStyle( + fixedSize: WidgetStatePropertyAll(Size.square(48)), + ); + + final textStyle = (theme.textTheme.titleSmall ?? const TextStyle()) + .copyWith(color: theme.colors.textOnPrimary); + + return IconButtonTheme( + data: const IconButtonThemeData(style: iconButtonStyle), + child: DefaultTextStyle( + style: textStyle, + maxLines: 1, + overflow: TextOverflow.ellipsis, + child: Container( + constraints: const BoxConstraints.tightFor(height: 56), + padding: const EdgeInsets.symmetric(horizontal: 6) + .add(const EdgeInsets.only(left: 6)), + child: Row( + children: [ + if (leading != null) leading!, + Expanded(child: title), + if (trailing.isNotEmpty) ...[ + const SizedBox(width: 12), + ...trailing, + ], + ], + ), + ), + ), + ); + } +} diff --git a/catalyst_voices/lib/widgets/headers/segment_header.dart b/catalyst_voices/lib/widgets/headers/segment_header.dart new file mode 100644 index 00000000000..074e5f34cb0 --- /dev/null +++ b/catalyst_voices/lib/widgets/headers/segment_header.dart @@ -0,0 +1,58 @@ +import 'package:flutter/material.dart'; + +class SegmentHeader extends StatelessWidget { + final String name; + final Widget? leading; + final List actions; + final bool isHighlighted; + + const SegmentHeader({ + super.key, + required this.name, + this.leading, + this.actions = const [], + this.isHighlighted = false, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final colors = theme.colorScheme; + + final backgroundColor = isHighlighted ? colors.primary : null; + final foregroundColor = isHighlighted ? colors.onPrimary : colors.primary; + + final iconButtonStyle = ButtonStyle( + fixedSize: WidgetStatePropertyAll(Size.square(48)), + iconColor: WidgetStatePropertyAll(foregroundColor), + ); + + final textStyle = (theme.textTheme.titleMedium ?? TextStyle()) + .copyWith(color: foregroundColor); + + return IconButtonTheme( + data: IconButtonThemeData(style: iconButtonStyle), + child: AnimatedDefaultTextStyle( + duration: kThemeChangeDuration, + style: textStyle, + maxLines: 1, + overflow: TextOverflow.ellipsis, + child: AnimatedContainer( + duration: kThemeChangeDuration, + constraints: BoxConstraints(minHeight: 52), + decoration: BoxDecoration(color: backgroundColor), + padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + if (leading != null) leading!, + Expanded(child: Text(name)), + if (actions.isNotEmpty) ...actions + ], + ), + ), + ), + ); + } +} diff --git a/catalyst_voices/lib/widgets/indicators/process_progress_indicator.dart b/catalyst_voices/lib/widgets/indicators/process_progress_indicator.dart new file mode 100644 index 00000000000..f67ab22c08d --- /dev/null +++ b/catalyst_voices/lib/widgets/indicators/process_progress_indicator.dart @@ -0,0 +1,339 @@ +import 'package:catalyst_voices/widgets/separators/voices_vertical_divider.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; + +/// A class representing a step in a progress indicator. +/// +/// Generic type [T] represents the value associated with the step. +class ProcessProgressStep { + /// The value associated with the step. + final T value; + + /// The name of the step. + final String name; + + ProcessProgressStep({ + required this.value, + required this.name, + }); +} + +/// A widget that displays a progress indicator for a series of steps. +/// +/// Generic type [T] represents the value associated with each step. +/// +/// Example usage: +/// ```dart +/// ProcessProgressIndicator( +/// steps: [ +/// ProcessProgressStep(value: 1, name: 'Step 1'), +/// ProcessProgressStep(value: 2, name: 'Step 2'), +/// ProcessProgressStep(value: 3, name: 'Step 3'), +/// ], +/// completed: {1, 2}, +/// current: 2, +/// ) +/// ``` +class ProcessProgressIndicator extends StatelessWidget { + /// The list of steps in the indicator. + final List> steps; + + /// The set of completed steps. + final Set completed; + + /// The current step. + final T? current; + + const ProcessProgressIndicator({ + super.key, + required this.steps, + this.completed = const {}, + this.current, + }); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: steps.mapIndexed( + (index, step) { + final isFirst = index == 0; + final isLast = index == steps.length - 1; + final isCompleted = completed.contains(step.value); + final isCurrent = current == step.value; + + final type = isLast + ? _StepIndicatorType.completes + : _StepIndicatorType.standard; + final status = isCompleted + ? _StepIndicatorStatus.completed + : isCurrent + ? _StepIndicatorStatus.current + : _StepIndicatorStatus.upcoming; + + return _StepRow( + key: ValueKey('Step${step.value}RowKey'), + name: step.name, + type: type, + status: status, + neighborhood: (previous: !isFirst, next: !isLast), + ); + }, + ).toList(), + ); + } +} + +enum _StepIndicatorType { + standard, + completes; + + bool get isStandard => this == _StepIndicatorType.standard; + + bool get doesCompletes => this == _StepIndicatorType.completes; +} + +enum _StepIndicatorStatus { + completed, + current, + upcoming; + + bool get isCompleted => this == _StepIndicatorStatus.completed; + + bool get isCurrent => this == _StepIndicatorStatus.current; + + bool get isUpcoming => this == _StepIndicatorStatus.upcoming; +} + +class _StepRow extends StatelessWidget { + const _StepRow({ + super.key, + required this.name, + required this.type, + required this.status, + required this.neighborhood, + }); + + final String name; + final _StepIndicatorType type; + final _StepIndicatorStatus status; + final ({bool previous, bool next}) neighborhood; + + @override + Widget build(BuildContext context) { + return Container( + constraints: !neighborhood.previous || !neighborhood.next + ? const BoxConstraints.tightFor(height: 60) + : const BoxConstraints.tightFor(height: 50), + child: Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + _StepStatusIndicatorContainer( + isExpanded: type.doesCompletes, + neighborhood: neighborhood, + child: _StepStatusIndicator( + type: type, + status: status, + ), + ), + _StepNameTextContainer( + name, + type: type, + status: status, + neighborhood: neighborhood, + ), + const SizedBox(width: 10), + ], + ), + ); + } +} + +class _StepNameTextContainer extends StatelessWidget { + const _StepNameTextContainer( + this.data, { + required this.type, + required this.status, + required this.neighborhood, + }); + + final String data; + final _StepIndicatorType type; + final _StepIndicatorStatus status; + final ({bool previous, bool next}) neighborhood; + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final textTheme = theme.textTheme; + final colors = theme.colors; + + return Container( + padding: !neighborhood.previous + ? const EdgeInsets.only(top: 14) + : !neighborhood.next + ? const EdgeInsets.only(bottom: 10) + : null, + alignment: !neighborhood.previous + ? Alignment.topCenter + : !neighborhood.next + ? Alignment.bottomCenter + : Alignment.center, + child: Text( + data, + style: switch (status) { + _StepIndicatorStatus.current => + textTheme.titleSmall?.copyWith(color: colors.textOnPrimary), + _StepIndicatorStatus.completed when type.doesCompletes => + textTheme.titleSmall?.copyWith(color: colors.success), + _StepIndicatorStatus.completed || + _StepIndicatorStatus.upcoming => + textTheme.bodyMedium?.copyWith(color: colors.textOnPrimary), + }, + ), + ); + } +} + +class _StepStatusIndicatorContainer extends StatelessWidget { + final bool isExpanded; + final ({bool previous, bool next}) neighborhood; + final Widget child; + + const _StepStatusIndicatorContainer({ + this.isExpanded = false, + required this.neighborhood, + required this.child, + }); + + @override + Widget build(BuildContext context) { + EdgeInsetsGeometry padding = isExpanded + ? const EdgeInsets.only(left: 4, right: 10) + : const EdgeInsets.only(left: 10, right: 16); + + if (!neighborhood.previous) { + padding = padding.add(const EdgeInsets.only(top: 10)); + } + + return Container( + constraints: const BoxConstraints.tightFor(width: 56), + padding: padding, + alignment: Alignment.center, + child: Column( + children: [ + if (neighborhood.previous) + const Expanded(child: VoicesVerticalDivider()), + ConstrainedBox( + constraints: const BoxConstraints(minWidth: 50, maxWidth: 60), + child: child, + ), + if (neighborhood.next) const Expanded(child: VoicesVerticalDivider()), + ], + ), + ); + } +} + +class _StepStatusIndicator extends StatelessWidget { + final _StepIndicatorType type; + final _StepIndicatorStatus status; + + const _StepStatusIndicator({ + required this.type, + required this.status, + }); + + @override + Widget build(BuildContext context) { + return AnimatedContainer( + constraints: BoxConstraints.tight(_buildSize()), + duration: const Duration(milliseconds: 300), + decoration: BoxDecoration( + color: status.isCompleted + ? Theme.of(context).colors.successContainer + : null, + border: _buildBorder(context), + shape: BoxShape.circle, + ), + alignment: Alignment.center, + child: switch (type) { + _StepIndicatorType.standard when status.isCurrent => + const _CurrentStepDot(), + _StepIndicatorType.standard when status.isCompleted => + const _StepIcon(), + _StepIndicatorType.completes when status.isCompleted => + const _StepIcon(showFlag: true), + _StepIndicatorType.completes => + const _StepIcon(showFlag: true, isCompleted: false), + _StepIndicatorType.standard => null, + }, + ); + } + + Size _buildSize() { + return switch (type) { + _StepIndicatorType.standard => status == _StepIndicatorStatus.current + ? const Size.square(24) + : const Size.square(30), + _StepIndicatorType.completes => const Size.square(40), + }; + } + + BoxBorder? _buildBorder(BuildContext context) { + if (status.isCompleted) { + return null; + } + + return switch (type) { + _StepIndicatorType.standard => Border.all( + color: Theme.of(context).colorScheme.onSurface, + ), + _StepIndicatorType.completes => Border.all( + color: Theme.of(context).colorScheme.onSurface, + width: status.isCurrent ? 2 : 1, + ), + }; + } +} + +class _CurrentStepDot extends StatelessWidget { + const _CurrentStepDot(); + + @override + Widget build(BuildContext context) { + return Container( + constraints: BoxConstraints.tight(const Size.square(10)), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.primary, + shape: BoxShape.circle, + ), + ); + } +} + +class _StepIcon extends StatelessWidget { + final bool showFlag; + final bool isCompleted; + + const _StepIcon({ + this.showFlag = false, + this.isCompleted = true, + }); + + @override + Widget build(BuildContext context) { + return IconTheme( + data: IconThemeData( + color: isCompleted + ? Theme.of(context).colors.iconsSuccess + : Theme.of(context).colors.iconsForeground, + ), + child: + showFlag ? const Icon(Icons.flag_outlined) : const Icon(Icons.check), + ); + } +} diff --git a/catalyst_voices/lib/widgets/indicators/voices_circular_progress_indicator.dart b/catalyst_voices/lib/widgets/indicators/voices_circular_progress_indicator.dart new file mode 100644 index 00000000000..c3c67e746af --- /dev/null +++ b/catalyst_voices/lib/widgets/indicators/voices_circular_progress_indicator.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; + +/// A Voices circular progress indicator with optional track visibility. +/// +/// This widget provides a circular progress indicator with customization +/// options for the progress value and track visibility. +class VoicesCircularProgressIndicator extends StatelessWidget { + /// The current progress value, from 0.0 to 1.0. If null, + /// the indicator will be indeterminate. + final double? value; + + /// Whether to show the progress indicator's track. + final bool showTrack; + + /// Creates a [VoicesCircularProgressIndicator] widget. + const VoicesCircularProgressIndicator({ + super.key, + this.value, + this.showTrack = true, + }); + + @override + Widget build(BuildContext context) { + return CircularProgressIndicator( + value: value, + strokeCap: StrokeCap.round, + backgroundColor: showTrack ? null : Colors.transparent, + ); + } +} diff --git a/catalyst_voices/lib/widgets/indicators/voices_linear_progress_indicator.dart b/catalyst_voices/lib/widgets/indicators/voices_linear_progress_indicator.dart new file mode 100644 index 00000000000..5c16f5cd00a --- /dev/null +++ b/catalyst_voices/lib/widgets/indicators/voices_linear_progress_indicator.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; + +/// A custom linear progress indicator with optional track visibility and +/// rounded corners. +/// +/// This widget provides a linear progress indicator with customization +/// options for the progress value, track visibility, and rounded corners. +class VoicesLinearProgressIndicator extends StatelessWidget { + /// The current progress value, from 0.0 to 1.0. If null, the indicator will + /// be indeterminate. + final double? value; + + /// Whether to show the progress indicator's track. + final bool showTrack; + + /// Creates a [VoicesLinearProgressIndicator] widget. + const VoicesLinearProgressIndicator({ + super.key, + this.value, + this.showTrack = true, + }); + + @override + Widget build(BuildContext context) { + return LinearProgressIndicator( + value: value, + borderRadius: BorderRadius.circular(4), + backgroundColor: showTrack ? null : Colors.transparent, + ); + } +} diff --git a/catalyst_voices/lib/widgets/indicators/voices_status_indicator.dart b/catalyst_voices/lib/widgets/indicators/voices_status_indicator.dart new file mode 100644 index 00000000000..23ab44f899e --- /dev/null +++ b/catalyst_voices/lib/widgets/indicators/voices_status_indicator.dart @@ -0,0 +1,148 @@ +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; +import 'package:flutter/material.dart'; + +/// Enum representing the two possible types of status indicators: +/// success or error. +/// +/// Type will result in different status colors. +enum VoicesStatusIndicatorType { success, error } + +/// A widget that displays a visual indicator of a status along with a +/// title and body text. +/// +/// Uses different colors and icons for success and error states. +/// +/// Example: +/// ```dart +/// VoicesStatusIndicator( +/// status: AffixDecorator( +/// prefix: Icon(Icons.check), +/// child: Text('QR VERIFIED'), +/// ), +/// title: Text('Your QR code verified successfully'), +/// body: Text('You can now use your QR-code 
to login into Catalyst.'), +/// type: VoicesStatusIndicatorType.success, +/// ), +/// ``` +class VoicesStatusIndicator extends StatelessWidget { + /// Typically [Text] or [Row] with [Text] and [Icon]. + final Widget status; + + /// Typically [Text]. + final Widget title; + + /// Typically [Text]. + final Widget body; + + /// Specifies [status] colors configuration. + final VoicesStatusIndicatorType type; + + const VoicesStatusIndicator({ + super.key, + required this.status, + required this.title, + required this.body, + required this.type, + }); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + _StatusContainer( + type: type, + child: status, + ), + _TitleContainer(child: title), + _BodyContainer(child: body), + ].separatedBy(const SizedBox(height: 16)).toList(), + ), + ); + } +} + +class _StatusContainer extends StatelessWidget { + final VoicesStatusIndicatorType type; + final Widget child; + + const _StatusContainer({ + required this.type, + required this.child, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + final backgroundColor = switch (type) { + VoicesStatusIndicatorType.success => theme.colors.successContainer, + VoicesStatusIndicatorType.error => theme.colors.errorContainer, + }; + + return Container( + decoration: BoxDecoration( + color: backgroundColor, + borderRadius: BorderRadius.circular(8), + ), + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16), + alignment: Alignment.center, + child: DefaultTextStyle( + style: (theme.textTheme.titleLarge ?? const TextStyle()) + .copyWith(color: theme.colors.textPrimary), + child: IconTheme( + data: IconThemeData( + size: 24, + color: theme.colors.textPrimary, + ), + child: child, + ), + ), + ); + } +} + +class _TitleContainer extends StatelessWidget { + final Widget child; + + const _TitleContainer({ + required this.child, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + return DefaultTextStyle( + style: (theme.textTheme.titleMedium ?? const TextStyle()).copyWith( + color: theme.colors.textOnPrimary, + ), + textAlign: TextAlign.center, + child: child, + ); + } +} + +class _BodyContainer extends StatelessWidget { + final Widget child; + + const _BodyContainer({ + required this.child, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + return DefaultTextStyle( + style: (theme.textTheme.bodyMedium ?? const TextStyle()).copyWith( + color: theme.colors.textOnPrimary, + ), + textAlign: TextAlign.center, + child: child, + ); + } +} diff --git a/catalyst_voices/lib/widgets/menu/voices_menu.dart b/catalyst_voices/lib/widgets/menu/voices_menu.dart new file mode 100644 index 00000000000..6f36791dfe8 --- /dev/null +++ b/catalyst_voices/lib/widgets/menu/voices_menu.dart @@ -0,0 +1,167 @@ +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:flutter/material.dart'; + +/// A menu of the app that +/// can be also us as a cascade. +class VoicesMenu extends StatelessWidget { + /// Menu items passed as models, can be nested. + final List menuItems; + + /// The widget that is clicked to open menu. + final Widget child; + + /// The callback called when the menu item is tapped. + final ValueChanged? onTap; + + /// The default constructor for the [VoicesMenu]. + const VoicesMenu({ + super.key, + required this.menuItems, + required this.child, + this.onTap, + }); + + @override + Widget build(BuildContext context) { + return MenuBar( + style: const MenuStyle( + elevation: WidgetStatePropertyAll(0), + ), + children: [ + SubmenuButton( + menuChildren: [...menuItems.map(_mapItemToButton)], + menuStyle: const MenuStyle(alignment: Alignment.centerRight), + style: MenuItemButton.styleFrom(shadowColor: Colors.transparent), + child: child, + ), + ], + ); + } + + _MenuButton _mapItemToButton(MenuItem item) { + return _MenuButton( + menuItem: item, + menuChildren: (item is SubMenuItem) + ? item.children.map(_mapItemToButton).toList() + : null, + onSelected: (item is SubMenuItem) ? null : onTap, + ); + } +} + +class _MenuButton extends StatelessWidget { + final MenuItem menuItem; + final List? menuChildren; + final ValueChanged? onSelected; + + const _MenuButton({ + required this.menuItem, + this.menuChildren, + this.onSelected, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final textTheme = theme.textTheme; + final icon = (menuItem.icon != null) ? Icon(menuItem.icon, size: 24) : null; + + final textStyle = textTheme.bodyMedium?.copyWith( + color: + menuItem.enabled ? textTheme.bodySmall?.color : theme.disabledColor, + ); + + final children = menuChildren; + return Column( + children: [ + Stack( + children: [ + if (icon != null) + Positioned.fill( + child: Align( + alignment: Alignment.centerLeft, + child: Padding( + padding: const EdgeInsets.only(left: 10), + child: Icon( + icon.icon, + size: icon.size, + color: menuItem.enabled + ? textTheme.bodySmall?.color + : theme.disabledColor, + ), + ), + ), + ), + if (children != null) + const Positioned.fill( + child: Align( + alignment: Alignment.centerRight, + child: Padding( + padding: EdgeInsets.only(right: 6), + child: Icon(CatalystVoicesIcons.chevron_right, size: 20), + ), + ), + ), + if (children == null) + MenuItemButton( + leadingIcon: icon, + onPressed: () => onSelected?.call(menuItem), + style: MenuItemButton.styleFrom(iconColor: Colors.transparent), + child: Text( + menuItem.label, + style: textStyle, + ), + ) + else + SubmenuButton( + leadingIcon: icon, + menuChildren: children, + style: MenuItemButton.styleFrom(iconColor: Colors.transparent), + child: Text( + menuItem.label, + style: textStyle, + ), + ), + ], + ), + if (menuItem.showDivider) + const Divider( + height: 0, + indent: 0, + thickness: 1, + ), + ], + ); + } +} + +/// Model representing Menu Item +class MenuItem { + final int id; + final String label; + final IconData? icon; + final bool showDivider; + final bool enabled; + + MenuItem({ + required this.id, + required this.label, + this.icon, + this.showDivider = false, + this.enabled = true, + }); +} + +/// Model representing Submenu Item +/// and extending from MenuItem +class SubMenuItem extends MenuItem { + List children; + + SubMenuItem({ + required super.id, + required super.label, + required this.children, + super.icon, + super.showDivider, + }); +} diff --git a/catalyst_voices/lib/widgets/menu/voices_node_menu.dart b/catalyst_voices/lib/widgets/menu/voices_node_menu.dart new file mode 100644 index 00000000000..562d6d02e93 --- /dev/null +++ b/catalyst_voices/lib/widgets/menu/voices_node_menu.dart @@ -0,0 +1,147 @@ +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:collection/collection.dart'; +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; + +base class VoicesNodeMenuItem extends Equatable { + final int id; + final String label; + + const VoicesNodeMenuItem({ + required this.id, + required this.label, + }); + + @override + List get props => [id, label]; +} + +final class VoicesNodeMenuData extends Equatable { + final int? selectedItemId; + final bool isExpanded; + + const VoicesNodeMenuData({ + this.selectedItemId, + this.isExpanded = false, + }); + + VoicesNodeMenuData copyWith({ + int? selectedItemId, + bool? isExpanded, + }) { + return VoicesNodeMenuData( + selectedItemId: selectedItemId ?? this.selectedItemId, + isExpanded: isExpanded ?? this.isExpanded, + ); + } + + VoicesNodeMenuData clearSelection() { + return VoicesNodeMenuData( + selectedItemId: null, + isExpanded: isExpanded, + ); + } + + @override + List get props => [ + selectedItemId, + isExpanded, + ]; +} + +final class VoicesNodeMenuController extends ValueNotifier { + VoicesNodeMenuController([ + super._value = const VoicesNodeMenuData(), + ]); + + set selected(int? newValue) { + value = newValue != null + ? value.copyWith(selectedItemId: newValue) + : value.clearSelection(); + } + + set isExpanded(bool newValue) { + value = value.copyWith(isExpanded: newValue); + } +} + +class VoicesNodeMenu extends StatelessWidget { + final String name; + final VoicesNodeMenuController controller; + final List items; + final ValueChanged? onSelectionChanged; + final ValueChanged? onExpandChanged; + + bool get _canTapItem => onSelectionChanged != null; + + bool get _canToggleExpand => onExpandChanged != null; + + const VoicesNodeMenu({ + super.key, + required this.name, + required this.controller, + required this.items, + this.onSelectionChanged, + this.onExpandChanged, + }); + + @override + Widget build(BuildContext context) { + return ValueListenableBuilder( + valueListenable: controller, + builder: (context, value, child) { + return SimpleTreeView( + isExpanded: value.isExpanded, + root: SimpleTreeViewRootRow( + onTap: _canToggleExpand ? _onRootTap : null, + leading: [ + _NodeIcon(isOpen: value.isExpanded), + VoicesAssets.images.viewGrid.buildIcon(), + ], + child: Text(name), + ), + children: items.mapIndexed( + (index, item) { + return SimpleTreeViewChildRow( + key: ValueKey('NodeMenu${item.id}RowKey'), + hasNext: index < items.length - 1, + isSelected: item.id == value.selectedItemId, + onTap: _canTapItem ? () => _onMenuItemTap(item) : null, + child: Text(item.label), + ); + }, + ).toList(), + ); + }, + ); + } + + void _onRootTap() { + onExpandChanged?.call(!controller.value.isExpanded); + } + + void _onMenuItemTap(VoicesNodeMenuItem item) { + final id = item.id != controller.value.selectedItemId ? item.id : null; + onSelectionChanged?.call(id); + } +} + +class _NodeIcon extends StatelessWidget { + final bool isOpen; + + const _NodeIcon({ + this.isOpen = true, + }); + + @override + Widget build(BuildContext context) { + return IconTheme( + data: IconThemeData(color: Theme.of(context).colors.iconsForeground), + child: isOpen + ? VoicesAssets.images.nodeOpen.buildIcon() + : VoicesAssets.images.nodeClosed.buildIcon(), + ); + } +} diff --git a/catalyst_voices/lib/widgets/menu/voices_wallet_tile.dart b/catalyst_voices/lib/widgets/menu/voices_wallet_tile.dart new file mode 100644 index 00000000000..648aeb456ac --- /dev/null +++ b/catalyst_voices/lib/widgets/menu/voices_wallet_tile.dart @@ -0,0 +1,70 @@ +import 'package:catalyst_cardano/catalyst_cardano.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:flutter/material.dart'; + +/// A replacement for the [ListTile] with customized +/// styling that displays a [CardanoWallet]. +class VoicesWalletTile extends StatelessWidget { + /// URI or base64 encoded icon of the wallet extension. + final String? iconSrc; + + /// The name of the wallet extension. + final Widget? name; + + /// A callback called when the widget is pressed. + final VoidCallback? onTap; + + /// The default constructor for the [VoicesWalletTile]. + const VoicesWalletTile({ + super.key, + this.iconSrc, + this.name, + this.onTap, + }); + + @override + Widget build(BuildContext context) { + final icon = this.iconSrc; + final name = this.name; + + return ListTile( + leading: SizedBox( + width: 40, + height: 40, + child: icon == null + ? _IconPlaceholder() + : Image.network( + icon, + errorBuilder: (context, error, stackTrace) { + return _IconPlaceholder(); + }, + ), + ), + horizontalTitleGap: 16, + title: name == null + ? null + : DefaultTextStyle( + style: Theme.of(context).textTheme.labelLarge!, + maxLines: 1, + overflow: TextOverflow.ellipsis, + child: name, + ), + trailing: Icon( + CatalystVoicesIcons.chevron_right, + size: 24, + ), + onTap: onTap, + ); + } +} + +class _IconPlaceholder extends StatelessWidget { + const _IconPlaceholder(); + + @override + Widget build(BuildContext context) { + return CircleAvatar( + backgroundColor: Theme.of(context).colorScheme.primaryContainer, + ); + } +} diff --git a/catalyst_voices/lib/widgets/modals/voices_desktop_dialog.dart b/catalyst_voices/lib/widgets/modals/voices_desktop_dialog.dart new file mode 100644 index 00000000000..b8165f6294d --- /dev/null +++ b/catalyst_voices/lib/widgets/modals/voices_desktop_dialog.dart @@ -0,0 +1,99 @@ +import 'package:catalyst_voices/widgets/buttons/voices_buttons.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:flutter/material.dart'; + +class VoicesDesktopDialog extends StatelessWidget { + final BoxConstraints constraints; + final Widget child; + + const VoicesDesktopDialog({ + super.key, + this.constraints = const BoxConstraints(minWidth: 900, minHeight: 600), + required this.child, + }); + + @override + Widget build(BuildContext context) { + return Dialog( + alignment: Alignment.topCenter, + insetPadding: EdgeInsets.symmetric(horizontal: 40, vertical: 90), + child: ConstrainedBox( + constraints: constraints, + child: child, + ), + ); + } +} + +/// Commonly used structure for desktop dialogs. +/// +/// Keep in mind that this dialog has fixed size of 900x600 and +/// is always adding close button in top right corner. +class VoicesDesktopPanelsDialog extends StatelessWidget { + final Widget left; + final Widget right; + + const VoicesDesktopPanelsDialog({ + super.key, + required this.left, + required this.right, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + return VoicesDesktopDialog( + constraints: BoxConstraints.tightFor(width: 900, height: 600), + child: Stack( + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Expanded( + child: Container( + padding: EdgeInsets.all(20), + decoration: BoxDecoration( + color: theme.colors.elevationsOnSurfaceNeutralLv1Grey, + ), + child: left, + ), + ), + Expanded( + child: Container( + padding: EdgeInsets.all(20), + decoration: BoxDecoration( + color: theme.colors.elevationsOnSurfaceNeutralLv1White, + ), + child: right, + ), + ), + ], + ), + _DialogCloseButton(), + ], + ), + ); + } +} + +class _DialogCloseButton extends StatelessWidget { + const _DialogCloseButton(); + + @override + Widget build(BuildContext context) { + final buttonStyle = ButtonStyle( + fixedSize: WidgetStatePropertyAll(Size.square(48)), + ); + + return Align( + alignment: Alignment.topRight, + child: IconButtonTheme( + data: IconButtonThemeData(style: buttonStyle), + child: XButton( + onTap: () => Navigator.of(context).pop(), + ), + ), + ); + } +} diff --git a/catalyst_voices/lib/widgets/modals/voices_dialog.dart b/catalyst_voices/lib/widgets/modals/voices_dialog.dart new file mode 100644 index 00000000000..da2b7de3634 --- /dev/null +++ b/catalyst_voices/lib/widgets/modals/voices_dialog.dart @@ -0,0 +1,16 @@ +import 'package:flutter/material.dart'; + +/// This class acts only as semantic anchor for [show] and its not +/// meant to be extended. +abstract final class VoicesDialog { + /// Encapsulates single entry point. + static Future show( + BuildContext context, { + required WidgetBuilder builder, + }) { + return showDialog( + context: context, + builder: builder, + ); + } +} diff --git a/catalyst_voices/lib/widgets/modals/voices_info_dialog.dart b/catalyst_voices/lib/widgets/modals/voices_info_dialog.dart new file mode 100644 index 00000000000..e14c2ac6e2c --- /dev/null +++ b/catalyst_voices/lib/widgets/modals/voices_info_dialog.dart @@ -0,0 +1,38 @@ +import 'package:catalyst_voices/widgets/modals/voices_desktop_dialog.dart'; +import 'package:catalyst_voices/widgets/modals/voices_dialog.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:flutter/material.dart'; + +/// Opinionated, two panels, dialog that is tailored for desktop +/// form factors. +/// +/// Uses [VoicesDesktopPanelsDialog] for base structure. +/// +/// Call [VoicesDialog.show] with [VoicesDesktopInfoDialog] in order +/// to show it. +class VoicesDesktopInfoDialog extends StatelessWidget { + final String title; + + const VoicesDesktopInfoDialog({ + required this.title, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + return VoicesDesktopPanelsDialog( + left: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: theme.textTheme.titleLarge + ?.copyWith(color: theme.colors.textOnPrimary), + ), + ], + ), + right: Column(), + ); + } +} diff --git a/catalyst_voices/lib/widgets/rich_text/voices_rich_text.dart b/catalyst_voices/lib/widgets/rich_text/voices_rich_text.dart new file mode 100644 index 00000000000..11431a537d9 --- /dev/null +++ b/catalyst_voices/lib/widgets/rich_text/voices_rich_text.dart @@ -0,0 +1,349 @@ +import 'package:catalyst_voices/widgets/buttons/voices_filled_button.dart'; +import 'package:catalyst_voices/widgets/buttons/voices_text_button.dart'; +import 'package:catalyst_voices/widgets/common/resizable_box_parent.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; +import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_quill/flutter_quill.dart'; +import 'package:flutter_quill_extensions/flutter_quill_extensions.dart'; + +/// A component for rich text writing +/// using Quill under the hood +/// https://pub.dev/packages/flutter_quill +class VoicesRichText extends StatefulWidget { + final String title; + final Document? document; + final ValueChanged? onSave; + final int? charsLimit; + + const VoicesRichText({ + super.key, + this.title = '', + this.document, + this.onSave, + this.charsLimit, + }); + + @override + State createState() => _VoicesRichTextState(); +} + +class _Editor extends StatelessWidget { + final bool editMode; + final QuillController controller; + final FocusNode focusNode; + + const _Editor({ + required this.editMode, + required this.controller, + required this.focusNode, + }); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: ResizableBoxParent( + minHeight: 400, + resizableVertically: true, + resizableHorizontally: false, + child: Container( + decoration: BoxDecoration( + color: editMode + ? Theme.of(context).colors.onSurfaceNeutralOpaqueLv1 + : Theme.of(context).colors.onSurfaceNeutralOpaqueLv0, + border: Border.all( + color: Theme.of(context).colorScheme.outlineVariant, + ), + borderRadius: BorderRadius.circular(8), + ), + child: QuillEditor.basic( + controller: controller, + focusNode: focusNode, + configurations: QuillEditorConfigurations( + padding: const EdgeInsets.all(16), + placeholder: context.l10n.placeholderRichText, + embedBuilders: CatalystPlatform.isWeb + ? FlutterQuillEmbeds.editorWebBuilders() + : FlutterQuillEmbeds.editorBuilders(), + ), + ), + ), + ), + ); + } +} + +class _Footer extends StatelessWidget { + final QuillController controller; + final ValueChanged? onSave; + + const _Footer({ + required this.controller, + this.onSave, + }); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 12.0, + ), + alignment: Alignment.centerRight, + color: Theme.of(context).colors.onSurfaceNeutralOpaqueLv1, + child: VoicesFilledButton( + child: Text(context.l10n.saveButtonText.toUpperCase()), + onTap: () => onSave?.call(controller.document), + ), + ); + } +} + +class _Limit extends StatelessWidget { + final int documentLength; + final int charsLimit; + + const _Limit({ + required this.documentLength, + required this.charsLimit, + }); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 32.0), + child: Row( + children: [ + Expanded( + child: Text( + context.l10n.supportingTextLabelText, + style: Theme.of(context).textTheme.bodySmall, + ), + ), + Text( + '${documentLength}/${charsLimit}', + style: Theme.of(context).textTheme.bodySmall, + ), + ], + ), + ); + } +} + +class _Toolbar extends StatelessWidget { + final QuillController controller; + + const _Toolbar({ + required this.controller, + }); + + @override + Widget build(BuildContext context) { + return Container( + color: Theme.of(context).colors.onSurfaceNeutralOpaqueLv1, + child: QuillToolbar( + configurations: const QuillToolbarConfigurations(), + child: Row( + children: [ + QuillToolbarIconButton( + tooltip: context.l10n.headerTooltipText, + onPressed: () { + if (controller.isHeaderSelected) { + controller.formatSelection(Attribute.header); + } else { + controller.formatSelection(Attribute.h1); + } + }, + icon: const Icon(CatalystVoicesIcons.rt_heading), + isSelected: controller.isHeaderSelected, + iconTheme: null, + ), + QuillToolbarToggleStyleButton( + options: const QuillToolbarToggleStyleButtonOptions( + iconData: CatalystVoicesIcons.rt_bold, + ), + controller: controller, + attribute: Attribute.bold, + ), + QuillToolbarToggleStyleButton( + options: const QuillToolbarToggleStyleButtonOptions( + iconData: CatalystVoicesIcons.rt_italic, + ), + controller: controller, + attribute: Attribute.italic, + ), + QuillToolbarToggleStyleButton( + options: const QuillToolbarToggleStyleButtonOptions( + iconData: CatalystVoicesIcons.rt_ordered_list, + ), + controller: controller, + attribute: Attribute.ol, + ), + QuillToolbarToggleStyleButton( + options: const QuillToolbarToggleStyleButtonOptions( + iconData: CatalystVoicesIcons.rt_unordered_list, + ), + controller: controller, + attribute: Attribute.ul, + ), + QuillToolbarIndentButton( + options: QuillToolbarIndentButtonOptions( + iconData: CatalystVoicesIcons.rt_increase_indent, + ), + controller: controller, + isIncrease: true, + ), + QuillToolbarIndentButton( + options: QuillToolbarIndentButtonOptions( + iconData: CatalystVoicesIcons.rt_decrease_indent, + ), + controller: controller, + isIncrease: false, + ), + QuillToolbarImageButton( + options: const QuillToolbarImageButtonOptions( + iconData: CatalystVoicesIcons.photograph, + ), + controller: controller, + ), + ], + ), + ), + ); + } +} + +class _TopBar extends StatelessWidget { + final String title; + final bool editMode; + final VoidCallback? onToggleEditMode; + + const _TopBar({ + required this.title, + required this.editMode, + this.onToggleEditMode, + }); + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Text( + title, + style: Theme.of(context).textTheme.titleMedium, + ), + Spacer(), + VoicesTextButton( + onTap: onToggleEditMode, + child: Text( + editMode + ? context.l10n.cancelButtonText + : context.l10n.editButtonText, + style: Theme.of(context).textTheme.labelSmall, + ), + ), + SizedBox(width: 8), + ], + ); + } +} + +class _VoicesRichTextState extends State { + final QuillController _controller = QuillController.basic(); + int _documentLength = 0; + bool _editMode = false; + final FocusNode _focusNode = FocusNode(); + + @override + Widget build(BuildContext context) { + return ExcludeFocus( + excluding: !_editMode, + child: Column( + children: [ + Padding( + padding: const EdgeInsets.only( + left: 16, + top: 20, + bottom: 20, + ), + child: _TopBar( + title: widget.title, + editMode: _editMode, + onToggleEditMode: () { + setState(() { + _editMode = !_editMode; + }); + }, + ), + ), + if (_editMode) + Padding( + padding: const EdgeInsets.only(bottom: 16.0), + child: _Toolbar(controller: _controller), + ), + _Editor( + editMode: _editMode, + controller: _controller, + focusNode: _focusNode, + ), + if (widget.charsLimit != null) + _Limit( + documentLength: _documentLength, + charsLimit: widget.charsLimit!, + ), + SizedBox(height: 16), + _Footer( + controller: _controller, + onSave: widget.onSave, + ), + ], + ), + ); + } + + @override + void dispose() { + _controller.dispose(); + _focusNode.dispose(); + super.dispose(); + } + + @override + void initState() { + super.initState(); + if (widget.document != null) _controller.document = widget.document!; + _controller.document.changes.listen(_onDocumentChange); + _documentLength = _controller.document.length; + } + + void _onDocumentChange(DocChange docChange) { + final documentLength = _controller.document.length; + + setState(() { + _documentLength = documentLength; + }); + + final limit = widget.charsLimit; + + if (limit == null) return; + + if (documentLength > limit) { + final latestIndex = limit - 1; + _controller.replaceText( + latestIndex, + documentLength - limit, + '', + TextSelection.collapsed(offset: latestIndex), + ); + } + } +} + +extension on QuillController { + bool get isHeaderSelected { + return getSelectionStyle().attributes.containsKey('header'); + } +} diff --git a/catalyst_voices/lib/widgets/seed_phrase/seed_phrases_completer.dart b/catalyst_voices/lib/widgets/seed_phrase/seed_phrases_completer.dart new file mode 100644 index 00000000000..0003e97bee4 --- /dev/null +++ b/catalyst_voices/lib/widgets/seed_phrase/seed_phrases_completer.dart @@ -0,0 +1,203 @@ +import 'package:catalyst_voices/widgets/common/affix_decorator.dart'; +import 'package:catalyst_voices/widgets/common/columns_row.dart'; +import 'package:catalyst_voices/widgets/seed_phrase/seed_phrases_picker.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; +import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; + +/// A widget that displays slots for selecting seed phrases and filling them up. +/// +/// Typically used together with a [SeedPhrasesPicker]. +class SeedPhrasesCompleter extends StatelessWidget { + /// The number of columns to use for displaying the slots. + /// Defaults to 2. + final int columnsCount; + + /// The total number of slots available for seed phrases. + final int slotsCount; + + /// A set of currently selected seed phrases. Defaults to an empty set. + final Set words; + + /// A callback function triggered when a non-filled slot or a filled but only + /// for previous selection. + final ValueChanged? onWordTap; + + /// Creates a [SeedPhrasesCompleter] widget. + const SeedPhrasesCompleter({ + super.key, + this.columnsCount = 2, + required this.slotsCount, + this.words = const {}, + this.onWordTap, + }); + + @override + Widget build(BuildContext context) { + final slots = List.generate(slotsCount, words.elementAtOrNull); + final onWordTap = this.onWordTap; + + // Identify the currently active slot (being filled) and the + // previous slot (deletable). + final currentIndex = words.length < slotsCount ? words.length : null; + + return MediaQuery.withNoTextScaling( + child: ColumnsRow( + columnsCount: 2, + mainAxisSpacing: 10, + crossAxisSpacing: 6, + children: slots.mapIndexed((index, element) { + final isCurrent = index == currentIndex; + final isPrevious = currentIndex != null + ? currentIndex == index + 1 + : index == slotsCount - 1; + + final canDelete = element != null && isPrevious; + + return _WordSlotCell( + element, + key: ValueKey('CompleterSeedPhrase${index}CellKey'), + slotNr: index + 1, + isActive: isCurrent, + showDelete: canDelete, + onTap: !canDelete || onWordTap == null + ? null + : () => onWordTap(element), + ); + }).toList(), + ), + ); + } +} + +/// A widget representing a single slot for selecting a seed phrase +/// within the [SeedPhrasesCompleter]. +class _WordSlotCell extends StatelessWidget { + /// The currently selected seed phrase for this slot (can be null). + final String? data; + + /// The slot number (1-based). + final int slotNr; + + /// Whether the slot is currently being filled (active). + final bool isActive; + + /// Whether the slot shows a delete icon when filled. + final bool showDelete; + + /// A callback function triggered when the slot is tapped (if allowed). + final VoidCallback? onTap; + + Set get states => { + if (data != null) WidgetState.selected, + if (isActive) WidgetState.focused, + if (data == null && !isActive) WidgetState.disabled, + }; + + const _WordSlotCell( + this.data, { + super.key, + required this.slotNr, + this.isActive = false, + this.showDelete = false, + this.onTap, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + final backgroundColor = _CellBackgroundColor(theme); + final foregroundColor = _CellForegroundColor(theme); + final border = _CellBorder(theme); + + return AnimatedContainer( + duration: kThemeChangeDuration, + constraints: const BoxConstraints.tightFor(height: 32), + decoration: BoxDecoration( + color: backgroundColor.resolve(states), + border: border.resolve(states), + borderRadius: BorderRadius.circular(8), + ), + child: Material( + type: MaterialType.transparency, + textStyle: (theme.textTheme.labelLarge ?? const TextStyle()) + .copyWith(color: foregroundColor.resolve(states)), + child: InkWell( + borderRadius: BorderRadius.circular(8), + onTap: onTap, + child: Center( + child: AffixDecorator( + gap: showDelete ? 8 : 0, + suffix: Offstage( + offstage: !showDelete, + child: const Icon(Icons.close), + ), + iconTheme: IconThemeData( + color: foregroundColor.resolve(states), + size: 18, + ), + child: Text(data ?? context.l10n.seedPhraseSlotNr(slotNr)), + ), + ), + ), + ), + ); + } +} + +final class _CellBackgroundColor extends WidgetStateProperty { + final ThemeData theme; + + _CellBackgroundColor(this.theme); + + @override + Color? resolve(Set states) { + if (states.contains(WidgetState.disabled)) { + return theme.colorScheme.onSurface.withOpacity(0.08); + } + + if (states.contains(WidgetState.focused)) { + return theme.colors.onSurfaceNeutralOpaqueLv0; + } + + return theme.colorScheme.primary; + } +} + +final class _CellForegroundColor extends WidgetStateProperty { + final ThemeData theme; + + _CellForegroundColor(this.theme); + + @override + Color? resolve(Set states) { + if (states.contains(WidgetState.disabled)) { + return theme.colors.textDisabled; + } + + if (states.contains(WidgetState.focused)) { + return theme.colorScheme.primary; + } + + return theme.colorScheme.onPrimary; + } +} + +final class _CellBorder extends WidgetStateProperty { + final ThemeData theme; + + _CellBorder(this.theme); + + @override + BoxBorder? resolve(Set states) { + if (states.contains(WidgetState.focused)) { + return Border.all( + color: theme.colorScheme.primary, + ); + } + + return null; + } +} diff --git a/catalyst_voices/lib/widgets/seed_phrase/seed_phrases_picker.dart b/catalyst_voices/lib/widgets/seed_phrase/seed_phrases_picker.dart new file mode 100644 index 00000000000..9ff3a944eb6 --- /dev/null +++ b/catalyst_voices/lib/widgets/seed_phrase/seed_phrases_picker.dart @@ -0,0 +1,133 @@ +import 'package:catalyst_voices/widgets/common/columns_row.dart'; +import 'package:catalyst_voices/widgets/seed_phrase/seed_phrases_completer.dart'; +import 'package:flutter/material.dart'; + +/// A widget that displays a grid of seed phrases with selection functionality. +/// +/// Typically used together with a [SeedPhrasesCompleter]. +class SeedPhrasesPicker extends StatelessWidget { + /// The number of columns to use for displaying the seed phrases. + /// Defaults to 2. + final int columnsCount; + + /// The list of seed phrases to be displayed. + final List words; + + /// A set of currently selected seed phrases. Defaults to an empty set. + final Set selectedWords; + + /// A callback function triggered when a non-selected seed phrase is tapped. + final ValueChanged? onWordTap; + + const SeedPhrasesPicker({ + super.key, + this.columnsCount = 2, + required this.words, + this.selectedWords = const {}, + this.onWordTap, + }); + + @override + Widget build(BuildContext context) { + final onWordTap = this.onWordTap; + + return MediaQuery.withNoTextScaling( + child: ColumnsRow( + columnsCount: 2, + mainAxisSpacing: 10, + crossAxisSpacing: 6, + children: words.map((word) { + final isSelected = selectedWords.contains(word); + + return _WordCell( + word, + key: ValueKey('PickerSeedPhrase${word}CellKey'), + isSelected: isSelected, + onTap: isSelected || onWordTap == null + ? null + : () { + onWordTap(word); + }, + ); + }).toList(), + ), + ); + } +} + +/// A widget representing a single seed phrase cell within the +/// [SeedPhrasesPicker]. +class _WordCell extends StatelessWidget { + /// The seed phrase word to be displayed. + final String data; + + /// Whether the seed phrase is currently selected. + final bool isSelected; + + /// A callback function triggered when the cell is tapped (if not selected). + final VoidCallback? onTap; + + Set get states => { + if (isSelected) WidgetState.selected, + }; + + const _WordCell( + this.data, { + super.key, + this.isSelected = false, + this.onTap, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + final backgroundColor = _CellBackgroundColor(theme); + final foregroundColor = _CellForegroundColor(theme); + + return ConstrainedBox( + constraints: const BoxConstraints.tightFor(height: 32), + child: Material( + color: backgroundColor.resolve(states), + borderRadius: BorderRadius.circular(8), + textStyle: theme.textTheme.labelLarge + ?.copyWith(color: foregroundColor.resolve(states)), + child: InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(8), + child: Center(child: Text(data)), + ), + ), + ); + } +} + +final class _CellBackgroundColor extends WidgetStateProperty { + final ThemeData theme; + + _CellBackgroundColor(this.theme); + + @override + Color resolve(Set states) { + if (states.contains(WidgetState.selected)) { + return theme.colorScheme.primary.withOpacity(0.12); + } + + return theme.colorScheme.primary; + } +} + +final class _CellForegroundColor extends WidgetStateProperty { + final ThemeData theme; + + _CellForegroundColor(this.theme); + + @override + Color resolve(Set states) { + if (states.contains(WidgetState.selected)) { + return theme.colorScheme.primary; + } + + return theme.colorScheme.onPrimary; + } +} diff --git a/catalyst_voices/lib/widgets/seed_phrase/seed_phrases_sequencer.dart b/catalyst_voices/lib/widgets/seed_phrase/seed_phrases_sequencer.dart new file mode 100644 index 00000000000..651e3e3e48d --- /dev/null +++ b/catalyst_voices/lib/widgets/seed_phrase/seed_phrases_sequencer.dart @@ -0,0 +1,108 @@ +import 'package:catalyst_voices/widgets/seed_phrase/seed_phrases_completer.dart'; +import 'package:catalyst_voices/widgets/seed_phrase/seed_phrases_picker.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +/// A widget that allows users to sequence a set of seed phrases. +/// +/// It provides a two-section interface: +/// - A completer section where users can fill slots with selected phrases. +/// - A picker section where users can select available phrases. +/// +/// The selected phrases are managed internally and updated through the +/// [onChanged] callback. +class SeedPhrasesSequencer extends StatefulWidget { + /// The list of available seed phrases. + final List words; + + /// A callback function triggered when the set of selected phrases changes. + final ValueChanged> onChanged; + + /// Creates a [SeedPhrasesSequencer] widget. + const SeedPhrasesSequencer({ + super.key, + required this.words, + required this.onChanged, + }); + + @override + State createState() => _SeedPhrasesSequencerState(); +} + +class _SeedPhrasesSequencerState extends State { + final _selected = {}; + + @override + void didUpdateWidget(covariant SeedPhrasesSequencer oldWidget) { + super.didUpdateWidget(oldWidget); + + if (!listEquals(widget.words, oldWidget.words)) { + _selected.clear(); + } + } + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + _BorderDecorator( + child: SeedPhrasesCompleter( + slotsCount: widget.words.length, + words: _selected, + onWordTap: _removeWord, + ), + ), + const SizedBox(height: 12), + _BorderDecorator( + child: SeedPhrasesPicker( + words: widget.words, + selectedWords: _selected, + onWordTap: _selectWord, + ), + ), + ], + ); + } + + void _removeWord(String word) { + setState(() { + _selected.remove(word); + widget.onChanged(Set.of(_selected)); + }); + } + + void _selectWord(String word) { + setState(() { + _selected.add(word); + widget.onChanged(Set.of(_selected)); + }); + } +} + +class _BorderDecorator extends StatelessWidget { + final Widget child; + + const _BorderDecorator({ + required this.child, + }); + + @override + Widget build(BuildContext context) { + return DecoratedBox( + decoration: BoxDecoration( + border: Border.all( + color: Theme.of(context).colors.outlineBorderVariant ?? + Theme.of(context).colorScheme.outlineVariant, + width: 1.5, + ), + borderRadius: BorderRadius.circular(12), + ), + child: Padding( + padding: const EdgeInsets.all(10), + child: child, + ), + ); + } +} diff --git a/catalyst_voices/lib/widgets/seed_phrase/seed_phrases_viewer.dart b/catalyst_voices/lib/widgets/seed_phrase/seed_phrases_viewer.dart new file mode 100644 index 00000000000..7b62506701c --- /dev/null +++ b/catalyst_voices/lib/widgets/seed_phrase/seed_phrases_viewer.dart @@ -0,0 +1,90 @@ +import 'package:catalyst_voices/widgets/common/columns_row.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; + +/// Displays a list of seed phrases in a grid-like layout with +/// customizable columns. +class SeedPhrasesViewer extends StatelessWidget { + /// The number of columns to use for displaying the seed phrases. + /// Defaults to 2. + final int columnsCount; + + /// The list of seed phrases to be displayed. + final List words; + + const SeedPhrasesViewer({ + super.key, + this.columnsCount = 2, + required this.words, + }); + + @override + Widget build(BuildContext context) { + return MediaQuery.withNoTextScaling( + child: ColumnsRow( + columnsCount: 2, + mainAxisSpacing: 24, + crossAxisSpacing: 12, + children: words.mapIndexed((index, word) { + return _WordCell( + word, + key: ValueKey('SeedPhrase${word}CellKey'), + number: index + 1, + ); + }).toList(), + ), + ); + } +} + +/// A widget representing a single seed phrase cell within the +/// [SeedPhrasesViewer]. +class _WordCell extends StatelessWidget { + /// The seed phrase word to be displayed. + final String data; + + /// The sequential number associated with the seed phrase. + final int number; + + const _WordCell( + this.data, { + super.key, + required this.number, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + return Container( + constraints: const BoxConstraints(minHeight: 56), + decoration: BoxDecoration( + color: theme.colors.onSurfaceNeutralOpaqueLv1, + borderRadius: BorderRadius.circular(4), + ), + padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 16), + child: DefaultTextStyle( + style: theme.textTheme.bodyLarge ?? const TextStyle(), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + '${number.toString().padLeft(2, '0')}.', + style: TextStyle(color: theme.colors.textOnPrimary), + ), + const SizedBox(width: 6), + Flexible( + child: Text( + data, + style: TextStyle(color: theme.colors.textPrimary), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + ), + ); + } +} diff --git a/catalyst_voices/lib/widgets/separators/voices_divider.dart b/catalyst_voices/lib/widgets/separators/voices_divider.dart new file mode 100644 index 00000000000..20a8a965345 --- /dev/null +++ b/catalyst_voices/lib/widgets/separators/voices_divider.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; + +const _kDefaultIndent = 24.0; + +/// The [VoicesDivider] widget is a simple wrapper around Material's [Divider] +/// widget that provides additional customization options for indentation. +/// +/// It's primarily used to create horizontal dividers within the context +/// of a list or other layout with specific indentation requirements. +class VoicesDivider extends StatelessWidget { + /// A double value representing the indentation of the divider from the + /// start of the parent container. + /// + /// Defaults to [_kDefaultIndent] (24.0). + final double indent; + + /// A double value representing the indentation of the divider from the + /// end of the parent container. + /// + /// Defaults to [_kDefaultIndent] (24.0). + final double endIndent; + + /// Optional color of divider. + final Color? color; + + const VoicesDivider({ + super.key, + this.indent = _kDefaultIndent, + this.endIndent = _kDefaultIndent, + this.color, + }); + + @override + Widget build(BuildContext context) { + return Divider( + indent: indent, + endIndent: endIndent, + color: color, + ); + } +} diff --git a/catalyst_voices/lib/widgets/separators/voices_text_divider.dart b/catalyst_voices/lib/widgets/separators/voices_text_divider.dart new file mode 100644 index 00000000000..c9e90b0d1c0 --- /dev/null +++ b/catalyst_voices/lib/widgets/separators/voices_text_divider.dart @@ -0,0 +1,60 @@ +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:flutter/material.dart'; + +/// A divider with text placed in the middle. +/// +/// This widget is a variation of [Divider] that displays a text child centered +/// between two divider lines. It provides customization options for indent, +/// gap, and overall size. +/// +/// Example usage: +/// +/// ```dart +/// VoicesTextDivider( +/// child: Text('My Name'), +/// ), +/// ``` +class VoicesTextDivider extends StatelessWidget { + /// The indentation of the divider lines from the start and end of the row. + final double indent; + + /// The gap between the divider lines and the text child. + final double nameGap; + + /// The size of the row containing the divider lines and text child. + final MainAxisSize mainAxisSize; + + /// The text to display in the middle of the divider. + final Widget child; + + const VoicesTextDivider({ + super.key, + this.indent = 24, + this.nameGap = 8, + this.mainAxisSize = MainAxisSize.min, + required this.child, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + return DividerTheme( + data: const DividerThemeData(space: 40), + child: Row( + mainAxisSize: mainAxisSize, + children: [ + Expanded(child: Divider(indent: indent)), + SizedBox(width: nameGap), + DefaultTextStyle( + style: (theme.textTheme.bodyLarge ?? const TextStyle()) + .copyWith(color: theme.colors.textOnPrimary), + child: child, + ), + SizedBox(width: nameGap), + Expanded(child: Divider(endIndent: indent)), + ], + ), + ); + } +} diff --git a/catalyst_voices/lib/widgets/separators/voices_vertical_divider.dart b/catalyst_voices/lib/widgets/separators/voices_vertical_divider.dart new file mode 100644 index 00000000000..31eac94a8fb --- /dev/null +++ b/catalyst_voices/lib/widgets/separators/voices_vertical_divider.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; + +/// A vertical divider that ensures consistent color based on [DividerTheme]. +/// +/// This widget wraps [VerticalDivider] and explicitly sets its `color` +/// property to match the current [DividerTheme]. This is necessary because M3 +/// overrides the default color behavior. +/// +/// You can customize the divider's appearance using the [indent] and +/// [endIndent] properties. +class VoicesVerticalDivider extends StatelessWidget { + /// The indentation of the divider from the start of the column. + /// + /// See [VerticalDivider.indent] for more details. + final double? indent; + + /// The indentation of the divider from the end of the column. + /// + /// See [VerticalDivider.endIndent] for more details. + final double? endIndent; + + /// Optional color of divider. + final Color? color; + + const VoicesVerticalDivider({ + super.key, + this.indent, + this.endIndent, + this.color, + }); + + @override + Widget build(BuildContext context) { + return VerticalDivider( + indent: indent, + endIndent: endIndent, + // M3 will override it and use outline color that's why setting + // it explicitly. + color: color ?? DividerTheme.of(context).color, + ); + } +} diff --git a/catalyst_voices/lib/widgets/text_field/voices_email_text_field.dart b/catalyst_voices/lib/widgets/text_field/voices_email_text_field.dart index d4c98d03fdc..e39beb61a47 100644 --- a/catalyst_voices/lib/widgets/text_field/voices_email_text_field.dart +++ b/catalyst_voices/lib/widgets/text_field/voices_email_text_field.dart @@ -18,12 +18,10 @@ final class VoicesEmailTextField extends StatelessWidget { keyboardType: TextInputType.emailAddress, textInputAction: TextInputAction.next, onChanged: onChanged, - decoration: InputDecoration( - filled: true, + decoration: VoicesTextFieldDecoration( labelText: l10n.emailLabelText, hintText: l10n.emailHintText, errorText: l10n.emailErrorText, - border: const OutlineInputBorder(), ), style: const TextStyle( fontWeight: FontWeight.w500, diff --git a/catalyst_voices/lib/widgets/text_field/voices_password_text_field.dart b/catalyst_voices/lib/widgets/text_field/voices_password_text_field.dart index 61a81284c9c..e2588d06205 100644 --- a/catalyst_voices/lib/widgets/text_field/voices_password_text_field.dart +++ b/catalyst_voices/lib/widgets/text_field/voices_password_text_field.dart @@ -19,13 +19,11 @@ final class VoicesPasswordTextField extends StatelessWidget { obscureText: true, textInputAction: TextInputAction.done, onChanged: onChanged, - decoration: InputDecoration( - filled: true, + decoration: VoicesTextFieldDecoration( errorMaxLines: 2, labelText: l10n.passwordLabelText, hintText: l10n.passwordHintText, errorText: l10n.passwordErrorText, - border: const OutlineInputBorder(), ), style: const TextStyle( fontWeight: FontWeight.w500, diff --git a/catalyst_voices/lib/widgets/text_field/voices_text_field.dart b/catalyst_voices/lib/widgets/text_field/voices_text_field.dart index feef7154e31..71e1426e5fe 100644 --- a/catalyst_voices/lib/widgets/text_field/voices_text_field.dart +++ b/catalyst_voices/lib/widgets/text_field/voices_text_field.dart @@ -1,7 +1,15 @@ +import 'package:catalyst_voices/widgets/common/resizable_box_parent.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; +import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; -/// Base Voices TextField widget -class VoicesTextField extends StatelessWidget { +/// A replacement for [TextField] and [TextFormField] +/// that is pre-configured to use Project Catalyst theming. +/// +/// Uses [OutlineInputBorder] instead of the default [UnderlineInputBorder] one. +class VoicesTextField extends StatefulWidget { /// [TextField.controller] final TextEditingController? controller; @@ -9,7 +17,7 @@ class VoicesTextField extends StatelessWidget { final FocusNode? focusNode; /// [TextField.decoration] - final InputDecoration? decoration; + final VoicesTextFieldDecoration? decoration; /// [TextField.keyboardType] final TextInputType? keyboardType; @@ -26,6 +34,27 @@ class VoicesTextField extends StatelessWidget { /// [TextField.obscureText] final bool obscureText; + /// [TextField.maxLines]. + final int? maxLines; + + /// [TextField.minLines]. + final int? minLines; + + /// [TextField.maxLength]. + final int? maxLength; + + /// [TextField.enabled]. + final bool enabled; + + /// Whether the text field can be resized by the user + /// in HTML's text area fashion. + /// + /// Defaults to true on web and desktop, false otherwise. + final bool? resizable; + + /// [TextFormField.validator] + final VoicesTextFieldValidator? validator; + /// [TextField.onChanged] final ValueChanged? onChanged; @@ -33,27 +62,511 @@ class VoicesTextField extends StatelessWidget { super.key, this.controller, this.focusNode, - this.decoration = const InputDecoration(), + this.decoration, this.keyboardType, this.textInputAction, this.textCapitalization = TextCapitalization.none, this.style, this.obscureText = false, + this.maxLength, + this.maxLines = 1, + this.minLines, + this.enabled = true, + this.validator, this.onChanged, + this.resizable, }); + @override + State createState() => _VoicesTextFieldState(); +} + +class _VoicesTextFieldState extends State { + TextEditingController? _customController; + + VoicesTextFieldValidationResult _validation = + const VoicesTextFieldValidationResult(status: VoicesTextFieldStatus.none); + + @override + void initState() { + super.initState(); + _obtainController().addListener(_onChanged); + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + + _validate(_obtainController().text); + } + + @override + void didUpdateWidget(VoicesTextField oldWidget) { + super.didUpdateWidget(oldWidget); + + final newController = _obtainController(); + + // unregister listener from potentially old controllers + _customController?.removeListener(_onChanged); + oldWidget.controller?.removeListener(_onChanged); + + // the widget got controller from the parent, lets dispose our own + if (widget.controller != null) { + _customController?.dispose(); + _customController = null; + } + + // register for a new one + newController.addListener(_onChanged); + + if (oldWidget.decoration?.errorText != widget.decoration?.errorText || + oldWidget.validator != widget.validator || + oldWidget.controller?.text != newController.text) { + _validate(newController.text); + } + } + + @override + void dispose() { + _customController?.dispose(); + _customController = null; + widget.controller?.removeListener(_onChanged); + super.dispose(); + } + @override Widget build(BuildContext context) { - return TextField( - controller: controller, - focusNode: focusNode, - decoration: decoration, - keyboardType: keyboardType, - textInputAction: textInputAction, - textCapitalization: textCapitalization, - style: style, - obscureText: obscureText, - onChanged: onChanged, + final theme = Theme.of(context); + final textTheme = theme.textTheme; + + final labelText = widget.decoration?.labelText ?? ''; + final resizable = _isResizable; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (labelText.isNotEmpty) ...[ + Text( + labelText, + style: widget.enabled + ? textTheme.titleSmall + : textTheme.titleSmall! + .copyWith(color: theme.colors.textDisabled), + ), + const SizedBox(height: 4), + ], + ResizableBoxParent( + resizableHorizontally: resizable, + resizableVertically: resizable, + child: TextFormField( + textAlignVertical: TextAlignVertical.top, + expands: resizable, + controller: _obtainController(), + focusNode: widget.focusNode, + decoration: InputDecoration( + contentPadding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 12, + ), + border: widget.decoration?.border ?? + _getBorder( + orDefault: OutlineInputBorder( + borderSide: BorderSide( + color: theme.colorScheme.outlineVariant, + ), + ), + ), + enabledBorder: widget.decoration?.enabledBorder ?? + _getBorder( + orDefault: OutlineInputBorder( + borderSide: BorderSide( + color: theme.colorScheme.outlineVariant, + ), + ), + ), + disabledBorder: widget.decoration?.disabledBorder ?? + OutlineInputBorder( + borderSide: BorderSide( + color: theme.colorScheme.outline, + ), + ), + errorBorder: widget.decoration?.errorBorder ?? + OutlineInputBorder( + borderSide: BorderSide( + width: 2, + color: _getStatusColor( + orDefault: theme.colorScheme.error, + ), + ), + ), + focusedBorder: widget.decoration?.focusedBorder ?? + _getBorder( + orDefault: OutlineInputBorder( + borderSide: BorderSide( + width: 2, + color: theme.colorScheme.primary, + ), + ), + ), + focusedErrorBorder: widget.decoration?.focusedErrorBorder ?? + _getBorder( + orDefault: OutlineInputBorder( + borderSide: BorderSide( + width: 2, + color: theme.colorScheme.error, + ), + ), + ), + helperText: widget.decoration?.helperText, + helperStyle: widget.enabled + ? textTheme.bodySmall + : textTheme.bodySmall! + .copyWith(color: theme.colors.textDisabled), + hintText: widget.decoration?.hintText, + hintStyle: widget.enabled + ? textTheme.bodyLarge + : textTheme.bodyLarge! + .copyWith(color: theme.colors.textDisabled), + errorText: + widget.decoration?.errorText ?? _validation.errorMessage, + errorMaxLines: widget.decoration?.errorMaxLines, + errorStyle: widget.enabled + ? textTheme.bodySmall + : textTheme.bodySmall! + .copyWith(color: theme.colors.textDisabled), + prefixIcon: widget.decoration?.prefixIcon, + prefixText: widget.decoration?.prefixText, + suffixIcon: + widget.decoration?.suffixIcon ?? _getStatusSuffixWidget(), + suffixText: widget.decoration?.suffixText, + counterText: widget.decoration?.counterText, + counterStyle: widget.enabled + ? textTheme.bodySmall + : textTheme.bodySmall! + .copyWith(color: theme.colors.textDisabled), + ), + keyboardType: widget.keyboardType, + textInputAction: widget.textInputAction, + textCapitalization: widget.textCapitalization, + style: widget.style, + obscureText: widget.obscureText, + maxLines: widget.maxLines, + minLines: widget.minLines, + maxLength: widget.maxLength, + enabled: widget.enabled, + onChanged: widget.onChanged, + ), + ), + ], + ); + } + + bool get _isResizable { + final resizable = widget.resizable ?? + (CatalystPlatform.isWebDesktop || CatalystPlatform.isDesktop); + + // expands property is not supported if any of these are specified, + // both must be null + final hasNoLineConstraints = + widget.maxLines == null && widget.minLines == null; + + return resizable && hasNoLineConstraints; + } + + InputBorder _getBorder({required InputBorder orDefault}) { + switch (_validation.status) { + case VoicesTextFieldStatus.none: + return orDefault; + case VoicesTextFieldStatus.success: + case VoicesTextFieldStatus.warning: + case VoicesTextFieldStatus.error: + return OutlineInputBorder( + borderSide: BorderSide( + width: 2, + color: _getStatusColor( + orDefault: Colors.transparent, + ), + ), + ); + } + } + + Widget? _getStatusSuffixWidget() { + final showStatusIcon = widget.decoration?.showStatusSuffixIcon ?? true; + if (!showStatusIcon) { + return null; + } + + final icon = getStatusSuffixIcon(); + if (icon == null) { + return null; + } + + return Padding( + padding: const EdgeInsetsDirectional.only(start: 4, end: 8), + child: Icon( + getStatusSuffixIcon(), + color: _getStatusColor(orDefault: Colors.transparent), + ), + ); + } + + IconData? getStatusSuffixIcon() { + switch (_validation.status) { + case VoicesTextFieldStatus.none: + return null; + case VoicesTextFieldStatus.success: + return CatalystVoicesIcons.check_circle; + case VoicesTextFieldStatus.warning: + // TODO(dtscalac): this is not the right icon, it should be outlined + // & rounded, ask designers to provide it and update it + return Icons.warning_outlined; + case VoicesTextFieldStatus.error: + return Icons.error_outline; + } + } + + Color _getStatusColor({required Color orDefault}) { + switch (_validation.status) { + case VoicesTextFieldStatus.none: + return orDefault; + case VoicesTextFieldStatus.success: + return Theme.of(context).colors.success!; + case VoicesTextFieldStatus.warning: + return Theme.of(context).colors.warning!; + case VoicesTextFieldStatus.error: + return Theme.of(context).colorScheme.error; + } + } + + TextEditingController _obtainController() { + final providedController = widget.controller; + if (providedController != null) { + return providedController; + } + + var customController = _customController; + if (customController == null) { + customController = TextEditingController(); + _customController = customController; + } + + return customController; + } + + void _onChanged() { + _validate(_obtainController().text); + } + + void _validate(String value) { + final errorText = widget.decoration?.errorText; + if (errorText != null) { + _onValidationResultChanged( + VoicesTextFieldValidationResult( + status: VoicesTextFieldStatus.error, + errorMessage: errorText, + ), + ); + return; + } + + final result = widget.validator?.call(value); + _onValidationResultChanged( + result ?? + const VoicesTextFieldValidationResult( + status: VoicesTextFieldStatus.none, + ), ); } + + void _onValidationResultChanged(VoicesTextFieldValidationResult validation) { + if (_validation != validation) { + setState(() { + _validation = validation; + }); + } + } +} + +/// A callback called to validate the input of the [VoicesTextField]. +/// +/// This callback returns a [VoicesTextFieldValidationResult] which defines +/// both the message to be shown where the [VoicesTextFieldDecoration.errorText] +/// is traditionally shown and as well the status which might decorate +/// the text field with success checkmark or error icon depending on the status. +typedef VoicesTextFieldValidator = VoicesTextFieldValidationResult Function( + String value, +); + +/// The return value of the [VoicesTextField.validator]. +class VoicesTextFieldValidationResult with EquatableMixin { + /// The status of the validation. + /// + /// The validation can be either a success, a warning or an error. + final VoicesTextFieldStatus status; + + /// The error message to be used in case of a [warning] or an [error]. + final String? errorMessage; + + const VoicesTextFieldValidationResult({ + required this.status, + this.errorMessage, + }) : assert( + (status == VoicesTextFieldStatus.warning || + status == VoicesTextFieldStatus.error) || + errorMessage == null, + 'errorMessage can be only used for warning or error status', + ); + + @override + List get props => [status, errorMessage]; + + /// Returns a successful validation result. + /// + /// The method was designed to be used as + /// [VoicesTextField.validator] param: + /// + /// ``` + /// validator: VoicesTextFieldValidationResult.success, + /// ``` + /// + /// in cases where the text field state is known in advance + /// and dynamic validation is not needed. + static VoicesTextFieldValidator success() { + return (_) => const VoicesTextFieldValidationResult( + status: VoicesTextFieldStatus.success, + ); + } + + /// Returns a warning validation result. + /// + /// The method was designed to be used as + /// [VoicesTextField.validator] param: + /// + /// ``` + /// validator: VoicesTextFieldValidationResult.warning, + /// ``` + /// + /// in cases where the text field state is known in advance + /// and dynamic validation is not needed. + static VoicesTextFieldValidator warning([String? message]) { + return (_) => VoicesTextFieldValidationResult( + status: VoicesTextFieldStatus.warning, + errorMessage: message, + ); + } + + /// Returns an error validation result. + /// + /// The method was designed to be used as + /// [VoicesTextField.validator] param: + /// + /// ``` + /// validator: VoicesTextFieldValidationResult.error, + /// ``` + /// + /// in cases where the text field state is known in advance + /// and dynamic validation is not needed. + static VoicesTextFieldValidator error([String? message]) { + return (_) => VoicesTextFieldValidationResult( + status: VoicesTextFieldStatus.error, + errorMessage: message, + ); + } +} + +/// Defines the appearance of the [VoicesTextField]. +enum VoicesTextFieldStatus { + /// The text field appears as usual. + none, + + /// The text field represents a success, + /// i.e. if the input text is successfully validated. + success, + + /// The text field represents a warning, + /// i.e. if the input text is validated with a warning. + warning, + + /// The text field represents an error, + /// i.e. if the input text is validated with an error. + error, +} + +/// A replacement for [InputDecoration] to only expose parameters +/// that are supported by [VoicesTextField]. +class VoicesTextFieldDecoration { + /// [InputDecoration.border]. + final InputBorder? border; + + /// [InputDecoration.enabledBorder]. + final InputBorder? enabledBorder; + + /// [InputDecoration.disabledBorder]. + final InputBorder? disabledBorder; + + /// [InputDecoration.errorBorder]. + final InputBorder? errorBorder; + + /// [InputDecoration.focusedBorder]. + final InputBorder? focusedBorder; + + /// [InputDecoration.focusedErrorBorder]. + final InputBorder? focusedErrorBorder; + + /// Similar to the [InputDecoration.labelText] but does not use + /// the floating behavior and is instead above the text field instead. + final String? labelText; + + /// [InputDecoration.helperText]. + final String? helperText; + + /// [InputDecoration.hintText]. + final String? hintText; + + /// [InputDecoration.errorText]. + final String? errorText; + + /// [InputDecoration.errorMaxLines]. + final int? errorMaxLines; + + /// [InputDecoration.prefixIcon]. + final Widget? prefixIcon; + + /// [InputDecoration.prefixText]. + final String? prefixText; + + /// [InputDecoration.suffixIcon]. + final Widget? suffixIcon; + + /// [InputDecoration.suffixText]. + final String? suffixText; + + /// [InputDecoration.counterText]. + final String? counterText; + + /// Whether the [VoicesTextField] will automatically + /// add a status [suffixIcon] based on the results of the validation. + final bool showStatusSuffixIcon; + + /// Creates a new text field decoration. + const VoicesTextFieldDecoration({ + this.border, + this.enabledBorder, + this.disabledBorder, + this.errorBorder, + this.focusedBorder, + this.focusedErrorBorder, + this.labelText, + this.helperText, + this.hintText, + this.errorText, + this.errorMaxLines, + this.prefixIcon, + this.prefixText, + this.suffixIcon, + this.suffixText, + this.counterText, + this.showStatusSuffixIcon = true, + }); } diff --git a/catalyst_voices/lib/widgets/toggles/voices_switch.dart b/catalyst_voices/lib/widgets/toggles/voices_switch.dart new file mode 100644 index 00000000000..0780d3cec95 --- /dev/null +++ b/catalyst_voices/lib/widgets/toggles/voices_switch.dart @@ -0,0 +1,47 @@ +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:flutter/material.dart'; + +/// A Voices version of a [Switch] widget with optional thumb icon +/// customization. +/// +/// This widget provides a basic switch functionality with the ability to +/// display a custom icon on the thumb. +class VoicesSwitch extends StatelessWidget { + /// The current state of the switch. + final bool value; + + /// An optional icon to display on the switch thumb. + final IconData? thumbIcon; + + /// A callback function triggered when the switch value changes. + final ValueChanged? onChanged; + + /// Creates a [VoicesSwitch] widget. + /// + /// The [value] argument is required and specifies the initial state + /// of the switch. + const VoicesSwitch({ + super.key, + required this.value, + this.thumbIcon, + this.onChanged, + }); + + @override + Widget build(BuildContext context) { + final thumbIcon = this.thumbIcon; + + return Switch( + value: value, + onChanged: onChanged, + thumbIcon: thumbIcon != null + ? WidgetStatePropertyAll( + Icon( + thumbIcon, + color: Theme.of(context).colors.iconsForeground, + ), + ) + : null, + ); + } +} diff --git a/catalyst_voices/lib/widgets/tooltips/voices_plain_tooltip.dart b/catalyst_voices/lib/widgets/tooltips/voices_plain_tooltip.dart new file mode 100644 index 00000000000..3ea8724555a --- /dev/null +++ b/catalyst_voices/lib/widgets/tooltips/voices_plain_tooltip.dart @@ -0,0 +1,55 @@ +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:flutter/material.dart'; + +/// A simple tooltip widget with a plain text message and a child widget. +/// +/// **Notes:** +/// - The tooltip's colors might need to be adjusted based on the final design. +/// - The tooltip's text is constrained to a maximum width of 200 pixels. +/// +/// **Usage:** +/// ```dart +/// VoicesPlainTooltip( +/// message: "This is a tooltip message.", +/// child: Icon(Icons.info), +/// ) +/// ``` +class VoicesPlainTooltip extends StatelessWidget { + /// The text message to display in the tooltip. + final String message; + + /// The widget that triggers tooltip visibility. + final Widget child; + + const VoicesPlainTooltip({ + super.key, + required this.message, + required this.child, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + return Tooltip( + richMessage: WidgetSpan( + child: ConstrainedBox( + key: const ValueKey('VoicesPlainTooltipContentKey'), + constraints: const BoxConstraints(maxWidth: 200), + child: Text( + message, + style: theme.textTheme.bodySmall?.copyWith( + color: theme.colors.iconsBackground, + ), + ), + ), + ), + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: theme.colors.iconsForeground, + borderRadius: BorderRadius.circular(4), + ), + child: child, + ); + } +} diff --git a/catalyst_voices/lib/widgets/tooltips/voices_rich_tooltip.dart b/catalyst_voices/lib/widgets/tooltips/voices_rich_tooltip.dart new file mode 100644 index 00000000000..3af6d65c558 --- /dev/null +++ b/catalyst_voices/lib/widgets/tooltips/voices_rich_tooltip.dart @@ -0,0 +1,171 @@ +import 'package:catalyst_voices/widgets/buttons/voices_text_button.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; +import 'package:flutter/material.dart'; + +final class VoicesRichTooltipActionData { + final String name; + final VoidCallback onTap; + + VoicesRichTooltipActionData({ + required this.name, + required this.onTap, + }); +} + +/// A tooltip widget with a rich text message (title and message) and +/// optional actions displayed at the bottom. +/// +/// **Notes:** +/// - The tooltip's maximum width is constrained to 312 pixels. +/// - The tooltip's background color and shadow are theme-dependent. +/// - If no actions are provided, the tooltip can be dismissed by tapping +/// anywhere on it. Otherwise, tapping will only trigger the action button +/// taps which will dismiss all tooltips see [Tooltip.dismissAllToolTips]. +/// +/// **Example Usage:** +/// ```dart +/// final actions = [ +/// VoicesRichTooltipActionData( +/// name: "Edit", +/// onTap: () => print("Edit tapped"), +/// ), +/// VoicesRichTooltipActionData( +/// name: "Delete", +/// onTap: () => print("Delete tapped"), +/// ), +/// ]; +/// +/// VoicesRichTooltip( +/// title: "Tooltip Title", +/// message: "This is a tooltip with a descriptive message.", +/// actions: actions, +/// child: Icon(Icons.info), +/// ) +/// ``` +class VoicesRichTooltip extends StatelessWidget { + /// The main title displayed at the top of the tooltip. + final String title; + + /// The descriptive message displayed below the title. + final String message; + + /// (Optional) A list of action buttons displayed at the + /// bottom of the tooltip. Each action has a `name` and an `onTap` callback. + final List actions; + + /// The widget that triggers tooltip visibility. + final Widget child; + + const VoicesRichTooltip({ + super.key, + required this.title, + required this.message, + this.actions = const [], + required this.child, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final isLightTheme = theme.brightness == Brightness.light; + + return Tooltip( + richMessage: WidgetSpan( + child: ConstrainedBox( + key: const ValueKey('VoicesRichTooltipContentKey'), + constraints: const BoxConstraints(maxWidth: 312), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + _Content(title, message), + if (actions.isNotEmpty) ...[ + const SizedBox(height: 8), + _ActionsRow(actions), + ], + const SizedBox(height: 8), + ], + ), + ), + ), + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: theme.colors.onSurfaceNeutralOpaqueLv2, + borderRadius: BorderRadius.circular(12), + boxShadow: isLightTheme ? kElevationToShadow[2] : null, + ), + enableTapToDismiss: actions.isEmpty, + child: child, + ); + } +} + +class _Content extends StatelessWidget { + final String title; + final String message; + + const _Content( + this.title, + this.message, + ); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + return Padding( + padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 16) + .add(const EdgeInsets.only(top: 8)), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: theme.textTheme.labelLarge?.copyWith( + color: theme.colors.textPrimary, + ), + textAlign: TextAlign.start, + ), + const SizedBox(height: 4), + Text( + message, + style: theme.textTheme.bodyMedium?.copyWith( + color: theme.colors.textPrimary, + ), + textAlign: TextAlign.start, + ), + ], + ), + ); + } +} + +class _ActionsRow extends StatelessWidget { + const _ActionsRow(this.actions); + + final List actions; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Row( + children: actions + .map( + (action) { + return VoicesTextButton( + onTap: () { + Tooltip.dismissAllToolTips(); + action.onTap(); + }, + child: Text(action.name), + ); + }, + ) + .separatedBy(const SizedBox(width: 8)) + .toList(), + ), + ); + } +} diff --git a/catalyst_voices/lib/widgets/widgets.dart b/catalyst_voices/lib/widgets/widgets.dart index 30c4c8cc5ee..9e55ddc7b68 100644 --- a/catalyst_voices/lib/widgets/widgets.dart +++ b/catalyst_voices/lib/widgets/widgets.dart @@ -1,11 +1,52 @@ +export 'app_bar/voices_app_bar.dart'; +export 'avatars/voices_avatar.dart'; +export 'buttons/voices_buttons.dart'; export 'buttons/voices_filled_button.dart'; export 'buttons/voices_icon_button.dart'; export 'buttons/voices_outlined_button.dart'; export 'buttons/voices_segmented_button.dart'; export 'buttons/voices_text_button.dart'; +export 'cards/funded_proposal_card.dart'; +export 'chips/voices_chip.dart'; +export 'common/link_text.dart'; +export 'common/navigation_location.dart'; +export 'common/simple_tree_view.dart'; +export 'common/tab_bar_stack_view.dart'; +export 'containers/sidebar_scaffold.dart'; +export 'containers/space_scaffold.dart'; +export 'containers/space_side_panel.dart'; +export 'containers/workspace_tile_container.dart'; +export 'drawer/voices_drawer.dart'; +export 'drawer/voices_drawer_nav_item.dart'; +export 'drawer/voices_drawer_space_chooser.dart'; +export 'footers/links_page_footer.dart'; +export 'footers/standard_links_page_footer.dart'; +export 'headers/section_header.dart'; +export 'headers/segment_header.dart'; +export 'indicators/process_progress_indicator.dart'; +export 'indicators/voices_circular_progress_indicator.dart'; +export 'indicators/voices_linear_progress_indicator.dart'; +export 'indicators/voices_status_indicator.dart'; +export 'menu/voices_list_tile.dart'; +export 'menu/voices_menu.dart'; +export 'menu/voices_node_menu.dart'; +export 'menu/voices_wallet_tile.dart'; +export 'modals/voices_desktop_dialog.dart'; +export 'modals/voices_dialog.dart'; +export 'modals/voices_info_dialog.dart'; +export 'seed_phrase/seed_phrases_completer.dart'; +export 'seed_phrase/seed_phrases_picker.dart'; +export 'seed_phrase/seed_phrases_sequencer.dart'; +export 'seed_phrase/seed_phrases_viewer.dart'; +export 'separators/voices_divider.dart'; +export 'separators/voices_text_divider.dart'; +export 'separators/voices_vertical_divider.dart'; export 'text_field/voices_email_text_field.dart'; export 'text_field/voices_password_text_field.dart'; export 'text_field/voices_text_field.dart'; export 'toggles/voices_checkbox.dart'; export 'toggles/voices_checkbox_group.dart'; export 'toggles/voices_radio.dart'; +export 'toggles/voices_switch.dart'; +export 'tooltips/voices_plain_tooltip.dart'; +export 'tooltips/voices_rich_tooltip.dart'; diff --git a/catalyst_voices/macos/Flutter/GeneratedPluginRegistrant.swift b/catalyst_voices/macos/Flutter/GeneratedPluginRegistrant.swift index 91bdbe11660..200cbdf930b 100644 --- a/catalyst_voices/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/catalyst_voices/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,16 +5,30 @@ import FlutterMacOS import Foundation +import device_info_plus +import file_selector_macos +import flutter_inappwebview_macos import flutter_secure_storage_macos +import gal +import irondash_engine_context import package_info_plus import path_provider_foundation import sentry_flutter +import super_native_extensions import url_launcher_macos +import video_player_avfoundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) + FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) + InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin")) FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) + GalPlugin.register(with: registry.registrar(forPlugin: "GalPlugin")) + IrondashEngineContextPlugin.register(with: registry.registrar(forPlugin: "IrondashEngineContextPlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SentryFlutterPlugin.register(with: registry.registrar(forPlugin: "SentryFlutterPlugin")) + SuperNativeExtensionsPlugin.register(with: registry.registrar(forPlugin: "SuperNativeExtensionsPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) + FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin")) } diff --git a/catalyst_voices/macos/Podfile.lock b/catalyst_voices/macos/Podfile.lock index d889f28268c..dcbae2b4f5e 100644 --- a/catalyst_voices/macos/Podfile.lock +++ b/catalyst_voices/macos/Podfile.lock @@ -7,11 +7,11 @@ PODS: - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS - - Sentry/HybridSDK (8.29.0) - - sentry_flutter (8.3.0): + - Sentry/HybridSDK (8.35.1) + - sentry_flutter (8.8.0): - Flutter - FlutterMacOS - - Sentry/HybridSDK (= 8.29.0) + - Sentry/HybridSDK (= 8.35.1) - url_launcher_macos (0.0.1): - FlutterMacOS @@ -46,8 +46,8 @@ SPEC CHECKSUMS: FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 package_info_plus: fa739dd842b393193c5ca93c26798dff6e3d0e0c path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 - Sentry: 016de45ee5ce5fca2a829996f1bfafeb5e62e8b4 - sentry_flutter: 5fb57c5b7e6427a9dc1fedde4269eb65823982d4 + Sentry: 1fe34e9c2cbba1e347623610d26db121dcb569f1 + sentry_flutter: a39c2a2d67d5e5b9cb0b94a4985c76dd5b3fc737 url_launcher_macos: 5f437abeda8c85500ceb03f5c1938a8c5a705399 PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 diff --git a/catalyst_voices/macos/Runner/AppDelegate.swift b/catalyst_voices/macos/Runner/AppDelegate.swift index d53ef643772..8e02df28883 100644 --- a/catalyst_voices/macos/Runner/AppDelegate.swift +++ b/catalyst_voices/macos/Runner/AppDelegate.swift @@ -1,7 +1,7 @@ import Cocoa import FlutterMacOS -@NSApplicationMain +@main class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true diff --git a/catalyst_voices/nginx.conf b/catalyst_voices/nginx.conf index 385df2501bc..8fdfc1091eb 100644 --- a/catalyst_voices/nginx.conf +++ b/catalyst_voices/nginx.conf @@ -22,6 +22,11 @@ http { index index.html; try_files $uri $uri/ /index.html; } + # Ensure that /m4 (and any other SPA path) serves index.html + location /m4 { + root /app; + try_files $uri $uri/ /index.html; + } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; diff --git a/catalyst_voices/packages/catalyst_voices_assets/analysis_options.yaml b/catalyst_voices/packages/catalyst_voices_assets/analysis_options.yaml new file mode 100644 index 00000000000..adb98a6554b --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_assets/analysis_options.yaml @@ -0,0 +1,14 @@ +include: package:catalyst_analysis/analysis_options.yaml + +analyzer: + exclude: + [ + build/**, + lib/*.g.dart, + lib/generated/**, + lib/src/catalyst_voices_icons.dart, + ] + +linter: + rules: + public_member_api_docs: false diff --git a/catalyst_voices/packages/catalyst_voices_assets/assets/colors/colors.xml b/catalyst_voices/packages/catalyst_voices_assets/assets/colors/colors.xml index 1f9493f3707..f38784a7ea4 100644 --- a/catalyst_voices/packages/catalyst_voices_assets/assets/colors/colors.xml +++ b/catalyst_voices/packages/catalyst_voices_assets/assets/colors/colors.xml @@ -5,6 +5,7 @@ #FFFFFF #61212A3D #123CD3 + #E8ECFD #FFFFFF #A1B4F7 #081B5E @@ -44,6 +45,7 @@ #29CC0000 #212A3D #FFFFFF + #FFFFFF #61212A3D #123CD3 #C014EB @@ -56,6 +58,9 @@ #FDE1CE #FFD1D1 #FFFFFF + #F2F4F8 + #FFFFFF + #E6E9F0 #FFD9DEE8 #61BFC8D9 #FFFFFF @@ -63,6 +68,7 @@ #0C288D #61D9DEE8 #728EF3 + #364463 #0C288D #1035BC #E8ECFD @@ -102,6 +108,7 @@ #29CC0000 #F2F4F8 #212A3D + #FFFFFF #61BFC8D9 #728EF3 #DF8AF5 @@ -114,6 +121,9 @@ #B64E07 #AD0000 #121721 + #212A3D + #2D3953 + #212A3D #FF7F90B3 #FF364463 diff --git a/catalyst_voices/packages/catalyst_voices_assets/assets/images/dragger.svg b/catalyst_voices/packages/catalyst_voices_assets/assets/images/dragger.svg new file mode 100644 index 00000000000..221e3c3b9c3 --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_assets/assets/images/dragger.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/catalyst_voices/packages/catalyst_voices_assets/assets/images/facebook.svg b/catalyst_voices/packages/catalyst_voices_assets/assets/images/facebook.svg new file mode 100644 index 00000000000..34b2a817abd --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_assets/assets/images/facebook.svg @@ -0,0 +1,4 @@ + + + + diff --git a/catalyst_voices/packages/catalyst_voices_assets/assets/images/facebook_mono.svg b/catalyst_voices/packages/catalyst_voices_assets/assets/images/facebook_mono.svg new file mode 100644 index 00000000000..5648e046148 --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_assets/assets/images/facebook_mono.svg @@ -0,0 +1,3 @@ + + + diff --git a/catalyst_voices/packages/catalyst_voices_assets/assets/images/linkedin.svg b/catalyst_voices/packages/catalyst_voices_assets/assets/images/linkedin.svg new file mode 100644 index 00000000000..5766224d0da --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_assets/assets/images/linkedin.svg @@ -0,0 +1,3 @@ + + + diff --git a/catalyst_voices/packages/catalyst_voices_assets/assets/images/linkedin_mono.svg b/catalyst_voices/packages/catalyst_voices_assets/assets/images/linkedin_mono.svg new file mode 100644 index 00000000000..4d598f0af6b --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_assets/assets/images/linkedin_mono.svg @@ -0,0 +1,3 @@ + + + diff --git a/catalyst_voices/packages/catalyst_voices_assets/assets/images/node-closed.svg b/catalyst_voices/packages/catalyst_voices_assets/assets/images/node-closed.svg new file mode 100644 index 00000000000..69f60bf103f --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_assets/assets/images/node-closed.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/catalyst_voices/packages/catalyst_voices_assets/assets/images/node_open.svg b/catalyst_voices/packages/catalyst_voices_assets/assets/images/node_open.svg new file mode 100644 index 00000000000..66f73d6d984 --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_assets/assets/images/node_open.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/catalyst_voices/packages/catalyst_voices_assets/assets/images/proposal_background_1.webp b/catalyst_voices/packages/catalyst_voices_assets/assets/images/proposal_background_1.webp new file mode 100644 index 00000000000..7136a7e9151 Binary files /dev/null and b/catalyst_voices/packages/catalyst_voices_assets/assets/images/proposal_background_1.webp differ diff --git a/catalyst_voices/packages/catalyst_voices_assets/assets/images/proposal_background_2.webp b/catalyst_voices/packages/catalyst_voices_assets/assets/images/proposal_background_2.webp new file mode 100644 index 00000000000..f93fc1fdc43 Binary files /dev/null and b/catalyst_voices/packages/catalyst_voices_assets/assets/images/proposal_background_2.webp differ diff --git a/catalyst_voices/packages/catalyst_voices_assets/assets/images/view_grid.svg b/catalyst_voices/packages/catalyst_voices_assets/assets/images/view_grid.svg new file mode 100644 index 00000000000..40284259275 --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_assets/assets/images/view_grid.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/catalyst_voices/packages/catalyst_voices_assets/assets/images/x.svg b/catalyst_voices/packages/catalyst_voices_assets/assets/images/x.svg new file mode 100644 index 00000000000..7a66fb88d59 --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_assets/assets/images/x.svg @@ -0,0 +1,3 @@ + + + diff --git a/catalyst_voices/packages/catalyst_voices_assets/assets/images/x_mono.svg b/catalyst_voices/packages/catalyst_voices_assets/assets/images/x_mono.svg new file mode 100644 index 00000000000..c90b249fe17 --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_assets/assets/images/x_mono.svg @@ -0,0 +1,3 @@ + + + diff --git a/catalyst_voices/packages/catalyst_voices_assets/example/lib/src/main.dart b/catalyst_voices/packages/catalyst_voices_assets/example/lib/src/main.dart index 537effa13d3..90232ca4892 100644 --- a/catalyst_voices/packages/catalyst_voices_assets/example/lib/src/main.dart +++ b/catalyst_voices/packages/catalyst_voices_assets/example/lib/src/main.dart @@ -1,7 +1,7 @@ import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; import 'package:flutter/material.dart'; -void main() => runApp(Example()); +void main() => runApp(const Example()); final class Example extends StatelessWidget { const Example({super.key}); diff --git a/catalyst_voices/packages/catalyst_voices_assets/example/pubspec.yaml b/catalyst_voices/packages/catalyst_voices_assets/example/pubspec.yaml index 6c9a219d1dc..1a2532aae13 100644 --- a/catalyst_voices/packages/catalyst_voices_assets/example/pubspec.yaml +++ b/catalyst_voices/packages/catalyst_voices_assets/example/pubspec.yaml @@ -4,8 +4,8 @@ version: 0.1.0+1 publish_to: none environment: - sdk: ">=3.3.4 <4.0.0" - flutter: ">=3.22.1" + sdk: ">=3.5.0 <4.0.0" + flutter: ">=3.24.1" dependencies: catalyst_voices_assets: diff --git a/catalyst_voices/packages/catalyst_voices_assets/internal_resources/fonts/CatalystVoicesIcons.ttf b/catalyst_voices/packages/catalyst_voices_assets/internal_resources/fonts/CatalystVoicesIcons.ttf index b3a1f614f74..430ba30bd30 100644 Binary files a/catalyst_voices/packages/catalyst_voices_assets/internal_resources/fonts/CatalystVoicesIcons.ttf and b/catalyst_voices/packages/catalyst_voices_assets/internal_resources/fonts/CatalystVoicesIcons.ttf differ diff --git a/catalyst_voices/packages/catalyst_voices_assets/lib/generated/assets.gen.dart b/catalyst_voices/packages/catalyst_voices_assets/lib/generated/assets.gen.dart index 13945b82808..ad41ff51fec 100644 --- a/catalyst_voices/packages/catalyst_voices_assets/lib/generated/assets.gen.dart +++ b/catalyst_voices/packages/catalyst_voices_assets/lib/generated/assets.gen.dart @@ -7,10 +7,10 @@ // ignore_for_file: type=lint // ignore_for_file: directives_ordering,unnecessary_import,implicit_dynamic_list_literal,deprecated_member_use -import 'package:flutter/widgets.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_svg/flutter_svg.dart'; -import 'package:vector_graphics/vector_graphics.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_svg/flutter_svg.dart' as _svg; +import 'package:vector_graphics/vector_graphics.dart' as _vg; class $AssetsImagesGen { const $AssetsImagesGen(); @@ -35,10 +35,20 @@ class $AssetsImagesGen { AssetGenImage get comingSoonBkg => const AssetGenImage('assets/images/coming_soon_bkg.webp'); + /// File path: assets/images/dragger.svg + SvgGenImage get dragger => const SvgGenImage('assets/images/dragger.svg'); + /// File path: assets/images/dummy_catalyst_voices.webp AssetGenImage get dummyCatalystVoices => const AssetGenImage('assets/images/dummy_catalyst_voices.webp'); + /// File path: assets/images/facebook.svg + SvgGenImage get facebook => const SvgGenImage('assets/images/facebook.svg'); + + /// File path: assets/images/facebook_mono.svg + SvgGenImage get facebookMono => + const SvgGenImage('assets/images/facebook_mono.svg'); + /// File path: assets/images/fallback_logo.svg SvgGenImage get fallbackLogo => const SvgGenImage('assets/images/fallback_logo.svg'); @@ -47,6 +57,37 @@ class $AssetsImagesGen { SvgGenImage get fallbackLogoIcon => const SvgGenImage('assets/images/fallback_logo_icon.svg'); + /// File path: assets/images/linkedin.svg + SvgGenImage get linkedin => const SvgGenImage('assets/images/linkedin.svg'); + + /// File path: assets/images/linkedin_mono.svg + SvgGenImage get linkedinMono => + const SvgGenImage('assets/images/linkedin_mono.svg'); + + /// File path: assets/images/node-closed.svg + SvgGenImage get nodeClosed => + const SvgGenImage('assets/images/node-closed.svg'); + + /// File path: assets/images/node_open.svg + SvgGenImage get nodeOpen => const SvgGenImage('assets/images/node_open.svg'); + + /// File path: assets/images/proposal_background_1.webp + AssetGenImage get proposalBackground1 => + const AssetGenImage('assets/images/proposal_background_1.webp'); + + /// File path: assets/images/proposal_background_2.webp + AssetGenImage get proposalBackground2 => + const AssetGenImage('assets/images/proposal_background_2.webp'); + + /// File path: assets/images/view_grid.svg + SvgGenImage get viewGrid => const SvgGenImage('assets/images/view_grid.svg'); + + /// File path: assets/images/x.svg + SvgGenImage get x => const SvgGenImage('assets/images/x.svg'); + + /// File path: assets/images/x_mono.svg + SvgGenImage get xMono => const SvgGenImage('assets/images/x_mono.svg'); + /// List of all assets List get values => [ catalystLogo, @@ -54,9 +95,21 @@ class $AssetsImagesGen { catalystLogoIconWhite, catalystLogoWhite, comingSoonBkg, + dragger, dummyCatalystVoices, + facebook, + facebookMono, fallbackLogo, - fallbackLogoIcon + fallbackLogoIcon, + linkedin, + linkedinMono, + nodeClosed, + nodeOpen, + proposalBackground1, + proposalBackground2, + viewGrid, + x, + xMono ]; } @@ -67,11 +120,16 @@ class VoicesAssets { } class AssetGenImage { - const AssetGenImage(this._assetName, {this.size = null}); + const AssetGenImage( + this._assetName, { + this.size, + this.flavors = const {}, + }); final String _assetName; final Size? size; + final Set flavors; Image image({ Key? key, @@ -145,20 +203,22 @@ class AssetGenImage { class SvgGenImage { const SvgGenImage( this._assetName, { - this.size = null, + this.size, + this.flavors = const {}, }) : _isVecFormat = false; const SvgGenImage.vec( this._assetName, { - this.size = null, + this.size, + this.flavors = const {}, }) : _isVecFormat = true; final String _assetName; - final Size? size; + final Set flavors; final bool _isVecFormat; - SvgPicture svg({ + _svg.SvgPicture svg({ Key? key, bool matchTextDirection = false, AssetBundle? bundle, @@ -171,19 +231,30 @@ class SvgGenImage { WidgetBuilder? placeholderBuilder, String? semanticsLabel, bool excludeFromSemantics = false, - SvgTheme? theme, + _svg.SvgTheme? theme, ColorFilter? colorFilter, Clip clipBehavior = Clip.hardEdge, @deprecated Color? color, @deprecated BlendMode colorBlendMode = BlendMode.srcIn, @deprecated bool cacheColorFilter = false, }) { - return SvgPicture( - _isVecFormat - ? AssetBytesLoader(_assetName, - assetBundle: bundle, packageName: package) - : SvgAssetLoader(_assetName, - assetBundle: bundle, packageName: package), + final _svg.BytesLoader loader; + if (_isVecFormat) { + loader = _vg.AssetBytesLoader( + _assetName, + assetBundle: bundle, + packageName: package, + ); + } else { + loader = _svg.SvgAssetLoader( + _assetName, + assetBundle: bundle, + packageName: package, + theme: theme, + ); + } + return _svg.SvgPicture( + loader, key: key, matchTextDirection: matchTextDirection, width: width, @@ -194,7 +265,6 @@ class SvgGenImage { placeholderBuilder: placeholderBuilder, semanticsLabel: semanticsLabel, excludeFromSemantics: excludeFromSemantics, - theme: theme, colorFilter: colorFilter ?? (color == null ? null : ColorFilter.mode(color, colorBlendMode)), clipBehavior: clipBehavior, diff --git a/catalyst_voices/packages/catalyst_voices_assets/lib/generated/colors.gen.dart b/catalyst_voices/packages/catalyst_voices_assets/lib/generated/colors.gen.dart index de50db4771d..ed85446334e 100644 --- a/catalyst_voices/packages/catalyst_voices_assets/lib/generated/colors.gen.dart +++ b/catalyst_voices/packages/catalyst_voices_assets/lib/generated/colors.gen.dart @@ -31,6 +31,15 @@ class VoicesColors { /// Color: #121721 static const Color darkElevationsOnSurfaceNeutralLv0 = Color(0xFF121721); + /// Color: #212A3D + static const Color darkElevationsOnSurfaceNeutralLv1Grey = Color(0xFF212A3D); + + /// Color: #2D3953 + static const Color darkElevationsOnSurfaceNeutralLv1White = Color(0xFF2D3953); + + /// Color: #212A3D + static const Color darkElevationsOnSurfaceNeutralLv2 = Color(0xFF212A3D); + /// Color: #FF9999 static const Color darkError = Color(0xFFFF9999); @@ -49,6 +58,9 @@ class VoicesColors { /// Color: #F2F4F8 static const Color darkIconsForeground = Color(0xFFF2F4F8); + /// Color: #FFFFFF + static const Color darkIconsOnImage = Color(0xFFFFFFFF); + /// Color: #728EF3 static const Color darkIconsPrimary = Color(0xFF728EF3); @@ -154,6 +166,9 @@ class VoicesColors { /// Color: #728EF3 static const Color darkPrimary = Color(0xFF728EF3); + /// Color: #364463 + static const Color darkPrimary98 = Color(0xFF364463); + /// Color: #1035BC static const Color darkPrimaryContainer = Color(0xFF1035BC); @@ -205,6 +220,16 @@ class VoicesColors { /// Color: #FFFFFF static const Color lightElevationsOnSurfaceNeutralLv0 = Color(0xFFFFFFFF); + /// Color: #F2F4F8 + static const Color lightElevationsOnSurfaceNeutralLv1Grey = Color(0xFFF2F4F8); + + /// Color: #FFFFFF + static const Color lightElevationsOnSurfaceNeutralLv1White = + Color(0xFFFFFFFF); + + /// Color: #E6E9F0 + static const Color lightElevationsOnSurfaceNeutralLv2 = Color(0xFFE6E9F0); + /// Color: #CC0000 static const Color lightError = Color(0xFFCC0000); @@ -223,6 +248,9 @@ class VoicesColors { /// Color: #212A3D static const Color lightIconsForeground = Color(0xFF212A3D); + /// Color: #FFFFFF + static const Color lightIconsOnImage = Color(0xFFFFFFFF); + /// Color: #123CD3 static const Color lightIconsPrimary = Color(0xFF123CD3); @@ -328,6 +356,9 @@ class VoicesColors { /// Color: #123CD3 static const Color lightPrimary = Color(0xFF123CD3); + /// Color: #E8ECFD + static const Color lightPrimary98 = Color(0xFFE8ECFD); + /// Color: #A1B4F7 static const Color lightPrimaryContainer = Color(0xFFA1B4F7); diff --git a/catalyst_voices/packages/catalyst_voices_assets/lib/src/assets_ext.dart b/catalyst_voices/packages/catalyst_voices_assets/lib/src/assets_ext.dart new file mode 100644 index 00000000000..7040cc09872 --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_assets/lib/src/assets_ext.dart @@ -0,0 +1,125 @@ +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart' + show SvgTheme, svg, SvgAssetLoader, ColorMapper; + +extension SvgGenImageExt on SvgGenImage { + /// Pre caching this svg if not already cached. + Future cache({ + BuildContext? context, + String package = 'catalyst_voices_assets', + AssetBundle? bundle, + ColorMapper? colorMapper, + }) { + final loader = SvgAssetLoader( + path, + packageName: package, + assetBundle: bundle, + colorMapper: colorMapper, + ); + return svg.cache.putIfAbsent( + loader.cacheKey(context), + () => loader.loadBytes(context), + ); + } + + /// Builds [CatalystSvgPicture]. See class for more context. + Widget buildPicture({ + Key? key, + AssetBundle? bundle, + String? semanticsLabel, + bool excludeFromSemantics = false, + double? width, + double? height, + BoxFit fit = BoxFit.contain, + Alignment alignment = Alignment.center, + bool matchTextDirection = false, + String package = 'catalyst_voices_assets', + bool allowDrawingOutsideViewBox = false, + WidgetBuilder? placeholderBuilder, + Clip clipBehavior = Clip.hardEdge, + SvgTheme? theme, + ColorFilter? colorFilter, + }) { + return CatalystSvgPicture.asset( + path, + key: key, + bundle: bundle, + semanticsLabel: semanticsLabel, + excludeFromSemantics: excludeFromSemantics, + width: width, + height: height, + fit: fit, + alignment: alignment, + matchTextDirection: matchTextDirection, + package: package, + allowDrawingOutsideViewBox: allowDrawingOutsideViewBox, + placeholderBuilder: placeholderBuilder, + clipBehavior: clipBehavior, + theme: theme, + colorFilter: colorFilter, + ); + } + + /// See [CatalystSvgIcon]. See class for more context. + Widget buildIcon({ + Key? key, + AssetBundle? bundle, + String? semanticsLabel, + bool excludeFromSemantics = false, + double? size, + BoxFit fit = BoxFit.contain, + Alignment alignment = Alignment.center, + bool matchTextDirection = false, + String package = 'catalyst_voices_assets', + bool allowDrawingOutsideViewBox = false, + WidgetBuilder? placeholderBuilder, + Clip clipBehavior = Clip.hardEdge, + SvgTheme? theme, + ColorFilter? colorFilter, + bool allowColorFilter = true, + }) { + return CatalystSvgIcon.asset( + path, + key: key, + bundle: bundle, + semanticsLabel: semanticsLabel, + excludeFromSemantics: excludeFromSemantics, + size: size, + fit: fit, + alignment: alignment, + matchTextDirection: matchTextDirection, + package: package, + allowDrawingOutsideViewBox: allowDrawingOutsideViewBox, + placeholderBuilder: placeholderBuilder, + clipBehavior: clipBehavior, + theme: theme, + colorFilter: colorFilter, + allowColorFilter: allowColorFilter, + ); + } +} + +extension AssetGenImageExt on AssetGenImage { + Future cache({ + required BuildContext context, + AssetBundle? bundle, + String package = 'catalyst_voices_assets', + Size? size, + ImageErrorListener? onError, + }) { + final loader = AssetImage( + path, + bundle: bundle, + package: package, + ); + + return precacheImage( + loader, + context, + size: size, + onError: onError, + ); + } +} diff --git a/catalyst_voices/packages/catalyst_voices_assets/lib/src/catalyst_image.dart b/catalyst_voices/packages/catalyst_voices_assets/lib/src/catalyst_image.dart index 35845744505..0bd88c66bbe 100644 --- a/catalyst_voices/packages/catalyst_voices_assets/lib/src/catalyst_image.dart +++ b/catalyst_voices/packages/catalyst_voices_assets/lib/src/catalyst_image.dart @@ -7,52 +7,29 @@ import 'package:flutter/widgets.dart'; /// For more information, see [Image.asset]. final class CatalystImage extends Image { CatalystImage.asset( - String name, { - AssetBundle? bundle, - ImageFrameBuilder? frameBuilder, - ImageErrorWidgetBuilder? errorBuilder, - String? semanticLabel, - bool excludeFromSemantics = false, - double? scale, - double? width, - double? height, - Color? color, - Animation? opacity, - BlendMode? colorBlendMode, - BoxFit? fit, - Alignment alignment = Alignment.center, - ImageRepeat repeat = ImageRepeat.noRepeat, - Rect? centerSlice, - bool matchTextDirection = false, - bool gaplessPlayback = false, - bool isAntiAlias = false, - String package = 'catalyst_voices_assets', - FilterQuality filterQuality = FilterQuality.low, - int? cacheWidth, - int? cacheHeight, - }) : super.asset( - name, - bundle: bundle, - frameBuilder: frameBuilder, - errorBuilder: errorBuilder, - semanticLabel: semanticLabel, - excludeFromSemantics: excludeFromSemantics, - scale: scale, - width: width, - height: height, - color: color, - opacity: opacity, - colorBlendMode: colorBlendMode, - fit: fit, - alignment: alignment, - repeat: repeat, - centerSlice: centerSlice, - matchTextDirection: matchTextDirection, - gaplessPlayback: gaplessPlayback, - isAntiAlias: isAntiAlias, - filterQuality: filterQuality, - cacheWidth: cacheWidth, - cacheHeight: cacheHeight, - package: package, - ); + super.name, { + super.key, + super.bundle, + super.frameBuilder, + super.errorBuilder, + super.semanticLabel, + super.excludeFromSemantics, + super.scale, + super.width, + super.height, + super.color, + super.opacity, + super.colorBlendMode, + super.fit, + Alignment super.alignment, + super.repeat, + super.centerSlice, + super.matchTextDirection, + super.gaplessPlayback, + super.isAntiAlias, + String super.package = 'catalyst_voices_assets', + super.filterQuality = FilterQuality.low, + super.cacheWidth, + super.cacheHeight, + }) : super.asset(); } diff --git a/catalyst_voices/packages/catalyst_voices_assets/lib/src/catalyst_svg_icon.dart b/catalyst_voices/packages/catalyst_voices_assets/lib/src/catalyst_svg_icon.dart new file mode 100644 index 00000000000..9030dc8cc1a --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_assets/lib/src/catalyst_svg_icon.dart @@ -0,0 +1,115 @@ +import 'package:catalyst_voices_assets/src/catalyst_svg_picture.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +/// Builds an svg icon using [SvgPicture] via [CatalystSvgPicture] but on +/// top of that extracts configuration from [IconTheme] like +/// [IconThemeData.size] or [IconThemeData.color] and applies those if not +/// specified directly in [size] or [colorFilter]. +class CatalystSvgIcon extends StatelessWidget { + /// See [SvgPicture.width] and [SvgPicture.height] + final double? size; + + /// See [SvgPicture.fit] + final BoxFit fit; + + /// See [SvgPicture.alignment] + final AlignmentGeometry alignment; + + /// See [SvgPicture.bytesLoader] + final BytesLoader bytesLoader; + + /// See [SvgPicture.placeholderBuilder] + final WidgetBuilder? placeholderBuilder; + + /// See [SvgPicture.matchTextDirection] + final bool matchTextDirection; + + /// See [SvgPicture.allowDrawingOutsideViewBox] + final bool allowDrawingOutsideViewBox; + + /// See [SvgPicture.semanticsLabel] + final String? semanticsLabel; + + /// See [SvgPicture.excludeFromSemantics] + final bool excludeFromSemantics; + + /// See [SvgPicture.clipBehavior] + final Clip clipBehavior; + + /// See [SvgPicture.colorFilter] + final ColorFilter? colorFilter; + + /// Whether to use [colorFilter] or not. Some icons don't need that + final bool allowColorFilter; + + const CatalystSvgIcon( + this.bytesLoader, { + super.key, + this.size, + this.fit = BoxFit.contain, + this.alignment = Alignment.center, + this.matchTextDirection = false, + this.allowDrawingOutsideViewBox = false, + this.placeholderBuilder, + this.colorFilter, + this.semanticsLabel, + this.excludeFromSemantics = false, + this.clipBehavior = Clip.hardEdge, + this.allowColorFilter = true, + }); + + CatalystSvgIcon.asset( + String assetName, { + super.key, + AssetBundle? bundle, + String? package = 'catalyst_voices_assets', + SvgTheme? theme, + this.size, + this.fit = BoxFit.contain, + this.alignment = Alignment.center, + this.matchTextDirection = false, + this.allowDrawingOutsideViewBox = false, + this.placeholderBuilder, + this.colorFilter, + this.semanticsLabel, + this.excludeFromSemantics = false, + this.clipBehavior = Clip.hardEdge, + this.allowColorFilter = true, + }) : bytesLoader = SvgAssetLoader( + assetName, + packageName: package, + assetBundle: bundle, + theme: theme, + ); + + @override + Widget build(BuildContext context) { + final effectiveSize = size ?? IconTheme.of(context).size; + final effectiveColorFilter = allowColorFilter + ? colorFilter ?? IconTheme.of(context).asColorFilter() + : null; + + return CatalystSvgPicture( + bytesLoader, + width: effectiveSize, + height: effectiveSize, + fit: fit, + alignment: alignment, + matchTextDirection: matchTextDirection, + allowDrawingOutsideViewBox: allowDrawingOutsideViewBox, + placeholderBuilder: placeholderBuilder, + colorFilter: effectiveColorFilter, + semanticsLabel: semanticsLabel, + excludeFromSemantics: excludeFromSemantics, + clipBehavior: clipBehavior, + ); + } +} + +extension _IconThemeDataExt on IconThemeData { + ColorFilter? asColorFilter() { + final color = this.color; + return color != null ? ColorFilter.mode(color, BlendMode.srcIn) : null; + } +} diff --git a/catalyst_voices/packages/catalyst_voices_assets/lib/src/catalyst_svg_picture.dart b/catalyst_voices/packages/catalyst_voices_assets/lib/src/catalyst_svg_picture.dart index ff37a53415a..823616e2f1f 100644 --- a/catalyst_voices/packages/catalyst_voices_assets/lib/src/catalyst_svg_picture.dart +++ b/catalyst_voices/packages/catalyst_voices_assets/lib/src/catalyst_svg_picture.dart @@ -1,4 +1,3 @@ -import 'package:flutter/widgets.dart'; import 'package:flutter_svg/flutter_svg.dart'; /// [CatalystSvgPicture] extends [SvgPicture] to have an asset constructor @@ -7,37 +6,38 @@ import 'package:flutter_svg/flutter_svg.dart'; /// every time. /// For more information, see [SvgPicture.asset]. final class CatalystSvgPicture extends SvgPicture { + const CatalystSvgPicture( + super.bytesLoader, { + super.key, + super.width, + super.height, + super.fit, + super.alignment, + super.matchTextDirection, + super.allowDrawingOutsideViewBox, + super.placeholderBuilder, + super.colorFilter, + super.semanticsLabel, + super.excludeFromSemantics, + super.clipBehavior, + }) : super(); + CatalystSvgPicture.asset( - String name, { - AssetBundle? bundle, - String? semanticsLabel, - bool excludeFromSemantics = false, - double? width, - double? height, - BoxFit fit = BoxFit.contain, - Alignment alignment = Alignment.center, - bool matchTextDirection = false, - String package = 'catalyst_voices_assets', - bool allowDrawingOutsideViewBox = false, - Widget Function(BuildContext)? placeholderBuilder, - Clip clipBehavior = Clip.hardEdge, - SvgTheme? theme, - ColorFilter? colorFilter, - }) : super.asset( - name, - bundle: bundle, - semanticsLabel: semanticsLabel, - excludeFromSemantics: excludeFromSemantics, - width: width, - height: height, - fit: fit, - alignment: alignment, - matchTextDirection: matchTextDirection, - package: package, - allowDrawingOutsideViewBox: allowDrawingOutsideViewBox, - placeholderBuilder: placeholderBuilder, - clipBehavior: clipBehavior, - theme: theme, - colorFilter: colorFilter, - ); + super.name, { + super.key, + super.matchTextDirection, + super.bundle, + super.package = 'catalyst_voices_assets', + super.width, + super.height, + super.fit, + super.alignment, + super.allowDrawingOutsideViewBox, + super.placeholderBuilder, + super.semanticsLabel, + super.excludeFromSemantics, + super.clipBehavior, + super.theme, + super.colorFilter, + }) : super.asset(); } diff --git a/catalyst_voices/packages/catalyst_voices_assets/lib/src/catalyst_voices_assets.dart b/catalyst_voices/packages/catalyst_voices_assets/lib/src/catalyst_voices_assets.dart index af3be458004..bd0c8a391ac 100644 --- a/catalyst_voices/packages/catalyst_voices_assets/lib/src/catalyst_voices_assets.dart +++ b/catalyst_voices/packages/catalyst_voices_assets/lib/src/catalyst_voices_assets.dart @@ -1,6 +1,8 @@ export 'package:catalyst_voices_assets/generated/assets.gen.dart'; export 'package:catalyst_voices_assets/generated/colors.gen.dart'; export 'package:catalyst_voices_assets/generated/fonts.gen.dart'; +export 'package:catalyst_voices_assets/src/assets_ext.dart'; export 'package:catalyst_voices_assets/src/catalyst_image.dart'; +export 'package:catalyst_voices_assets/src/catalyst_svg_icon.dart'; export 'package:catalyst_voices_assets/src/catalyst_svg_picture.dart'; export 'package:catalyst_voices_assets/src/catalyst_voices_icons.dart'; diff --git a/catalyst_voices/packages/catalyst_voices_assets/lib/src/catalyst_voices_icons.dart b/catalyst_voices/packages/catalyst_voices_assets/lib/src/catalyst_voices_icons.dart index 24a537ff7e9..fea8e4e7bd0 100644 --- a/catalyst_voices/packages/catalyst_voices_assets/lib/src/catalyst_voices_icons.dart +++ b/catalyst_voices/packages/catalyst_voices_assets/lib/src/catalyst_voices_icons.dart @@ -14,7 +14,6 @@ /// /// import 'package:flutter/widgets.dart'; - class CatalystVoicesIcons { CatalystVoicesIcons._(); @@ -23,8 +22,8 @@ class CatalystVoicesIcons { static const IconData academic_cap = IconData(0xe800, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData adjustments = IconData(0xe801, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData all_spaces_menu = IconData(0xe802, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData all_spaces_menu_1 = IconData(0xe803, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData all_spaces_menu_1 = IconData(0xe802, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData all_spaces_menu = IconData(0xe803, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData annotation = IconData(0xe804, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData archive = IconData(0xe805, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData arrow_circle_down = IconData(0xe806, fontFamily: _kFontFam, fontPackage: _kFontPkg); @@ -38,223 +37,237 @@ class CatalystVoicesIcons { static const IconData arrow_narrow_right = IconData(0xe80e, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData arrow_narrow_up = IconData(0xe80f, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData arrow_right = IconData(0xe810, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData arrows_expand = IconData(0xe811, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData arrow_triangle_down = IconData(0xe812, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData arrow_triangle_up = IconData(0xe813, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData at_symbol = IconData(0xe814, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData arrow_up = IconData(0xe815, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData arrow_triangle_down = IconData(0xe811, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData arrow_triangle_up = IconData(0xe812, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData arrow_up = IconData(0xe813, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData arrows_expand = IconData(0xe814, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData at_symbol = IconData(0xe815, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData backspace = IconData(0xe816, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData badge_check = IconData(0xe817, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData ban = IconData(0xe818, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData beaker = IconData(0xe819, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData bell = IconData(0xe81a, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData bookmark = IconData(0xe81b, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData book_open = IconData(0xe81b, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData bookmark_alt = IconData(0xe81c, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData book_open = IconData(0xe81d, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData bottom_rail_toggle = IconData(0xe81e, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData bookmark = IconData(0xe81d, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData bottom_main_content = IconData(0xe81e, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData bottom_rail_toggle_1 = IconData(0xe81f, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData briefcase = IconData(0xe820, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData cake = IconData(0xe821, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData calculator = IconData(0xe822, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData calendar = IconData(0xe823, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData camera = IconData(0xe824, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData cash = IconData(0xe825, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData chart_bar = IconData(0xe826, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData chart_pie = IconData(0xe827, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData chart_square_bar = IconData(0xe828, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData chat = IconData(0xe829, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData chat_alt = IconData(0xe82a, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData chat_alt_2 = IconData(0xe82b, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData check = IconData(0xe82c, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData bottom_rail_toggle = IconData(0xe820, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData briefcase = IconData(0xe821, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData cake = IconData(0xe822, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData calculator = IconData(0xe823, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData calendar = IconData(0xe824, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData camera = IconData(0xe825, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData cash = IconData(0xe826, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData chart_bar = IconData(0xe827, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData chart_pie = IconData(0xe828, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData chart_square_bar = IconData(0xe829, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData chat_alt_2 = IconData(0xe82a, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData chat_alt = IconData(0xe82b, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData chat = IconData(0xe82c, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData check_circle = IconData(0xe82d, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData chevron_down = IconData(0xe8ec, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData chevron_left = IconData(0xe8ed, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData chevron_right = IconData(0xe8ee, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData chevron_up = IconData(0xe8ef, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData chevron_double_down = IconData(0xe832, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData chevron_double_left = IconData(0xe833, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData chevron_double_up = IconData(0xe834, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData chevron_double_right = IconData(0xe835, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData chip = IconData(0xe836, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData clipboard = IconData(0xe837, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData check = IconData(0xe82e, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData chevron_double_down = IconData(0xe82f, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData chevron_double_left = IconData(0xe830, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData chevron_double_right = IconData(0xe831, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData chevron_double_up = IconData(0xe832, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData chevron_down = IconData(0xe833, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData chevron_left = IconData(0xe834, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData chevron_right = IconData(0xe835, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData chevron_up = IconData(0xe836, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData chip = IconData(0xe837, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData clipboard_check = IconData(0xe838, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData clipboard_copy = IconData(0xe839, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData clipboard_list = IconData(0xe83a, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData clock = IconData(0xe83b, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData cloud = IconData(0xe83c, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData clipboard = IconData(0xe83b, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData clock = IconData(0xe83c, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData cloud_download = IconData(0xe83d, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData cloud_upload = IconData(0xe83e, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData code = IconData(0xe83f, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData cog_gear = IconData(0xe840, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData collection = IconData(0xe841, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData color_swatch = IconData(0xe842, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData credit_card = IconData(0xe843, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData cube = IconData(0xe844, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData cloud = IconData(0xe83f, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData code = IconData(0xe840, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData cog_gear = IconData(0xe841, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData collection = IconData(0xe842, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData color_swatch = IconData(0xe843, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData credit_card = IconData(0xe844, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData cube_transparent = IconData(0xe845, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData currency_bangladeshi = IconData(0xe846, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData currency_dollar = IconData(0xe847, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData currency_euro = IconData(0xe848, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData currency_pound = IconData(0xe849, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData currency_rupee = IconData(0xe84a, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData currency_yen = IconData(0xe84b, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData cursor_click = IconData(0xe84c, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData database = IconData(0xe84d, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData desktop_computer = IconData(0xe84e, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData device_mobile = IconData(0xe84f, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData device_tablet = IconData(0xe850, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData document = IconData(0xe851, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData cube = IconData(0xe846, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData currency_bangladeshi = IconData(0xe847, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData currency_dollar = IconData(0xe848, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData currency_euro = IconData(0xe849, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData currency_pound = IconData(0xe84a, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData currency_rupee = IconData(0xe84b, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData currency_yen = IconData(0xe84c, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData cursor_click = IconData(0xe84d, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData database = IconData(0xe84e, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData desktop_computer = IconData(0xe84f, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData device_mobile = IconData(0xe850, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData device_tablet = IconData(0xe851, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData document_add = IconData(0xe852, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData document_remove = IconData(0xe853, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData document_report = IconData(0xe854, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData document_search = IconData(0xe855, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData document_text = IconData(0xe856, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData dots_circle_horizontal = IconData(0xe857, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData dots_horizontal = IconData(0xe858, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData dots_vertical = IconData(0xe859, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData download = IconData(0xe85a, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData duplicate = IconData(0xe85b, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData emoji_happy = IconData(0xe85c, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData emoji_sad = IconData(0xe85d, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData exclamation = IconData(0xe85e, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData document = IconData(0xe857, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData dots_circle_horizontal = IconData(0xe858, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData dots_horizontal = IconData(0xe859, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData dots_vertical = IconData(0xe85a, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData download = IconData(0xe85b, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData duplicate = IconData(0xe85c, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData emoji_happy = IconData(0xe85d, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData emoji_sad = IconData(0xe85e, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData exclamation_circle = IconData(0xe85f, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData external_link = IconData(0xe860, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData eye = IconData(0xe861, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData exclamation = IconData(0xe860, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData external_link = IconData(0xe861, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData eye_off = IconData(0xe862, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData fast_forward = IconData(0xe863, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData film = IconData(0xe864, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData filter = IconData(0xe865, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData finger_print = IconData(0xe866, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData fire = IconData(0xe867, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData flag = IconData(0xe868, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData folder = IconData(0xe869, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData eye = IconData(0xe863, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData fast_forward = IconData(0xe864, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData film = IconData(0xe865, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData filter = IconData(0xe866, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData finger_print = IconData(0xe867, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData fire = IconData(0xe868, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData flag = IconData(0xe869, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData folder_add = IconData(0xe86a, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData folder_download = IconData(0xe86b, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData folder_open = IconData(0xe86c, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData folder_remove = IconData(0xe86d, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData fund = IconData(0xe86e, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData gift = IconData(0xe86f, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData globe = IconData(0xe870, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData folder = IconData(0xe86e, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData fund = IconData(0xe86f, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData gift = IconData(0xe870, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData globe_alt = IconData(0xe871, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData hand = IconData(0xe872, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData hashtag = IconData(0xe873, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData heart = IconData(0xe874, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData home = IconData(0xe875, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData icon_user_remove = IconData(0xe876, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData identification = IconData(0xe877, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData inbox = IconData(0xe878, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData globe = IconData(0xe872, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData hand = IconData(0xe873, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData hashtag = IconData(0xe874, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData heart = IconData(0xe875, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData home = IconData(0xe876, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData icon_user_remove = IconData(0xe877, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData identification = IconData(0xe878, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData inbox_in = IconData(0xe879, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData information_circle = IconData(0xe87a, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData key = IconData(0xe87b, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData left_rail_toggle = IconData(0xe87c, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData library_icon = IconData(0xe87d, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData light_bulb = IconData(0xe87e, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData lightning_bolt = IconData(0xe87f, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData link = IconData(0xe880, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData location_marker = IconData(0xe881, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData lock_closed = IconData(0xe882, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData lock_open = IconData(0xe883, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData logout = IconData(0xe884, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData inbox = IconData(0xe87a, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData information_circle = IconData(0xe87b, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData key = IconData(0xe87c, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData left_rail_toggle = IconData(0xe87d, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData library_icon = IconData(0xe87e, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData light_bulb = IconData(0xe87f, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData lightning_bolt = IconData(0xe880, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData link = IconData(0xe881, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData location_marker = IconData(0xe882, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData lock_closed = IconData(0xe883, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData lock_open = IconData(0xe884, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData logout_1 = IconData(0xe885, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData mail = IconData(0xe886, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData logout = IconData(0xe886, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData mail_open = IconData(0xe887, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData map = IconData(0xe888, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData maximize_toggle = IconData(0xe889, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData menu = IconData(0xe8eb, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData menu_alt_1 = IconData(0xe88a, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData menu_alt_2 = IconData(0xe88b, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData menu_alt_3 = IconData(0xe88c, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData menu_alt_4 = IconData(0xe88d, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData microphone = IconData(0xe88e, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData minimize_toggle = IconData(0xe88f, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData minus = IconData(0xe890, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData minus_circle = IconData(0xe891, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData moon = IconData(0xe892, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData music_note = IconData(0xe893, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData newspaper = IconData(0xe894, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData office_building = IconData(0xe895, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData paper_airplane = IconData(0xe896, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData paper_clip = IconData(0xe897, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData pause = IconData(0xe898, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData pencil = IconData(0xe899, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData pencil_alt = IconData(0xe89a, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData phone = IconData(0xe89b, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData phone_incoming = IconData(0xe89c, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData phone_missed_call = IconData(0xe89d, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData phone_outgoing = IconData(0xe89e, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData photograph = IconData(0xe89f, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData play = IconData(0xe8a0, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData plus = IconData(0xe8a1, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData plus_circle = IconData(0xe8a2, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData presentation_chart_bar = IconData(0xe8a3, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData presentation_chart_line = IconData(0xe8a4, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData printer = IconData(0xe8a5, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData puzzle = IconData(0xe8a6, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData qrcode = IconData(0xe8a7, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData question_mark_circle = IconData(0xe8a8, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData receipt_refund = IconData(0xe8a9, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData receipt_tax = IconData(0xe8aa, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData refresh = IconData(0xe8ab, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData reply = IconData(0xe8ac, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData rewind = IconData(0xe8ad, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData right_rail_toggle = IconData(0xe8ae, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData rss = IconData(0xe8af, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData save = IconData(0xe8b0, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData save_as = IconData(0xe8b1, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData scale = IconData(0xe8b2, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData scissors = IconData(0xe8b3, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData search = IconData(0xe8b4, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData search_circle = IconData(0xe8b5, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData selector = IconData(0xe8b6, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData send_airplane = IconData(0xe8b7, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData server = IconData(0xe8b8, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData share = IconData(0xe8b9, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData shield_check = IconData(0xe8ba, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData shield_exclamation = IconData(0xe8bb, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData shopping_bag = IconData(0xe8bc, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData shopping_cart = IconData(0xe8bd, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData sm_view_grid_add = IconData(0xe8be, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData sort_ascending = IconData(0xe8bf, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData sort_descending = IconData(0xe8c0, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData sparkles = IconData(0xe8c1, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData speakerphone = IconData(0xe8c2, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData star = IconData(0xe8c3, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData status_offline = IconData(0xe8c4, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData status_online = IconData(0xe8c5, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData stop = IconData(0xe8c6, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData sun = IconData(0xe8c7, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData support = IconData(0xe8c8, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData switch_horizontal = IconData(0xe8c9, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData switch_vertical = IconData(0xe8ca, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData table = IconData(0xe8cb, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData tag = IconData(0xe8cc, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData template = IconData(0xe8cd, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData terminal = IconData(0xe8ce, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData thumb_down = IconData(0xe8cf, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData thumb_up = IconData(0xe8d0, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData ticket = IconData(0xe8d1, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData translate = IconData(0xe8d2, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData trash = IconData(0xe8d3, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData trending_down = IconData(0xe8d4, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData trending_up = IconData(0xe8d5, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData truck = IconData(0xe8d6, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData upload = IconData(0xe8d7, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData user = IconData(0xe8d8, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData user_add = IconData(0xe8d9, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData user_circle = IconData(0xe8da, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData user_group = IconData(0xe8db, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData users = IconData(0xe8dc, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData variable = IconData(0xe8dd, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData video_camera = IconData(0xe8de, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData view_boards = IconData(0xe8df, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData view_grid = IconData(0xe8e0, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData view_list = IconData(0xe8e1, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData volume_off = IconData(0xe8e2, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData volume_up = IconData(0xe8e3, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData vote = IconData(0xe8e4, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData wallet = IconData(0xe8e5, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData wifi = IconData(0xe8e6, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData x = IconData(0xe8e7, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData x_circle = IconData(0xe8e8, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData zoom_in = IconData(0xe8e9, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData zoom_out = IconData(0xe8ea, fontFamily: _kFontFam, fontPackage: _kFontPkg); -} \ No newline at end of file + static const IconData mail = IconData(0xe888, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData map = IconData(0xe889, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData maximize_toggle = IconData(0xe88a, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData menu_alt_1 = IconData(0xe88b, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData menu_alt_2 = IconData(0xe88c, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData menu_alt_3 = IconData(0xe88d, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData menu_alt_4 = IconData(0xe88e, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData menu = IconData(0xe88f, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData microphone = IconData(0xe890, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData minimize_toggle = IconData(0xe891, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData minus_circle = IconData(0xe892, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData minus = IconData(0xe893, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData moon = IconData(0xe894, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData move_item = IconData(0xe895, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData music_note = IconData(0xe896, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData newspaper = IconData(0xe897, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData node_closed = IconData(0xe898, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData node_line_end = IconData(0xe899, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData node_line = IconData(0xe89a, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData node_open = IconData(0xe89b, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData office_building = IconData(0xe89c, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData paper_airplane = IconData(0xe89d, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData paper_clip = IconData(0xe89e, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData pause = IconData(0xe89f, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData pencil_alt = IconData(0xe8a0, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData pencil = IconData(0xe8a1, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData phone_incoming = IconData(0xe8a2, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData phone_missed_call = IconData(0xe8a3, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData phone_outgoing = IconData(0xe8a4, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData phone = IconData(0xe8a5, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData photograph = IconData(0xe8a6, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData play = IconData(0xe8a7, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData plus_circle = IconData(0xe8a8, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData plus = IconData(0xe8a9, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData presentation_chart_bar = IconData(0xe8aa, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData presentation_chart_line = IconData(0xe8ab, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData printer = IconData(0xe8ac, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData progress_track_warning = IconData(0xe8ad, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData puzzle = IconData(0xe8ae, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData qrcode = IconData(0xe8af, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData question_mark_circle = IconData(0xe8b0, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData receipt_refund = IconData(0xe8b1, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData receipt_tax = IconData(0xe8b2, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData refresh = IconData(0xe8b3, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData reply = IconData(0xe8b4, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData rewind = IconData(0xe8b5, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData right_rail_toggle = IconData(0xe8b6, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData rss = IconData(0xe8b7, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData rt_bold = IconData(0xe8b8, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData rt_decrease_indent = IconData(0xe8b9, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData rt_heading = IconData(0xe8ba, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData rt_increase_indent = IconData(0xe8bb, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData rt_italic = IconData(0xe8bc, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData rt_ordered_list = IconData(0xe8bd, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData rt_unordered_list = IconData(0xe8be, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData save_as = IconData(0xe8bf, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData save = IconData(0xe8c0, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData scale = IconData(0xe8c1, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData scissors = IconData(0xe8c2, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData search_circle = IconData(0xe8c3, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData search = IconData(0xe8c4, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData selector = IconData(0xe8c5, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData send_airplane = IconData(0xe8c6, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData server = IconData(0xe8c7, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData share = IconData(0xe8c8, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData shield_check = IconData(0xe8c9, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData shield_exclamation = IconData(0xe8ca, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData shopping_bag = IconData(0xe8cb, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData shopping_cart = IconData(0xe8cc, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData sm_view_grid_add = IconData(0xe8cd, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData sort_ascending = IconData(0xe8ce, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData sort_descending = IconData(0xe8cf, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData sparkles = IconData(0xe8d0, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData speakerphone = IconData(0xe8d1, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData star = IconData(0xe8d2, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData status_offline = IconData(0xe8d3, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData status_online = IconData(0xe8d4, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData stop = IconData(0xe8d5, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData sun = IconData(0xe8d6, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData support = IconData(0xe8d7, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData switch_horizontal = IconData(0xe8d8, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData switch_vertical = IconData(0xe8d9, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData table = IconData(0xe8da, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData tag = IconData(0xe8db, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData template = IconData(0xe8dc, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData terminal = IconData(0xe8dd, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData thumb_down = IconData(0xe8de, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData thumb_up = IconData(0xe8df, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData ticket = IconData(0xe8e0, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData translate = IconData(0xe8e1, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData trash = IconData(0xe8e2, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData trending_down = IconData(0xe8e3, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData trending_up = IconData(0xe8e4, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData truck = IconData(0xe8e5, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData upload = IconData(0xe8e6, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData user_add = IconData(0xe8e7, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData user_circle = IconData(0xe8e8, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData user_group = IconData(0xe8e9, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData user = IconData(0xe8ea, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData users = IconData(0xe8eb, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData variable = IconData(0xe8ec, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData video_camera = IconData(0xe8ed, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData view_boards = IconData(0xe8ee, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData view_grid = IconData(0xe8ef, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData view_list = IconData(0xe8f0, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData volume_off = IconData(0xe8f1, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData volume_up = IconData(0xe8f2, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData vote = IconData(0xe8f3, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData wallet = IconData(0xe8f4, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData wifi = IconData(0xe8f5, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData x_circle = IconData(0xe8f6, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData x = IconData(0xe8f7, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData zoom_in = IconData(0xe8f8, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData zoom_out = IconData(0xe8f9, fontFamily: _kFontFam, fontPackage: _kFontPkg); +} diff --git a/catalyst_voices/packages/catalyst_voices_assets/pubspec.yaml b/catalyst_voices/packages/catalyst_voices_assets/pubspec.yaml index 0e8ba698943..492e5976e5d 100644 --- a/catalyst_voices/packages/catalyst_voices_assets/pubspec.yaml +++ b/catalyst_voices/packages/catalyst_voices_assets/pubspec.yaml @@ -4,8 +4,8 @@ version: 0.1.0+1 publish_to: none environment: - sdk: ">=3.3.4 <4.0.0" - flutter: ">=3.22.1" + sdk: ">=3.5.0 <4.0.0" + flutter: ">=3.24.1" dependencies: flutter: @@ -13,7 +13,8 @@ dependencies: flutter_svg: ^2.0.10+1 dev_dependencies: - build_runner: ^2.3.3 + build_runner: ^2.4.12 + catalyst_analysis: ^2.0.0 flutter_gen_runner: ^5.3.2 flutter: @@ -25,7 +26,7 @@ flutter: fonts: - asset: assets/fonts/SF-Pro-Rounded-Regular.ttf weight: 600 - - family: CatalystVoicesIcons + - family: CatalystVoicesIcons fonts: - asset: internal_resources/fonts/CatalystVoicesIcons.ttf diff --git a/catalyst_voices/packages/catalyst_voices_blocs/analysis_options.yaml b/catalyst_voices/packages/catalyst_voices_blocs/analysis_options.yaml index d5015346bf1..eb691624826 100644 --- a/catalyst_voices/packages/catalyst_voices_blocs/analysis_options.yaml +++ b/catalyst_voices/packages/catalyst_voices_blocs/analysis_options.yaml @@ -1,4 +1,4 @@ -include: package:catalyst_analysis/analysis_options.1.0.0.yaml +include: package:catalyst_analysis/analysis_options.yaml analyzer: exclude: [ diff --git a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/brand/brand_bloc.dart b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/brand/brand_bloc.dart index 902383bce3b..7a7cdf34f52 100644 --- a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/brand/brand_bloc.dart +++ b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/brand/brand_bloc.dart @@ -7,19 +7,19 @@ part 'brand_event.dart'; part 'brand_state.dart'; /// [BrandBloc] is a Bloc responsible for managing the brand state. -/// +/// /// This Bloc listens for [BrandEvent]s and updates the [BrandState] /// accordingly. -/// The [BrandState] can be consumed by the [MaterialApp] to build the +/// The [BrandState] can be consumed by the [MaterialApp] to build the /// appropriate [ThemeData] based on the [BrandKey] stored in the state. -/// +/// /// To build the appropriate [ThemeData] based on the [BrandKey] is /// possible to use the [ThemeBuilder] utility form the `catalyst_voices_brands` /// package. /// /// The [BrandChangedEvent] accepts a [BrandKey] a simple enum that /// contains all possible brands/themes. -/// +/// /// To trigger the theme change is just necessary to dispatch the /// [BrandChangedEvent] with the appropriate key: /// @@ -71,5 +71,4 @@ final class BrandBloc extends Bloc { ) { emit(BrandState(brandKey: event.brand)); } - } diff --git a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/brand/brand_state.dart b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/brand/brand_state.dart index e02893822da..b5382ae3e2b 100644 --- a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/brand/brand_state.dart +++ b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/brand/brand_state.dart @@ -4,7 +4,7 @@ final class BrandState extends Equatable { final BrandKey? brandKey; const BrandState({BrandKey? brandKey}) - : brandKey = brandKey ?? BrandKey.catalyst; + : brandKey = brandKey ?? BrandKey.catalyst; @override List get props => [brandKey ?? '']; diff --git a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/catalyst_voices_blocs.dart b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/catalyst_voices_blocs.dart index 8240fdcab67..052c5e5a318 100644 --- a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/catalyst_voices_blocs.dart +++ b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/catalyst_voices_blocs.dart @@ -1,3 +1,5 @@ export 'authentication/authentication.dart'; export 'brand/brand.dart'; export 'login/login.dart'; +export 'session/session.dart'; +export 'user_profile/user_profile.dart'; diff --git a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/session/session.dart b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/session/session.dart new file mode 100644 index 00000000000..b66aef28d52 --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/session/session.dart @@ -0,0 +1,3 @@ +export 'session_bloc.dart'; +export 'session_event.dart'; +export 'session_state.dart'; diff --git a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/session/session_bloc.dart b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/session/session_bloc.dart new file mode 100644 index 00000000000..dddd6dde108 --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/session/session_bloc.dart @@ -0,0 +1,23 @@ +import 'package:catalyst_voices_blocs/src/session/session_event.dart'; +import 'package:catalyst_voices_blocs/src/session/session_state.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +/// Manages the user session. +final class SessionBloc extends Bloc { + SessionBloc() : super(const VisitorSessionState()) { + on(_handleNextState); + } + + void _handleNextState( + NextStateSessionEvent event, + Emitter emit, + ) { + final nextState = switch (state) { + VisitorSessionState() => const GuestSessionState(), + GuestSessionState() => const ActiveUserSessionState(), + ActiveUserSessionState() => const VisitorSessionState(), + }; + + emit(nextState); + } +} diff --git a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/session/session_event.dart b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/session/session_event.dart new file mode 100644 index 00000000000..13503d5e802 --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/session/session_event.dart @@ -0,0 +1,15 @@ +import 'package:equatable/equatable.dart'; + +/// Describes events that change the user session. +sealed class SessionEvent extends Equatable { + const SessionEvent(); +} + +/// Dummy implementation of session management, +/// just toggles the next session state or reset to the initial one. +final class NextStateSessionEvent extends SessionEvent { + const NextStateSessionEvent(); + + @override + List get props => []; +} diff --git a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/session/session_state.dart b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/session/session_state.dart new file mode 100644 index 00000000000..0da9837a4e5 --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/session/session_state.dart @@ -0,0 +1,30 @@ +import 'package:equatable/equatable.dart'; + +/// Determines the state of the user session. +sealed class SessionState extends Equatable { + const SessionState(); +} + +/// The user hasn't registered yet nor setup the keychain. +final class VisitorSessionState extends SessionState { + const VisitorSessionState(); + + @override + List get props => []; +} + +/// The user has registered the keychain but it's locked. +final class GuestSessionState extends SessionState { + const GuestSessionState(); + + @override + List get props => []; +} + +/// The user has registered and unlocked the keychain. +final class ActiveUserSessionState extends SessionState { + const ActiveUserSessionState(); + + @override + List get props => []; +} diff --git a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/user_profile/user_profile.dart b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/user_profile/user_profile.dart new file mode 100644 index 00000000000..4c519976d88 --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/user_profile/user_profile.dart @@ -0,0 +1,3 @@ +export 'user_profile_bloc.dart'; +export 'user_profile_event.dart'; +export 'user_profile_state.dart'; diff --git a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/user_profile/user_profile_bloc.dart b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/user_profile/user_profile_bloc.dart new file mode 100644 index 00000000000..294f5a6dbae --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/user_profile/user_profile_bloc.dart @@ -0,0 +1,23 @@ +import 'package:catalyst_voices_blocs/src/catalyst_voices_blocs.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +/// Manages the user profile. +final class UserProfileBloc extends Bloc { + UserProfileBloc() : super(const VisitorUserProfileState()) { + on(_toggleUserProfile); + } + + void _toggleUserProfile( + ToggleUserProfileEvent event, + Emitter emit, + ) { + final nextState = switch (state) { + VisitorUserProfileState() => + const ActiveUserProfileState(user: User(name: 'John Smith')), + ActiveUserProfileState() => const VisitorUserProfileState(), + }; + + emit(nextState); + } +} diff --git a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/user_profile/user_profile_event.dart b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/user_profile/user_profile_event.dart new file mode 100644 index 00000000000..a3d5044679d --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/user_profile/user_profile_event.dart @@ -0,0 +1,15 @@ +import 'package:equatable/equatable.dart'; + +/// Describes events that change the user profile. +sealed class UserProfileEvent extends Equatable { + const UserProfileEvent(); +} + +/// Dummy implementation of user management, +/// just logins/logouts the user. +final class ToggleUserProfileEvent extends UserProfileEvent { + const ToggleUserProfileEvent(); + + @override + List get props => []; +} diff --git a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/user_profile/user_profile_state.dart b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/user_profile/user_profile_state.dart new file mode 100644 index 00000000000..4275f8634df --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/user_profile/user_profile_state.dart @@ -0,0 +1,25 @@ +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:equatable/equatable.dart'; + +/// Determines the state of the user profile. +sealed class UserProfileState extends Equatable { + const UserProfileState(); +} + +/// The user hasn't login yet. +final class VisitorUserProfileState extends UserProfileState { + const VisitorUserProfileState(); + + @override + List get props => []; +} + +/// The user has logged in. +final class ActiveUserProfileState extends UserProfileState { + final User user; + + const ActiveUserProfileState({required this.user}); + + @override + List get props => [user]; +} diff --git a/catalyst_voices/packages/catalyst_voices_blocs/pubspec.yaml b/catalyst_voices/packages/catalyst_voices_blocs/pubspec.yaml index 0191cea5056..e3f11a3fcc4 100644 --- a/catalyst_voices/packages/catalyst_voices_blocs/pubspec.yaml +++ b/catalyst_voices/packages/catalyst_voices_blocs/pubspec.yaml @@ -4,35 +4,33 @@ version: 0.1.0+1 publish_to: none environment: - sdk: ">=3.3.4 <4.0.0" - flutter: ">=3.22.1" + sdk: ">=3.5.0 <4.0.0" + flutter: ">=3.24.1" dependencies: - bloc: ^8.1.2 - bloc_concurrency: ^0.2.2 - catalyst_voices_brands: - path: ../catalyst_voices_brands - catalyst_voices_models: - path: ../catalyst_voices_models - catalyst_voices_repositories: - path: ../catalyst_voices_repositories - catalyst_voices_services: - path: ../catalyst_voices_services - catalyst_voices_view_models: - path: ../catalyst_voices_view_models - collection: ^1.18.0 - equatable: ^2.0.5 - flutter: - sdk: flutter - formz: ^0.7.0 - meta: ^1.10.0 - result_type: ^0.2.0 + bloc_concurrency: ^0.2.2 + catalyst_voices_brands: + path: ../catalyst_voices_brands + catalyst_voices_models: + path: ../catalyst_voices_models + catalyst_voices_repositories: + path: ../catalyst_voices_repositories + catalyst_voices_services: + path: ../catalyst_voices_services + catalyst_voices_view_models: + path: ../catalyst_voices_view_models + collection: ^1.18.0 + equatable: ^2.0.5 + flutter: + sdk: flutter + flutter_bloc: ^8.1.5 + formz: ^0.7.0 + meta: ^1.10.0 + result_type: ^0.2.0 dev_dependencies: - bloc_test: ^9.1.4 - catalyst_analysis: - path: ../../../catalyst_voices_packages/catalyst_analysis - flutter_bloc: ^8.1.5 - flutter_test: - sdk: flutter - test: ^1.24.9 + bloc_test: ^9.1.4 + catalyst_analysis: ^2.0.0 + flutter_test: + sdk: flutter + test: ^1.24.9 diff --git a/catalyst_voices/packages/catalyst_voices_brands/analysis_options.yaml b/catalyst_voices/packages/catalyst_voices_brands/analysis_options.yaml index d5015346bf1..eb691624826 100644 --- a/catalyst_voices/packages/catalyst_voices_brands/analysis_options.yaml +++ b/catalyst_voices/packages/catalyst_voices_brands/analysis_options.yaml @@ -1,4 +1,4 @@ -include: package:catalyst_analysis/analysis_options.1.0.0.yaml +include: package:catalyst_analysis/analysis_options.yaml analyzer: exclude: [ diff --git a/catalyst_voices/packages/catalyst_voices_brands/lib/src/theme_extensions/voices_color_scheme.dart b/catalyst_voices/packages/catalyst_voices_brands/lib/src/theme_extensions/voices_color_scheme.dart index 2bb1b698ab1..cb64279e33a 100644 --- a/catalyst_voices/packages/catalyst_voices_brands/lib/src/theme_extensions/voices_color_scheme.dart +++ b/catalyst_voices/packages/catalyst_voices_brands/lib/src/theme_extensions/voices_color_scheme.dart @@ -37,6 +37,7 @@ class VoicesColorScheme extends ThemeExtension { final Color? onSurfaceError016; final Color? iconsForeground; final Color? iconsBackground; + final Color? iconsOnImage; final Color? iconsDisabled; final Color? iconsPrimary; final Color? iconsSecondary; @@ -49,8 +50,12 @@ class VoicesColorScheme extends ThemeExtension { final Color? avatarsWarning; final Color? avatarsError; final Color? elevationsOnSurfaceNeutralLv0; + final Color? elevationsOnSurfaceNeutralLv1Grey; + final Color? elevationsOnSurfaceNeutralLv1White; + final Color? elevationsOnSurfaceNeutralLv2; final Color? outlineBorder; final Color? outlineBorderVariant; + final Color? primary98; final Color? primaryContainer; final Color? onPrimaryContainer; final Color? errorContainer; @@ -87,6 +92,7 @@ class VoicesColorScheme extends ThemeExtension { required this.onSurfaceError016, required this.iconsForeground, required this.iconsBackground, + required this.iconsOnImage, required this.iconsDisabled, required this.iconsPrimary, required this.iconsSecondary, @@ -99,8 +105,12 @@ class VoicesColorScheme extends ThemeExtension { required this.avatarsWarning, required this.avatarsError, required this.elevationsOnSurfaceNeutralLv0, + required this.elevationsOnSurfaceNeutralLv1Grey, + required this.elevationsOnSurfaceNeutralLv1White, + required this.elevationsOnSurfaceNeutralLv2, required this.outlineBorder, required this.outlineBorderVariant, + required this.primary98, required this.primaryContainer, required this.onPrimaryContainer, required this.errorContainer, @@ -139,6 +149,7 @@ class VoicesColorScheme extends ThemeExtension { this.onSurfaceError016, this.iconsForeground, this.iconsBackground, + this.iconsOnImage, this.iconsDisabled, this.iconsPrimary, this.iconsSecondary, @@ -151,8 +162,12 @@ class VoicesColorScheme extends ThemeExtension { this.avatarsWarning, this.avatarsError, this.elevationsOnSurfaceNeutralLv0, + this.elevationsOnSurfaceNeutralLv1Grey, + this.elevationsOnSurfaceNeutralLv1White, + this.elevationsOnSurfaceNeutralLv2, this.outlineBorder, this.outlineBorderVariant, + this.primary98, this.primaryContainer, this.onPrimaryContainer, this.errorContainer, @@ -191,6 +206,7 @@ class VoicesColorScheme extends ThemeExtension { Color? onSurfaceError016, Color? iconsForeground, Color? iconsBackground, + Color? iconsOnImage, Color? iconsDisabled, Color? iconsPrimary, Color? iconsSecondary, @@ -203,8 +219,12 @@ class VoicesColorScheme extends ThemeExtension { Color? avatarsWarning, Color? avatarsError, Color? elevationsOnSurfaceNeutralLv0, + Color? elevationsOnSurfaceNeutralLv1Grey, + Color? elevationsOnSurfaceNeutralLv1White, + Color? elevationsOnSurfaceNeutralLv2, Color? outlineBorder, Color? outlineBorderVariant, + Color? primary98, Color? primaryContainer, Color? onPrimaryContainer, Color? errorContainer, @@ -248,6 +268,7 @@ class VoicesColorScheme extends ThemeExtension { onSurfaceError016: onSurfaceError016 ?? this.onSurfaceError016, iconsForeground: iconsForeground ?? this.iconsForeground, iconsBackground: iconsBackground ?? this.iconsBackground, + iconsOnImage: iconsOnImage ?? this.iconsOnImage, iconsDisabled: iconsDisabled ?? this.iconsDisabled, iconsPrimary: iconsPrimary ?? this.iconsPrimary, iconsSecondary: iconsSecondary ?? this.iconsSecondary, @@ -261,8 +282,15 @@ class VoicesColorScheme extends ThemeExtension { avatarsError: avatarsError ?? this.avatarsError, elevationsOnSurfaceNeutralLv0: elevationsOnSurfaceNeutralLv0 ?? this.elevationsOnSurfaceNeutralLv0, + elevationsOnSurfaceNeutralLv1Grey: elevationsOnSurfaceNeutralLv1Grey ?? + this.elevationsOnSurfaceNeutralLv1Grey, + elevationsOnSurfaceNeutralLv1White: elevationsOnSurfaceNeutralLv1White ?? + this.elevationsOnSurfaceNeutralLv1White, + elevationsOnSurfaceNeutralLv2: + elevationsOnSurfaceNeutralLv2 ?? this.elevationsOnSurfaceNeutralLv2, outlineBorder: outlineBorder ?? this.outlineBorder, outlineBorderVariant: outlineBorderVariant ?? this.outlineBorderVariant, + primary98: primary98 ?? this.primary98, primaryContainer: primaryContainer ?? this.primaryContainer, onPrimaryContainer: onPrimaryContainer ?? this.onPrimaryContainer, errorContainer: errorContainer ?? this.errorContainer, @@ -339,6 +367,7 @@ class VoicesColorScheme extends ThemeExtension { Color.lerp(onSurfaceError016, other.onSurfaceError016, t), iconsForeground: Color.lerp(iconsForeground, other.iconsForeground, t), iconsBackground: Color.lerp(iconsBackground, other.iconsBackground, t), + iconsOnImage: Color.lerp(iconsOnImage, other.iconsOnImage, t), iconsDisabled: Color.lerp(iconsDisabled, other.iconsDisabled, t), iconsPrimary: Color.lerp(iconsPrimary, other.iconsPrimary, t), iconsSecondary: Color.lerp(iconsSecondary, other.iconsSecondary, t), @@ -355,9 +384,25 @@ class VoicesColorScheme extends ThemeExtension { other.elevationsOnSurfaceNeutralLv0, t, ), + elevationsOnSurfaceNeutralLv1Grey: Color.lerp( + elevationsOnSurfaceNeutralLv1Grey, + other.elevationsOnSurfaceNeutralLv1Grey, + t, + ), + elevationsOnSurfaceNeutralLv1White: Color.lerp( + elevationsOnSurfaceNeutralLv1White, + other.elevationsOnSurfaceNeutralLv1White, + t, + ), + elevationsOnSurfaceNeutralLv2: Color.lerp( + elevationsOnSurfaceNeutralLv2, + other.elevationsOnSurfaceNeutralLv2, + t, + ), outlineBorder: Color.lerp(outlineBorder, other.outlineBorder, t), outlineBorderVariant: Color.lerp(outlineBorderVariant, other.outlineBorderVariant, t), + primary98: Color.lerp(primary98, other.primary98, t), primaryContainer: Color.lerp(primaryContainer, other.primaryContainer, t), onPrimaryContainer: Color.lerp(onPrimaryContainer, other.onPrimaryContainer, t), diff --git a/catalyst_voices/packages/catalyst_voices_brands/lib/src/themes/catalyst.dart b/catalyst_voices/packages/catalyst_voices_brands/lib/src/themes/catalyst.dart index 6c98debc648..03e65371ead 100644 --- a/catalyst_voices/packages/catalyst_voices_brands/lib/src/themes/catalyst.dart +++ b/catalyst_voices/packages/catalyst_voices_brands/lib/src/themes/catalyst.dart @@ -53,6 +53,7 @@ const VoicesColorScheme darkVoicesColorScheme = VoicesColorScheme( onSurfaceError016: VoicesColors.darkOnSurfaceError016, iconsForeground: VoicesColors.darkIconsForeground, iconsBackground: VoicesColors.darkIconsBackground, + iconsOnImage: VoicesColors.darkIconsOnImage, iconsDisabled: VoicesColors.darkIconsDisabled, iconsPrimary: VoicesColors.darkIconsPrimary, iconsSecondary: VoicesColors.darkIconsSecondary, @@ -65,8 +66,14 @@ const VoicesColorScheme darkVoicesColorScheme = VoicesColorScheme( avatarsWarning: VoicesColors.darkAvatarsWarning, avatarsError: VoicesColors.darkAvatarsError, elevationsOnSurfaceNeutralLv0: VoicesColors.darkElevationsOnSurfaceNeutralLv0, + elevationsOnSurfaceNeutralLv1Grey: + VoicesColors.darkElevationsOnSurfaceNeutralLv1Grey, + elevationsOnSurfaceNeutralLv1White: + VoicesColors.darkElevationsOnSurfaceNeutralLv1White, + elevationsOnSurfaceNeutralLv2: VoicesColors.darkElevationsOnSurfaceNeutralLv2, outlineBorder: VoicesColors.darkOutlineBorderOutline, outlineBorderVariant: VoicesColors.darkOutlineBorderOutlineVariant, + primary98: VoicesColors.darkPrimary98, primaryContainer: VoicesColors.darkPrimaryContainer, onPrimaryContainer: VoicesColors.darkOnPrimaryContainer, errorContainer: VoicesColors.darkErrorContainer, @@ -119,8 +126,9 @@ const VoicesColorScheme lightVoicesColorScheme = VoicesColorScheme( onSurfaceError08: VoicesColors.lightOnSurfaceError08, onSurfaceError012: VoicesColors.lightOnSurfaceError012, onSurfaceError016: VoicesColors.lightOnSurfaceError016, - iconsForeground: VoicesColors.lightIconsForeground, + iconsForeground: Color.fromARGB(255, 151, 164, 193), iconsBackground: VoicesColors.lightIconsBackground, + iconsOnImage: VoicesColors.lightIconsOnImage, iconsDisabled: VoicesColors.lightIconsDisabled, iconsPrimary: VoicesColors.lightIconsPrimary, iconsSecondary: VoicesColors.lightIconsSecondary, @@ -134,8 +142,15 @@ const VoicesColorScheme lightVoicesColorScheme = VoicesColorScheme( avatarsError: VoicesColors.lightAvatarsError, elevationsOnSurfaceNeutralLv0: VoicesColors.lightElevationsOnSurfaceNeutralLv0, + elevationsOnSurfaceNeutralLv1Grey: + VoicesColors.lightElevationsOnSurfaceNeutralLv1Grey, + elevationsOnSurfaceNeutralLv1White: + VoicesColors.lightElevationsOnSurfaceNeutralLv1White, + elevationsOnSurfaceNeutralLv2: + VoicesColors.lightElevationsOnSurfaceNeutralLv2, outlineBorder: VoicesColors.lightOutlineBorderOutline, outlineBorderVariant: VoicesColors.lightOutlineBorderOutlineVariant, + primary98: VoicesColors.lightPrimary98, primaryContainer: VoicesColors.lightPrimaryContainer, onPrimaryContainer: VoicesColors.lightOnPrimaryContainer, errorContainer: VoicesColors.lightErrorContainer, @@ -284,10 +299,18 @@ ThemeData _buildThemeData( return ThemeData( appBarTheme: AppBarTheme( backgroundColor: voicesColorScheme.onSurfaceNeutralOpaqueLv1, + scrolledUnderElevation: 0, ), drawerTheme: DrawerThemeData( backgroundColor: voicesColorScheme.elevationsOnSurfaceNeutralLv0, ), + dialogTheme: DialogTheme( + // TODO(damian-molinski): themed value needed. We don't have it defined yet. + barrierColor: Color(0x612A3D61), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + clipBehavior: Clip.hardEdge, + backgroundColor: voicesColorScheme.onSurfaceNeutralOpaqueLv0, + ), listTileTheme: ListTileThemeData( shape: const StadiumBorder(), minTileHeight: 56, @@ -296,9 +319,28 @@ ThemeData _buildThemeData( ), dividerTheme: DividerThemeData( color: colorScheme.outlineVariant, + space: 16, + thickness: 1, + ), + tabBarTheme: TabBarTheme( + dividerColor: colorScheme.primaryContainer, + tabAlignment: TabAlignment.start, + ), + progressIndicatorTheme: ProgressIndicatorThemeData( + color: colorScheme.primary, + linearTrackColor: colorScheme.secondaryContainer, + circularTrackColor: colorScheme.secondaryContainer, + refreshBackgroundColor: colorScheme.secondaryContainer, ), textTheme: textTheme, colorScheme: colorScheme, + iconTheme: IconThemeData( + color: voicesColorScheme.iconsForeground, + ), + primaryIconTheme: IconThemeData( + color: colorScheme.onPrimary, + ), + scaffoldBackgroundColor: voicesColorScheme.onSurfaceNeutralOpaqueLv1, extensions: >[ voicesColorScheme, brandAssets, diff --git a/catalyst_voices/packages/catalyst_voices_brands/lib/src/themes/widgets/buttons_theme.dart b/catalyst_voices/packages/catalyst_voices_brands/lib/src/themes/widgets/buttons_theme.dart index 67bc8a0d047..5b6f7c2a3a3 100644 --- a/catalyst_voices/packages/catalyst_voices_brands/lib/src/themes/widgets/buttons_theme.dart +++ b/catalyst_voices/packages/catalyst_voices_brands/lib/src/themes/widgets/buttons_theme.dart @@ -8,9 +8,6 @@ extension ButtonsThemeExt on ThemeData { /// reusability if we're going to have more brands. ThemeData copyWithButtonsTheme() { return copyWith( - buttonBarTheme: const ButtonBarThemeData( - buttonHeight: 40, - ), filledButtonTheme: FilledButtonThemeData( style: FilledButton.styleFrom( foregroundColor: colorScheme.onPrimary, diff --git a/catalyst_voices/packages/catalyst_voices_brands/lib/src/themes/widgets/toggles_theme.dart b/catalyst_voices/packages/catalyst_voices_brands/lib/src/themes/widgets/toggles_theme.dart index 640f3315ef5..949b7231d5d 100644 --- a/catalyst_voices/packages/catalyst_voices_brands/lib/src/themes/widgets/toggles_theme.dart +++ b/catalyst_voices/packages/catalyst_voices_brands/lib/src/themes/widgets/toggles_theme.dart @@ -106,6 +106,62 @@ extension TogglesTheme on ThemeData { materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, visualDensity: const VisualDensity(horizontal: -4, vertical: -4), ), + switchTheme: SwitchThemeData( + trackColor: WidgetStateProperty.resolveWith( + (states) { + if (states.contains(WidgetState.selected) && + !states.contains(WidgetState.disabled)) { + return colorScheme.primary; + } + + return colors.outlineBorderVariant?.withOpacity(0.38); + }, + ), + trackOutlineColor: WidgetStateProperty.resolveWith( + (states) { + if (states.contains(WidgetState.selected) && + !states.contains(WidgetState.disabled)) { + return colorScheme.primary; + } + + return colors.outlineBorder; + }, + ), + trackOutlineWidth: WidgetStateProperty.resolveWith( + (states) { + if (states.contains(WidgetState.selected)) { + return states.contains(WidgetState.disabled) ? 1.0 : null; + } + + return 2.0; + }, + ), + thumbColor: WidgetStateProperty.resolveWith( + (states) { + // Selected states + if (states.contains(WidgetState.selected)) { + if (states.contains(WidgetState.disabled)) { + return colorScheme.surface; + } + if (states.contains(WidgetState.pressed) || + states.contains(WidgetState.hovered)) { + return colorScheme.primaryContainer; + } + + return colorScheme.onPrimary; + } + + // Not disabled and pressed or hovered + if (!states.contains(WidgetState.disabled) && + (states.contains(WidgetState.pressed) || + states.contains(WidgetState.hovered))) { + return colors.iconsForeground; + } + + return colors.outlineBorder; + }, + ), + ), ); } } diff --git a/catalyst_voices/packages/catalyst_voices_brands/pubspec.yaml b/catalyst_voices/packages/catalyst_voices_brands/pubspec.yaml index d1f524b93a2..684cf1c2f0b 100644 --- a/catalyst_voices/packages/catalyst_voices_brands/pubspec.yaml +++ b/catalyst_voices/packages/catalyst_voices_brands/pubspec.yaml @@ -4,8 +4,8 @@ version: 0.1.0+1 publish_to: none environment: - sdk: ">=3.3.4 <4.0.0" - flutter: ">=3.22.1" + sdk: ">=3.5.0 <4.0.0" + flutter: ">=3.24.1" dependencies: catalyst_voices_assets: @@ -15,8 +15,7 @@ dependencies: google_fonts: ^6.2.1 dev_dependencies: - catalyst_analysis: - path: ../../../catalyst_voices_packages/catalyst_analysis + catalyst_analysis: ^2.0.0 catalyst_voices_blocs: path: ../catalyst_voices_blocs flutter_bloc: ^8.1.5 diff --git a/catalyst_voices/packages/catalyst_voices_localization/analysis_options.yaml b/catalyst_voices/packages/catalyst_voices_localization/analysis_options.yaml new file mode 100644 index 00000000000..83c56e6802f --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_localization/analysis_options.yaml @@ -0,0 +1,12 @@ +include: package:catalyst_analysis/analysis_options.yaml + +analyzer: + exclude: [ + build/**, + lib/*.g.dart, + lib/generated/** + ] + +linter: + rules: + public_member_api_docs: false \ No newline at end of file diff --git a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations.dart b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations.dart index 3f3950bdcbd..ea16018c9b0 100644 --- a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations.dart +++ b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations.dart @@ -7,6 +7,8 @@ import 'package:intl/intl.dart' as intl; import 'catalyst_voices_localizations_en.dart' deferred as catalyst_voices_localizations_en; import 'catalyst_voices_localizations_es.dart' deferred as catalyst_voices_localizations_es; +// ignore_for_file: type=lint + /// Callers can lookup localized strings with an instance of VoicesLocalizations /// returned by `VoicesLocalizations.of(context)`. /// @@ -110,6 +112,42 @@ abstract class VoicesLocalizations { /// **'mail@example.com'** String get emailErrorText; + /// Text shown in cancel button + /// + /// In en, this message translates to: + /// **'Cancel'** + String get cancelButtonText; + + /// Text shown in edit button + /// + /// In en, this message translates to: + /// **'Edit'** + String get editButtonText; + + /// Text shown in header tooltip + /// + /// In en, this message translates to: + /// **'Header'** + String get headerTooltipText; + + /// Text shown as placeholder in rich text editor + /// + /// In en, this message translates to: + /// **'Start writing your text...'** + String get placeholderRichText; + + /// Text shown as placeholder in rich text editor + /// + /// In en, this message translates to: + /// **'Supporting text'** + String get supportingTextLabelText; + + /// Text shown in save button + /// + /// In en, this message translates to: + /// **'Save'** + String get saveButtonText; + /// Text shown in password field /// /// In en, this message translates to: @@ -277,6 +315,108 @@ abstract class VoicesLocalizations { /// In en, this message translates to: /// **'Ok'** String get snackbarOkButtonText; + + /// When user arranges seed phrases this text is shown when phrase was not selected + /// + /// In en, this message translates to: + /// **'Slot {nr}'** + String seedPhraseSlotNr(int nr); + + /// Indicates to user that status is in ready mode + /// + /// In en, this message translates to: + /// **'Ready'** + String get proposalStatusReady; + + /// Indicates to user that status is in draft mode + /// + /// In en, this message translates to: + /// **'Draft'** + String get proposalStatusDraft; + + /// Label shown on a proposal card indicating that the proposal is funded. + /// + /// In en, this message translates to: + /// **'Funded proposal'** + String get fundedProposal; + + /// Label shown on a proposal card indicating that the proposal is not yet funded. + /// + /// In en, this message translates to: + /// **'Published proposal'** + String get publishedProposal; + + /// Indicates date of funding (a proposal). + /// + /// In en, this message translates to: + /// **'Funded {date}'** + String fundedProposalDate(DateTime date); + + /// Indicates a last update date. + /// + /// In en, this message translates to: + /// **'Last update: {date}.'** + String lastUpdateDate(String date); + + /// Indicates the amount of ADA requested in a fund on a proposal card. + /// + /// In en, this message translates to: + /// **'Funds requested'** + String get fundsRequested; + + /// Indicates the amount of comments on a proposal card. + /// + /// In en, this message translates to: + /// **'{count} {count, plural, =0{comments} =1{comment} other{comments}}'** + String noOfComments(num count); + + /// Indicates the amount of comments on a proposal card. + /// + /// In en, this message translates to: + /// **'{completed} of {total} ({percentage}%) {total, plural, =0{segments} =1{segment} other{segments}} completed'** + String noOfSegmentsCompleted(num completed, num total, num percentage); + + /// Refers to date which is today. + /// + /// In en, this message translates to: + /// **'Today'** + String get today; + + /// Refers to date which is yesterday. + /// + /// In en, this message translates to: + /// **'Yesterday'** + String get yesterday; + + /// Refers to date which is two days ago. + /// + /// In en, this message translates to: + /// **'2 days ago'** + String get twoDaysAgo; + + /// Refers to date which is tomorrow. + /// + /// In en, this message translates to: + /// **'Tomorrow'** + String get tomorrow; + + /// Title of the voting space. + /// + /// In en, this message translates to: + /// **'Active voting round 14'** + String get activeVotingRound; + + /// Tab label for all proposals in voting space + /// + /// In en, this message translates to: + /// **'All proposals ({count})'** + String noOfAllProposals(int count); + + /// Refers to a list of favorites. + /// + /// In en, this message translates to: + /// **'Favorites'** + String get favorites; } class _VoicesLocalizationsDelegate extends LocalizationsDelegate { diff --git a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_en.dart b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_en.dart index bd04c65217f..7216e6e5c07 100644 --- a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_en.dart +++ b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_en.dart @@ -1,5 +1,9 @@ +import 'package:intl/intl.dart' as intl; + import 'catalyst_voices_localizations.dart'; +// ignore_for_file: type=lint + /// The translations for English (`en`). class VoicesLocalizationsEn extends VoicesLocalizations { VoicesLocalizationsEn([String locale = 'en']) : super(locale); @@ -13,6 +17,24 @@ class VoicesLocalizationsEn extends VoicesLocalizations { @override String get emailErrorText => 'mail@example.com'; + @override + String get cancelButtonText => 'Cancel'; + + @override + String get editButtonText => 'Edit'; + + @override + String get headerTooltipText => 'Header'; + + @override + String get placeholderRichText => 'Start writing your text...'; + + @override + String get supportingTextLabelText => 'Supporting text'; + + @override + String get saveButtonText => 'Save'; + @override String get passwordLabelText => 'Password'; @@ -96,4 +118,106 @@ class VoicesLocalizationsEn extends VoicesLocalizations { @override String get snackbarOkButtonText => 'Ok'; + + @override + String seedPhraseSlotNr(int nr) { + return 'Slot $nr'; + } + + @override + String get proposalStatusReady => 'Ready'; + + @override + String get proposalStatusDraft => 'Draft'; + + @override + String get fundedProposal => 'Funded proposal'; + + @override + String get publishedProposal => 'Published proposal'; + + @override + String fundedProposalDate(DateTime date) { + final intl.DateFormat dateDateFormat = intl.DateFormat.yMMMMd(localeName); + final String dateString = dateDateFormat.format(date); + + return 'Funded $dateString'; + } + + @override + String lastUpdateDate(String date) { + return 'Last update: $date.'; + } + + @override + String get fundsRequested => 'Funds requested'; + + @override + String noOfComments(num count) { + final intl.NumberFormat countNumberFormat = intl.NumberFormat.compact( + locale: localeName, + + ); + final String countString = countNumberFormat.format(count); + + String _temp0 = intl.Intl.pluralLogic( + count, + locale: localeName, + other: 'comments', + one: 'comment', + zero: 'comments', + ); + return '$countString $_temp0'; + } + + @override + String noOfSegmentsCompleted(num completed, num total, num percentage) { + final intl.NumberFormat completedNumberFormat = intl.NumberFormat.compact( + locale: localeName, + + ); + final String completedString = completedNumberFormat.format(completed); + final intl.NumberFormat totalNumberFormat = intl.NumberFormat.compact( + locale: localeName, + + ); + final String totalString = totalNumberFormat.format(total); + final intl.NumberFormat percentageNumberFormat = intl.NumberFormat.compact( + locale: localeName, + + ); + final String percentageString = percentageNumberFormat.format(percentage); + + String _temp0 = intl.Intl.pluralLogic( + total, + locale: localeName, + other: 'segments', + one: 'segment', + zero: 'segments', + ); + return '$completedString of $totalString ($percentageString%) $_temp0 completed'; + } + + @override + String get today => 'Today'; + + @override + String get yesterday => 'Yesterday'; + + @override + String get twoDaysAgo => '2 days ago'; + + @override + String get tomorrow => 'Tomorrow'; + + @override + String get activeVotingRound => 'Active voting round 14'; + + @override + String noOfAllProposals(int count) { + return 'All proposals ($count)'; + } + + @override + String get favorites => 'Favorites'; } diff --git a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_es.dart b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_es.dart index 91f8e2e31cf..37dd2f52710 100644 --- a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_es.dart +++ b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_es.dart @@ -1,5 +1,9 @@ +import 'package:intl/intl.dart' as intl; + import 'catalyst_voices_localizations.dart'; +// ignore_for_file: type=lint + /// The translations for Spanish Castilian (`es`). class VoicesLocalizationsEs extends VoicesLocalizations { VoicesLocalizationsEs([String locale = 'es']) : super(locale); @@ -13,6 +17,24 @@ class VoicesLocalizationsEs extends VoicesLocalizations { @override String get emailErrorText => 'mail@example.com'; + @override + String get cancelButtonText => 'Cancel'; + + @override + String get editButtonText => 'Edit'; + + @override + String get headerTooltipText => 'Header'; + + @override + String get placeholderRichText => 'Start writing your text...'; + + @override + String get supportingTextLabelText => 'Supporting text'; + + @override + String get saveButtonText => 'Save'; + @override String get passwordLabelText => 'Contraseña'; @@ -96,4 +118,106 @@ class VoicesLocalizationsEs extends VoicesLocalizations { @override String get snackbarOkButtonText => 'Ok'; + + @override + String seedPhraseSlotNr(int nr) { + return 'Slot $nr'; + } + + @override + String get proposalStatusReady => 'Ready'; + + @override + String get proposalStatusDraft => 'Draft'; + + @override + String get fundedProposal => 'Funded proposal'; + + @override + String get publishedProposal => 'Published proposal'; + + @override + String fundedProposalDate(DateTime date) { + final intl.DateFormat dateDateFormat = intl.DateFormat.yMMMMd(localeName); + final String dateString = dateDateFormat.format(date); + + return 'Funded $dateString'; + } + + @override + String lastUpdateDate(String date) { + return 'Last update: $date.'; + } + + @override + String get fundsRequested => 'Funds requested'; + + @override + String noOfComments(num count) { + final intl.NumberFormat countNumberFormat = intl.NumberFormat.compact( + locale: localeName, + + ); + final String countString = countNumberFormat.format(count); + + String _temp0 = intl.Intl.pluralLogic( + count, + locale: localeName, + other: 'comments', + one: 'comment', + zero: 'comments', + ); + return '$countString $_temp0'; + } + + @override + String noOfSegmentsCompleted(num completed, num total, num percentage) { + final intl.NumberFormat completedNumberFormat = intl.NumberFormat.compact( + locale: localeName, + + ); + final String completedString = completedNumberFormat.format(completed); + final intl.NumberFormat totalNumberFormat = intl.NumberFormat.compact( + locale: localeName, + + ); + final String totalString = totalNumberFormat.format(total); + final intl.NumberFormat percentageNumberFormat = intl.NumberFormat.compact( + locale: localeName, + + ); + final String percentageString = percentageNumberFormat.format(percentage); + + String _temp0 = intl.Intl.pluralLogic( + total, + locale: localeName, + other: 'segments', + one: 'segment', + zero: 'segments', + ); + return '$completedString of $totalString ($percentageString%) $_temp0 completed'; + } + + @override + String get today => 'Today'; + + @override + String get yesterday => 'Yesterday'; + + @override + String get twoDaysAgo => '2 days ago'; + + @override + String get tomorrow => 'Tomorrow'; + + @override + String get activeVotingRound => 'Active voting round 14'; + + @override + String noOfAllProposals(int count) { + return 'All proposals ($count)'; + } + + @override + String get favorites => 'Favorites'; } diff --git a/catalyst_voices/packages/catalyst_voices_localization/lib/l10n/intl_en.arb b/catalyst_voices/packages/catalyst_voices_localization/lib/l10n/intl_en.arb index b363e041203..3a361c79c90 100644 --- a/catalyst_voices/packages/catalyst_voices_localization/lib/l10n/intl_en.arb +++ b/catalyst_voices/packages/catalyst_voices_localization/lib/l10n/intl_en.arb @@ -12,6 +12,30 @@ "@emailErrorText": { "description": "Text shown in email field when input is invalid" }, + "cancelButtonText": "Cancel", + "@cancelButtonText": { + "description": "Text shown in cancel button" + }, + "editButtonText": "Edit", + "@editButtonText": { + "description": "Text shown in edit button" + }, + "headerTooltipText": "Header", + "@headerTooltipText": { + "description": "Text shown in header tooltip" + }, + "placeholderRichText": "Start writing your text...", + "@placeholderRichText": { + "description": "Text shown as placeholder in rich text editor" + }, + "supportingTextLabelText": "Supporting text", + "@supportingTextLabelText": { + "description": "Text shown as placeholder in rich text editor" + }, + "saveButtonText": "Save", + "@saveButtonText": { + "description": "Text shown in save button" + }, "passwordLabelText": "Password", "@passwordLabelText": { "description": "Text shown in password field" @@ -123,5 +147,114 @@ "snackbarOkButtonText": "Ok", "@snackbarOkButtonText": { "description": "Text shown in the Snackbar widget for the ok button." + }, + "seedPhraseSlotNr": "Slot {nr}", + "@seedPhraseSlotNr": { + "description": "When user arranges seed phrases this text is shown when phrase was not selected", + "placeholders": { + "nr": { + "type": "int" + } + } + }, + "proposalStatusReady": "Ready", + "@proposalStatusReady": { + "description": "Indicates to user that status is in ready mode" + }, + "proposalStatusDraft": "Draft", + "@proposalStatusDraft": { + "description": "Indicates to user that status is in draft mode" + }, + "fundedProposal": "Funded proposal", + "@fundedProposal": { + "description": "Label shown on a proposal card indicating that the proposal is funded." + }, + "publishedProposal": "Published proposal", + "@publishedProposal": { + "description": "Label shown on a proposal card indicating that the proposal is not yet funded." + }, + "fundedProposalDate": "Funded {date}", + "@fundedProposalDate": { + "description": "Indicates date of funding (a proposal).", + "placeholders": { + "date": { + "type": "DateTime", + "format": "yMMMMd" + } + } + }, + "lastUpdateDate": "Last update: {date}.", + "@lastUpdateDate": { + "description": "Indicates a last update date.", + "placeholders": { + "date": { + "type": "String" + } + } + }, + "fundsRequested": "Funds requested", + "@fundsRequested": { + "description": "Indicates the amount of ADA requested in a fund on a proposal card." + }, + "noOfComments": "{count} {count, plural, =0{comments} =1{comment} other{comments}}", + "@noOfComments": { + "description": "Indicates the amount of comments on a proposal card.", + "placeholders": { + "count": { + "type": "num", + "format": "compact" + } + } + }, + "noOfSegmentsCompleted": "{completed} of {total} ({percentage}%) {total, plural, =0{segments} =1{segment} other{segments}} completed", + "@noOfSegmentsCompleted": { + "description": "Indicates the amount of comments on a proposal card.", + "placeholders": { + "completed": { + "type": "num", + "format": "compact" + }, + "total": { + "type": "num", + "format": "compact" + }, + "percentage": { + "type": "num", + "format": "compact" + } + } + }, + "today": "Today", + "@today": { + "description": "Refers to date which is today." + }, + "yesterday": "Yesterday", + "@yesterday": { + "description": "Refers to date which is yesterday." + }, + "twoDaysAgo": "2 days ago", + "@twoDaysAgo": { + "description": "Refers to date which is two days ago." + }, + "tomorrow": "Tomorrow", + "@tomorrow": { + "description": "Refers to date which is tomorrow." + }, + "activeVotingRound": "Active voting round 14", + "@activeVotingRound": { + "description": "Title of the voting space." + }, + "noOfAllProposals": "All proposals ({count})", + "@noOfAllProposals": { + "description": "Tab label for all proposals in voting space", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "favorites": "Favorites", + "@favorites": { + "description": "Refers to a list of favorites." } -} +} \ No newline at end of file diff --git a/catalyst_voices/packages/catalyst_voices_localization/pubspec.yaml b/catalyst_voices/packages/catalyst_voices_localization/pubspec.yaml index acd07e4eae3..e9600cbd6ee 100644 --- a/catalyst_voices/packages/catalyst_voices_localization/pubspec.yaml +++ b/catalyst_voices/packages/catalyst_voices_localization/pubspec.yaml @@ -4,8 +4,8 @@ version: 0.1.0+1 publish_to: none environment: - sdk: ">=3.3.4 <4.0.0" - flutter: ">=3.22.1" + sdk: ">=3.5.0 <4.0.0" + flutter: ">=3.24.1" dependencies: flutter: @@ -15,6 +15,7 @@ dependencies: intl: ^0.19.0 dev_dependencies: + catalyst_analysis: ^2.0.0 flutter_test: sdk: flutter diff --git a/catalyst_voices/packages/catalyst_voices_models/lib/catalyst_voices_models.dart b/catalyst_voices/packages/catalyst_voices_models/lib/catalyst_voices_models.dart index 14d334ec9c2..34ee251355e 100644 --- a/catalyst_voices/packages/catalyst_voices_models/lib/catalyst_voices_models.dart +++ b/catalyst_voices/packages/catalyst_voices_models/lib/catalyst_voices_models.dart @@ -1,5 +1,3 @@ library catalyst_voices_models; -export 'src/authentication_status.dart'; export 'src/catalyst_voices_models.dart'; -export 'src/session_data.dart'; diff --git a/catalyst_voices/packages/catalyst_voices_models/lib/src/catalyst_voices_models.dart b/catalyst_voices/packages/catalyst_voices_models/lib/src/catalyst_voices_models.dart index e321b665c65..9c8052769c7 100644 --- a/catalyst_voices/packages/catalyst_voices_models/lib/src/catalyst_voices_models.dart +++ b/catalyst_voices/packages/catalyst_voices_models/lib/src/catalyst_voices_models.dart @@ -1 +1,10 @@ +library catalyst_voices_models; + +export 'authentication_status.dart'; export 'errors/errors.dart'; +export 'proposal/funded_proposal.dart'; +export 'proposal/pending_proposal.dart'; +export 'proposal/proposal_status.dart'; +export 'session_data.dart'; +export 'space.dart'; +export 'user/user.dart'; diff --git a/catalyst_voices/packages/catalyst_voices_models/lib/src/proposal/funded_proposal.dart b/catalyst_voices/packages/catalyst_voices_models/lib/src/proposal/funded_proposal.dart new file mode 100644 index 00000000000..1fad937efcc --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_models/lib/src/proposal/funded_proposal.dart @@ -0,0 +1,37 @@ +import 'package:catalyst_cardano_serialization/catalyst_cardano_serialization.dart'; +import 'package:equatable/equatable.dart'; + +/// Defines the already funded proposal. +final class FundedProposal extends Equatable { + final String id; + final String fund; + final String category; + final String title; + final DateTime fundedDate; + final Coin fundsRequested; + final int commentsCount; + final String description; + + const FundedProposal({ + required this.id, + required this.fund, + required this.category, + required this.title, + required this.fundedDate, + required this.fundsRequested, + required this.commentsCount, + required this.description, + }); + + @override + List get props => [ + id, + fund, + category, + title, + fundedDate, + fundsRequested, + commentsCount, + description, + ]; +} diff --git a/catalyst_voices/packages/catalyst_voices_models/lib/src/proposal/pending_proposal.dart b/catalyst_voices/packages/catalyst_voices_models/lib/src/proposal/pending_proposal.dart new file mode 100644 index 00000000000..3d16e11938c --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_models/lib/src/proposal/pending_proposal.dart @@ -0,0 +1,43 @@ +import 'package:catalyst_cardano_serialization/catalyst_cardano_serialization.dart'; +import 'package:equatable/equatable.dart'; + +/// Defines the pending proposal that is not funded yet. +final class PendingProposal extends Equatable { + final String id; + final String fund; + final String category; + final String title; + final DateTime lastUpdateDate; + final Coin fundsRequested; + final int commentsCount; + final String description; + final int completedSegments; + final int totalSegments; + + const PendingProposal({ + required this.id, + required this.fund, + required this.category, + required this.title, + required this.lastUpdateDate, + required this.fundsRequested, + required this.commentsCount, + required this.description, + required this.completedSegments, + required this.totalSegments, + }); + + @override + List get props => [ + id, + fund, + category, + title, + lastUpdateDate, + fundsRequested, + commentsCount, + description, + completedSegments, + totalSegments, + ]; +} diff --git a/catalyst_voices/packages/catalyst_voices_models/lib/src/proposal/proposal_status.dart b/catalyst_voices/packages/catalyst_voices_models/lib/src/proposal/proposal_status.dart new file mode 100644 index 00000000000..a53236b7676 --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_models/lib/src/proposal/proposal_status.dart @@ -0,0 +1,4 @@ +enum ProposalStatus { + ready, + draft; +} diff --git a/catalyst_voices/packages/catalyst_voices_models/lib/src/space.dart b/catalyst_voices/packages/catalyst_voices_models/lib/src/space.dart new file mode 100644 index 00000000000..ebe3214a430 --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_models/lib/src/space.dart @@ -0,0 +1,8 @@ +/// Main spaces between which user can navigate. +enum Space { + treasury, + discovery, + workspace, + voting, + fundedProjects; +} diff --git a/catalyst_voices/packages/catalyst_voices_models/lib/src/user/user.dart b/catalyst_voices/packages/catalyst_voices_models/lib/src/user/user.dart new file mode 100644 index 00000000000..42803e1e53e --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_models/lib/src/user/user.dart @@ -0,0 +1,11 @@ +import 'package:equatable/equatable.dart'; + +/// Defines the profile of the app user. +final class User extends Equatable { + final String name; + + const User({required this.name}); + + @override + List get props => [name]; +} diff --git a/catalyst_voices/packages/catalyst_voices_models/pubspec.yaml b/catalyst_voices/packages/catalyst_voices_models/pubspec.yaml index 17f232d0274..bf137446228 100644 --- a/catalyst_voices/packages/catalyst_voices_models/pubspec.yaml +++ b/catalyst_voices/packages/catalyst_voices_models/pubspec.yaml @@ -4,13 +4,13 @@ version: 0.1.0+1 publish_to: none environment: - sdk: ">=3.3.4 <4.0.0" + sdk: ">=3.5.0 <4.0.0" dependencies: + catalyst_cardano_serialization: ^0.4.0 equatable: ^2.0.5 meta: ^1.10.0 dev_dependencies: - catalyst_analysis: - path: ../../../catalyst_voices_packages/catalyst_analysis - test: ^1.24.9 \ No newline at end of file + catalyst_analysis: ^2.0.0 + test: ^1.24.9 diff --git a/catalyst_voices/packages/catalyst_voices_repositories/analysis_options.yaml b/catalyst_voices/packages/catalyst_voices_repositories/analysis_options.yaml index 2e7be08b970..83c56e6802f 100644 --- a/catalyst_voices/packages/catalyst_voices_repositories/analysis_options.yaml +++ b/catalyst_voices/packages/catalyst_voices_repositories/analysis_options.yaml @@ -1,4 +1,4 @@ -include: package:catalyst_analysis/analysis_options.1.0.0.yaml +include: package:catalyst_analysis/analysis_options.yaml analyzer: exclude: [ diff --git a/catalyst_voices/packages/catalyst_voices_repositories/pubspec.yaml b/catalyst_voices/packages/catalyst_voices_repositories/pubspec.yaml index 073911a3299..ea6bd4ece2d 100644 --- a/catalyst_voices/packages/catalyst_voices_repositories/pubspec.yaml +++ b/catalyst_voices/packages/catalyst_voices_repositories/pubspec.yaml @@ -4,8 +4,8 @@ version: 0.1.0+1 publish_to: none environment: - sdk: ">=3.3.4 <4.0.0" - flutter: ">=3.22.1" + sdk: ">=3.5.0 <4.0.0" + flutter: ">=3.24.1" dependencies: catalyst_voices_models: @@ -20,8 +20,7 @@ dependencies: rxdart: ^0.27.7 dev_dependencies: - build_runner: ^2.3.3 - catalyst_analysis: - path: ../../../catalyst_voices_packages/catalyst_analysis + build_runner: ^2.4.12 + catalyst_analysis: ^2.0.0 mockito: ^5.4.4 test: ^1.24.9 diff --git a/catalyst_voices/packages/catalyst_voices_services/analysis_options.yaml b/catalyst_voices/packages/catalyst_voices_services/analysis_options.yaml index 2e7be08b970..83c56e6802f 100644 --- a/catalyst_voices/packages/catalyst_voices_services/analysis_options.yaml +++ b/catalyst_voices/packages/catalyst_voices_services/analysis_options.yaml @@ -1,4 +1,4 @@ -include: package:catalyst_analysis/analysis_options.1.0.0.yaml +include: package:catalyst_analysis/analysis_options.yaml analyzer: exclude: [ diff --git a/catalyst_voices/packages/catalyst_voices_services/pubspec.yaml b/catalyst_voices/packages/catalyst_voices_services/pubspec.yaml index 91f330a1e82..427e6268e94 100644 --- a/catalyst_voices/packages/catalyst_voices_services/pubspec.yaml +++ b/catalyst_voices/packages/catalyst_voices_services/pubspec.yaml @@ -4,8 +4,8 @@ version: 0.1.0+1 publish_to: none environment: - sdk: ">=3.3.4 <4.0.0" - flutter: ">=3.22.1" + sdk: ">=3.5.0 <4.0.0" + flutter: ">=3.24.1" dependencies: chopper: ^7.2.0 @@ -16,9 +16,8 @@ dependencies: rxdart: ^0.27.7 dev_dependencies: - build_runner: ^2.3.3 - catalyst_analysis: - path: ../../../catalyst_voices_packages/catalyst_analysis + build_runner: ^2.4.12 + catalyst_analysis: ^2.0.0 chopper_generator: ^7.2.0 json_serializable: ^6.7.1 swagger_dart_code_generator: ^2.15.2 diff --git a/catalyst_voices/packages/catalyst_voices_shared/analysis_options.yaml b/catalyst_voices/packages/catalyst_voices_shared/analysis_options.yaml index 2e7be08b970..83c56e6802f 100644 --- a/catalyst_voices/packages/catalyst_voices_shared/analysis_options.yaml +++ b/catalyst_voices/packages/catalyst_voices_shared/analysis_options.yaml @@ -1,4 +1,4 @@ -include: package:catalyst_analysis/analysis_options.1.0.0.yaml +include: package:catalyst_analysis/analysis_options.yaml analyzer: exclude: [ diff --git a/catalyst_voices/packages/catalyst_voices_shared/lib/catalyst_voices_shared.dart b/catalyst_voices/packages/catalyst_voices_shared/lib/catalyst_voices_shared.dart index b3e795a7c25..3e3b306f20e 100644 --- a/catalyst_voices/packages/catalyst_voices_shared/lib/catalyst_voices_shared.dart +++ b/catalyst_voices/packages/catalyst_voices_shared/lib/catalyst_voices_shared.dart @@ -1,4 +1,3 @@ -/// A Very Good Project created by Very Good CLI. library catalyst_voices_shared; export 'src/catalyst_voices_shared.dart'; diff --git a/catalyst_voices/packages/catalyst_voices_shared/lib/src/catalyst_voices_shared.dart b/catalyst_voices/packages/catalyst_voices_shared/lib/src/catalyst_voices_shared.dart index 6f00b494ce6..f80314f6864 100644 --- a/catalyst_voices/packages/catalyst_voices_shared/lib/src/catalyst_voices_shared.dart +++ b/catalyst_voices/packages/catalyst_voices_shared/lib/src/catalyst_voices_shared.dart @@ -1,8 +1,12 @@ export 'common/build_config.dart'; export 'common/build_environment.dart'; export 'dependency/dependency_provider.dart'; +export 'formatter/cryptocurrency_formatter.dart'; export 'platform/catalyst_platform.dart'; export 'platform_aware_builder/platform_aware_builder.dart'; export 'responsive/responsive_builder.dart'; export 'responsive/responsive_child.dart'; export 'responsive/responsive_padding.dart'; +export 'utils/date_time_ext.dart'; +export 'utils/iterable_ext.dart'; +export 'utils/typedefs.dart'; diff --git a/catalyst_voices/packages/catalyst_voices_shared/lib/src/dependency/dependency_provider.dart b/catalyst_voices/packages/catalyst_voices_shared/lib/src/dependency/dependency_provider.dart index c51a5b986f4..e18fbb2c52d 100644 --- a/catalyst_voices/packages/catalyst_voices_shared/lib/src/dependency/dependency_provider.dart +++ b/catalyst_voices/packages/catalyst_voices_shared/lib/src/dependency/dependency_provider.dart @@ -17,7 +17,7 @@ abstract class DependencyProvider { } /// Sets an instance of [DependencyProvider]. - /// + /// /// Must be called before the instance could be accessed. static set instance(DependencyProvider instance) { _instance = instance; diff --git a/catalyst_voices/packages/catalyst_voices_shared/lib/src/formatter/cryptocurrency_formatter.dart b/catalyst_voices/packages/catalyst_voices_shared/lib/src/formatter/cryptocurrency_formatter.dart new file mode 100644 index 00000000000..74affa5e108 --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_shared/lib/src/formatter/cryptocurrency_formatter.dart @@ -0,0 +1,31 @@ +import 'package:catalyst_cardano_serialization/catalyst_cardano_serialization.dart'; +import 'package:intl/intl.dart'; + +/// Formats amounts of ADA cryptocurrency. +abstract class CryptocurrencyFormatter { + static const adaSymbol = '₳'; + + static const _million = 1000000; + static const _thousand = 1000; + + /// Formats the [amount] of ADA cryptocurrency. + /// + /// Uses K (thousands) or M (millions) multipliers. + /// Examples: + /// - ₳123 = ₳123 + /// - ₳1000 = ₳1K + /// - ₳1000000 = ₳1M + static String formatAmount(Coin amount) { + final numberFormat = NumberFormat('#.##'); + final ada = amount.ada; + if (ada >= _million) { + final millions = ada / _million; + return adaSymbol + numberFormat.format(millions) + 'M'; + } else if (ada >= _thousand) { + final thousands = ada / _thousand; + return adaSymbol + numberFormat.format(thousands) + 'K'; + } else { + return adaSymbol + numberFormat.format(ada); + } + } +} diff --git a/catalyst_voices/packages/catalyst_voices_shared/lib/src/responsive/responsive_builder.dart b/catalyst_voices/packages/catalyst_voices_shared/lib/src/responsive/responsive_builder.dart index 55da2f90dca..a817a0e38c0 100644 --- a/catalyst_voices/packages/catalyst_voices_shared/lib/src/responsive/responsive_builder.dart +++ b/catalyst_voices/packages/catalyst_voices_shared/lib/src/responsive/responsive_builder.dart @@ -1,4 +1,6 @@ import 'package:catalyst_voices_shared/src/responsive/responsive_breakpoint_key.dart'; +import 'package:catalyst_voices_shared/src/utils/typedefs.dart'; +import 'package:collection/collection.dart'; import 'package:flutter/widgets.dart'; // A [ResponsiveBuilder] is a StatelessWidget that is aware about the current @@ -44,9 +46,16 @@ import 'package:flutter/widgets.dart'; // ); // ``` +const Map _breakpoints = { + ResponsiveBreakpointKey.xs: (min: 0, max: 599), + ResponsiveBreakpointKey.sm: (min: 600, max: 1239), + ResponsiveBreakpointKey.md: (min: 1240, max: 1439), + ResponsiveBreakpointKey.lg: (min: 1440, max: 2048), +}; + class ResponsiveBuilder extends StatelessWidget { - final Widget Function(BuildContext context, T? data) builder; - final Map _responsiveData; + final DataWidgetBuilder builder; + final Map _responsiveData; ResponsiveBuilder({ super.key, @@ -57,10 +66,10 @@ class ResponsiveBuilder extends StatelessWidget { T? lg, required T other, }) : _responsiveData = { - ResponsiveBreakpointKey.xs: xs, - ResponsiveBreakpointKey.sm: sm, - ResponsiveBreakpointKey.md: md, - ResponsiveBreakpointKey.lg: lg, + if (xs != null) ResponsiveBreakpointKey.xs: xs, + if (sm != null) ResponsiveBreakpointKey.sm: sm, + if (md != null) ResponsiveBreakpointKey.md: md, + if (lg != null) ResponsiveBreakpointKey.lg: lg, ResponsiveBreakpointKey.other: other, }; @@ -73,23 +82,21 @@ class ResponsiveBuilder extends StatelessWidget { final screenWidth = MediaQuery.sizeOf(context).width; final breakpointKey = _breakpoints.entries - .firstWhere( - (entry) => (screenWidth >= entry.value.min && - screenWidth <= entry.value.max && - _responsiveData[entry.key] != null), - orElse: () => const MapEntry( - ResponsiveBreakpointKey.other, - (min: 0, max: 0), - ), - ) - .key; + .where((entry) => _responsiveData.containsKey(entry.key)) + .firstWhereOrNull((entry) => entry.value.contains(screenWidth)) + ?.key ?? + ResponsiveBreakpointKey.other; + + assert( + _responsiveData.containsKey(breakpointKey), + 'Selected key[$breakpointKey] data is not defined. ' + 'Make sure at least .other is not null', + ); + return _responsiveData[breakpointKey]!; } +} - final Map _breakpoints = { - ResponsiveBreakpointKey.xs: (min: 0, max: 599), - ResponsiveBreakpointKey.sm: (min: 600, max: 1239), - ResponsiveBreakpointKey.md: (min: 1240, max: 1439), - ResponsiveBreakpointKey.lg: (min: 1440, max: 2048), - }; +extension _RangeExt on ({int min, int max}) { + bool contains(double value) => value >= min && value <= max; } diff --git a/catalyst_voices/packages/catalyst_voices_shared/lib/src/responsive/responsive_child.dart b/catalyst_voices/packages/catalyst_voices_shared/lib/src/responsive/responsive_child.dart index ec07aa2fb5e..17547115da5 100644 --- a/catalyst_voices/packages/catalyst_voices_shared/lib/src/responsive/responsive_child.dart +++ b/catalyst_voices/packages/catalyst_voices_shared/lib/src/responsive/responsive_child.dart @@ -33,7 +33,7 @@ import 'package:flutter/material.dart'; // ``` class ResponsiveChild extends StatelessWidget { - final Map _widgets; + final Map _widgets; ResponsiveChild({ super.key, @@ -43,17 +43,17 @@ class ResponsiveChild extends StatelessWidget { WidgetBuilder? lg, required WidgetBuilder other, }) : _widgets = { - ResponsiveBreakpointKey.xs: xs, - ResponsiveBreakpointKey.sm: sm, - ResponsiveBreakpointKey.md: md, - ResponsiveBreakpointKey.lg: lg, + if (xs != null) ResponsiveBreakpointKey.xs: xs, + if (sm != null) ResponsiveBreakpointKey.sm: sm, + if (md != null) ResponsiveBreakpointKey.md: md, + if (lg != null) ResponsiveBreakpointKey.lg: lg, ResponsiveBreakpointKey.other: other, }; @override Widget build(BuildContext context) { return ResponsiveBuilder( - builder: (context, childBuilder) => childBuilder!(context), + builder: (context, childBuilder) => childBuilder(context), xs: _widgets[ResponsiveBreakpointKey.xs], sm: _widgets[ResponsiveBreakpointKey.sm], md: _widgets[ResponsiveBreakpointKey.md], diff --git a/catalyst_voices/packages/catalyst_voices_shared/lib/src/responsive/responsive_padding.dart b/catalyst_voices/packages/catalyst_voices_shared/lib/src/responsive/responsive_padding.dart index a19087b5b1a..31be0d700ea 100644 --- a/catalyst_voices/packages/catalyst_voices_shared/lib/src/responsive/responsive_padding.dart +++ b/catalyst_voices/packages/catalyst_voices_shared/lib/src/responsive/responsive_padding.dart @@ -49,7 +49,7 @@ class ResponsivePadding extends StatelessWidget { Widget build(BuildContext context) { return ResponsiveBuilder( builder: (context, padding) => Padding( - padding: padding!, + padding: padding, child: child, ), xs: _paddings[ResponsiveBreakpointKey.xs], diff --git a/catalyst_voices/packages/catalyst_voices_shared/lib/src/utils/date_time_ext.dart b/catalyst_voices/packages/catalyst_voices_shared/lib/src/utils/date_time_ext.dart new file mode 100644 index 00000000000..f28c079507d --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_shared/lib/src/utils/date_time_ext.dart @@ -0,0 +1,53 @@ +import 'package:flutter/foundation.dart'; + +extension DateTimeExt on DateTime { + static DateTime? _mockedDateTime; + + /// Overrides the dateTime returned by [now]. + /// Useful for unit testing. + @visibleForTesting + static set mockedDateTime(DateTime? customTime) { + _mockedDateTime = customTime; + } + + /// Testable [DateTime] factory method which returns: + /// - mocked value, if not null, set with [mockedDateTime] + /// - if[utc] current utc [DateTime.timestamp] + /// - else current local [DateTime.now] + static DateTime now({bool utc = false}) { + DateTime? getMockedDateTime() { + return utc ? _mockedDateTime?.toUtc() : _mockedDateTime; + } + + DateTime getDateTime() { + return utc ? DateTime.timestamp() : DateTime.now(); + } + + return getMockedDateTime() ?? getDateTime(); + } + + /// Returns whether two date times have the same year, month and day. + bool isSameDateAs(DateTime other) { + return year == other.year && month == other.month && day == other.day; + } + + /// A method that correctly adds / subtracts days from the dateTime. + /// + /// The default approach of `dateTime.add(Duration(days: 1))` doesn't work + /// for days that have more or less than 24 hours. Due to DST some days have + /// 25 hours and some 23 hours. Duration class is not aware of DST changes so + /// `Duration(days: 1)` literally means `24 hours`. + DateTime plusDays(int days) { + final DateTime temp = + DateTime(year, month, day, 12).add(Duration(days: days)); + if (isUtc) { + return DateTime.utc(temp.year, temp.month, temp.day, hour, minute, second, + millisecond, microsecond); + } + return DateTime(temp.year, temp.month, temp.day, hour, minute, second, + millisecond, microsecond); + } + + /// Subtracts [days] from the datetime, being aware of the DST. + DateTime minusDays(int days) => plusDays(-days); +} diff --git a/catalyst_voices/packages/catalyst_voices_shared/lib/src/utils/iterable_ext.dart b/catalyst_voices/packages/catalyst_voices_shared/lib/src/utils/iterable_ext.dart new file mode 100644 index 00000000000..1fd71195fed --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_shared/lib/src/utils/iterable_ext.dart @@ -0,0 +1,38 @@ +extension IterableExt on Iterable { + /// Syntax sugar for [separatedByIndexed] when index or value does not + /// matter. + /// + /// One common example is Column children separated by SizedBox. + /// + /// ```dart + /// Column( + /// children: [ + /// const Text('Title'), + /// const Text('Subtitle'), + /// const Text('Body'), + /// ].separatedBy(const SizedBox(height: 8)).toList(), + /// ); + /// ``` + Iterable separatedBy(T value) => separatedByIndexed((_, __) => value); + + /// Inserts a value generated by the provided [builder] function between each + /// element of the iterable. + /// + /// The builder function receives the index of the current element and + /// the element itself. + /// Returns a new iterable containing the original elements separated by the + /// values generated by the builder function. + Iterable separatedByIndexed(T Function(int index, T value) builder) sync* { + for (var index = 0; index < length; index++) { + final value = elementAt(index); + final isLast = index == length - 1; + + // Yield the current value and the separator if it's not the last element. + if (isLast) { + yield* [value]; + } else { + yield* [value, builder(index, value)]; + } + } + } +} diff --git a/catalyst_voices/packages/catalyst_voices_shared/lib/src/utils/typedefs.dart b/catalyst_voices/packages/catalyst_voices_shared/lib/src/utils/typedefs.dart new file mode 100644 index 00000000000..88c31ca8111 --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_shared/lib/src/utils/typedefs.dart @@ -0,0 +1,10 @@ +import 'package:catalyst_voices_shared/src/responsive/responsive_builder.dart'; +import 'package:flutter/widgets.dart'; + +/// Builds a [Widget] when given a concrete value of a [ResponsiveBuilder]. +/// +/// See also: +/// +/// * [ResponsiveBuilder], a widget which invokes this builder each time +/// a screenSize changes value. +typedef DataWidgetBuilder = Widget Function(BuildContext context, T data); diff --git a/catalyst_voices/packages/catalyst_voices_shared/pubspec.yaml b/catalyst_voices/packages/catalyst_voices_shared/pubspec.yaml index 9c6b413cb03..fa0fa9dec3c 100644 --- a/catalyst_voices/packages/catalyst_voices_shared/pubspec.yaml +++ b/catalyst_voices/packages/catalyst_voices_shared/pubspec.yaml @@ -4,18 +4,20 @@ version: 0.1.0+1 publish_to: none environment: - sdk: ">=3.3.4 <4.0.0" - flutter: ">=3.22.1" + sdk: ">=3.5.0 <4.0.0" + flutter: ">=3.24.1" dependencies: + catalyst_cardano_serialization: ^0.4.0 + collection: ^1.18.0 flutter: sdk: flutter get_it: ^7.6.7 + intl: ^0.19.0 web: ^0.5.0 dev_dependencies: - catalyst_analysis: - path: ../../../catalyst_voices_packages/catalyst_analysis + catalyst_analysis: ^2.0.0 flutter_test: sdk: flutter test: ^1.24.9 diff --git a/catalyst_voices/packages/catalyst_voices_shared/test/src/formatter/cryptocurrency_formatter_test.dart b/catalyst_voices/packages/catalyst_voices_shared/test/src/formatter/cryptocurrency_formatter_test.dart new file mode 100644 index 00000000000..ea18173170b --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_shared/test/src/formatter/cryptocurrency_formatter_test.dart @@ -0,0 +1,49 @@ +import 'package:catalyst_cardano_serialization/catalyst_cardano_serialization.dart'; +import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group(CryptocurrencyFormatter, () { + test('should format fractional ADA', () { + expect( + CryptocurrencyFormatter.formatAmount(Coin.fromAda(0.21)), + equals('₳0.21'), + ); + }); + + test('should format less than 1000 ADA', () { + expect( + CryptocurrencyFormatter.formatAmount(Coin.fromAda(975)), + equals('₳975'), + ); + }); + + test('should format 1000 ADA', () { + expect( + CryptocurrencyFormatter.formatAmount(Coin.fromAda(1000)), + equals('₳1K'), + ); + }); + + test('should format amounts in thousands of ADA', () { + expect( + CryptocurrencyFormatter.formatAmount(Coin.fromAda(15000)), + equals('₳15K'), + ); + }); + + test('should format amounts in millions of ADA', () { + expect( + CryptocurrencyFormatter.formatAmount(Coin.fromAda(2500000)), + equals('₳2.5M'), + ); + }); + + test('should format exactly 1 million ADA', () { + expect( + CryptocurrencyFormatter.formatAmount(Coin.fromAda(1000000)), + equals('₳1M'), + ); + }); + }); +} diff --git a/catalyst_voices/packages/catalyst_voices_shared/test/src/responsive_builder_test.dart b/catalyst_voices/packages/catalyst_voices_shared/test/src/responsive_builder_test.dart index a3d1cb13f81..35a038ca1d1 100644 --- a/catalyst_voices/packages/catalyst_voices_shared/test/src/responsive_builder_test.dart +++ b/catalyst_voices/packages/catalyst_voices_shared/test/src/responsive_builder_test.dart @@ -37,7 +37,7 @@ void main() { md: 'Medium device', lg: 'Large device', other: 'Other device', - builder: (context, data) => Text(data!), + builder: (context, data) => Text(data), ), ), ); @@ -73,7 +73,7 @@ void main() { lg: const EdgeInsets.all(16), other: const EdgeInsets.all(32), builder: (context, padding) => Padding( - padding: padding!, + padding: padding, child: const Text('Test'), ), ), diff --git a/catalyst_voices/packages/catalyst_voices_shared/test/src/utils/date_time_ext_test.dart b/catalyst_voices/packages/catalyst_voices_shared/test/src/utils/date_time_ext_test.dart new file mode 100644 index 00000000000..67474596046 --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_shared/test/src/utils/date_time_ext_test.dart @@ -0,0 +1,27 @@ +import 'package:catalyst_voices_shared/src/utils/date_time_ext.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('DateTimeExt', () { + test('isSameDateAs', () { + final DateTime today = DateTimeExt.now(); + expect(today.isSameDateAs(today), isTrue); + expect(today.isSameDateAs(today.plusDays(1)), isFalse); + expect(today.isSameDateAs(today.minusDays(1)), isFalse); + }); + + test('plusDays', () { + final DateTime dstEnd = DateTime(2022, 10, 30, 0, 0); + final DateTime nextDay = DateTime(2022, 10, 31, 0, 0); + + expect(dstEnd.plusDays(1), equals(nextDay)); + }); + + test('minusDays', () { + final DateTime dstEnd = DateTime(2022, 10, 31, 0, 0); + final DateTime previousDay = DateTime(2022, 10, 30, 0, 0); + + expect(dstEnd.minusDays(1), equals(previousDay)); + }); + }); +} diff --git a/catalyst_voices/packages/catalyst_voices_shared/test/src/utils/iterable_ext_test.dart b/catalyst_voices/packages/catalyst_voices_shared/test/src/utils/iterable_ext_test.dart new file mode 100644 index 00000000000..7725e045470 --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_shared/test/src/utils/iterable_ext_test.dart @@ -0,0 +1,85 @@ +import 'package:catalyst_voices_shared/src/catalyst_voices_shared.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('separatedBy', () { + test('adds given separator between each item in the list', () { + // Given + const source = [1, 2, 3, 4]; + const separator = 99; + const expectedList = [1, separator, 2, separator, 3, separator, 4]; + + // When + final separatedSource = source.separatedBy(separator); + + // Then + expect(separatedSource, expectedList); + }); + + test('adds nothing when source is empty', () { + // Given + const source = []; + const separator = 99; + const expectedList = []; + + // When + final separatedSource = source.separatedBy(separator); + + // Then + expect(separatedSource, expectedList); + }); + + test('adds nothing when source has one item', () { + // Given + const source = [1]; + const separator = 99; + const expectedList = [1]; + + // When + final separatedSource = source.separatedBy(separator); + + // Then + expect(separatedSource, expectedList); + }); + }); + + group('separatedByIndexed', () { + test('inserts correctly separator base on index', () { + // Given + const source = [1, 2, 3, 4]; + const expectedList = [1, 0, 2, 1, 3, 2, 4]; + + // When + final separatedSource = source.separatedByIndexed((index, _) => index); + + // Then + expect(separatedSource, expectedList); + }); + + test('adds nothing when source is empty', () { + // Given + const source = []; + const separator = 99; + const expectedList = []; + + // When + final separatedSource = source.separatedByIndexed((_, __) => separator); + + // Then + expect(separatedSource, expectedList); + }); + + test('adds nothing when source has one item', () { + // Given + const source = [1]; + const separator = 99; + const expectedList = [1]; + + // When + final separatedSource = source.separatedByIndexed((_, __) => separator); + + // Then + expect(separatedSource, expectedList); + }); + }); +} diff --git a/catalyst_voices/packages/catalyst_voices_view_models/analysis_options.yaml b/catalyst_voices/packages/catalyst_voices_view_models/analysis_options.yaml index 2e7be08b970..83c56e6802f 100644 --- a/catalyst_voices/packages/catalyst_voices_view_models/analysis_options.yaml +++ b/catalyst_voices/packages/catalyst_voices_view_models/analysis_options.yaml @@ -1,4 +1,4 @@ -include: package:catalyst_analysis/analysis_options.1.0.0.yaml +include: package:catalyst_analysis/analysis_options.yaml analyzer: exclude: [ diff --git a/catalyst_voices/packages/catalyst_voices_view_models/pubspec.yaml b/catalyst_voices/packages/catalyst_voices_view_models/pubspec.yaml index 1ce0b937fc1..5268e3c258f 100644 --- a/catalyst_voices/packages/catalyst_voices_view_models/pubspec.yaml +++ b/catalyst_voices/packages/catalyst_voices_view_models/pubspec.yaml @@ -4,8 +4,8 @@ version: 0.1.0+1 publish_to: none environment: - sdk: ">=3.3.4 <4.0.0" - flutter: ">=3.22.1" + sdk: ">=3.5.0 <4.0.0" + flutter: ">=3.24.1" dependencies: equatable: ^2.0.5 @@ -14,6 +14,5 @@ dependencies: formz: ^0.7.0 dev_dependencies: - catalyst_analysis: - path: ../../../catalyst_voices_packages/catalyst_analysis + catalyst_analysis: ^2.0.0 test: ^1.24.9 \ No newline at end of file diff --git a/catalyst_voices/pubspec.yaml b/catalyst_voices/pubspec.yaml index 93c8a90d5d5..a2ad10b732c 100644 --- a/catalyst_voices/pubspec.yaml +++ b/catalyst_voices/pubspec.yaml @@ -4,15 +4,15 @@ version: 0.1.0+1 publish_to: none environment: - sdk: ">=3.3.4 <4.0.0" - flutter: ">=3.22.1" + sdk: ">=3.5.0 <4.0.0" + flutter: ">=3.24.1" dependencies: animated_text_kit: ^4.2.2 animations: ^2.0.11 - catalyst_cardano: ^0.1.3 - catalyst_cardano_serialization: ^0.1.3 - catalyst_cardano_web: ^0.1.3 + catalyst_cardano: ^0.3.0 + catalyst_cardano_serialization: ^0.4.0 + catalyst_cardano_web: ^0.3.0 catalyst_voices_assets: path: ./packages/catalyst_voices_assets catalyst_voices_blocs: @@ -32,25 +32,31 @@ dependencies: catalyst_voices_view_models: path: ./packages/catalyst_voices_view_models collection: ^1.18.0 + equatable: ^2.0.5 flutter: sdk: flutter - flutter_adaptive_scaffold: ^0.1.11 - flutter_bloc: ^8.1.3 + flutter_adaptive_scaffold: ^0.2.4 + flutter_bloc: ^8.1.5 flutter_localized_locales: ^2.0.5 + flutter_quill: ^10.5.0 + flutter_quill_extensions: ^10.5.0 flutter_web_plugins: sdk: flutter formz: ^0.7.0 go_router: ^14.0.2 google_fonts: ^6.2.1 - sentry_flutter: ^8.3.0 + sentry_flutter: ^8.8.0 url_launcher: ^6.2.2 - url_strategy: ^0.2.0 + url_strategy: ^0.3.0 + # TODO(dtscalac): win32 dependency is just a transitive dependency and shouldn't be imported + # but here we import it explicitly to make sure the latest version is used which addresses + # the problem from here: https://github.com/jonataslaw/get_cli/issues/263 + win32: ^5.5.4 dev_dependencies: - build_runner: ^2.3.3 + build_runner: ^2.4.12 build_verify: ^3.1.0 - catalyst_analysis: - path: ../catalyst_voices_packages/catalyst_analysis + catalyst_analysis: ^2.0.0 flutter_test: sdk: flutter go_router_builder: ^2.4.1 diff --git a/catalyst_voices/test/app/view/app_test.dart b/catalyst_voices/test/app/view/app_test.dart index 28bfe199c9d..3f47f1d0afe 100644 --- a/catalyst_voices/test/app/view/app_test.dart +++ b/catalyst_voices/test/app/view/app_test.dart @@ -1,4 +1,4 @@ -import 'package:catalyst_voices/pages/coming_soon/coming_soon.dart'; +import 'package:catalyst_voices/pages/coming_soon/coming_soon_page.dart'; import 'package:flutter_test/flutter_test.dart'; import '../../helpers/helpers.dart'; diff --git a/catalyst_voices/test/common/formatters/date_formatter_test.dart b/catalyst_voices/test/common/formatters/date_formatter_test.dart new file mode 100644 index 00000000000..0b163672d45 --- /dev/null +++ b/catalyst_voices/test/common/formatters/date_formatter_test.dart @@ -0,0 +1,53 @@ +import 'package:catalyst_voices/common/formatters/date_formatter.dart'; +import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; +import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:intl/intl.dart'; + +class _FakeVoicesLocalizations extends Fake implements VoicesLocalizations { + @override + String get today => 'Today'; + @override + String get tomorrow => 'Tomorrow'; + @override + String get yesterday => 'Yesterday'; + @override + String get twoDaysAgo => '2 days ago'; +} + +void main() { + group(DateFormatter, () { + final l10n = _FakeVoicesLocalizations(); + + test('should return "Today" for today\'s date', () { + final today = DateTimeExt.now(); + final result = DateFormatter.formatRecentDate(l10n, today); + expect(result, l10n.today); + }); + + test('should return "Tomorrow" for tomorrow\'s date', () { + final tomorrow = DateTimeExt.now().plusDays(1); + final result = DateFormatter.formatRecentDate(l10n, tomorrow); + expect(result, l10n.tomorrow); + }); + + test('should return "Yesterday" for yesterday\'s date', () { + final yesterday = DateTimeExt.now().minusDays(1); + final result = DateFormatter.formatRecentDate(l10n, yesterday); + expect(result, l10n.yesterday); + }); + + test('should return "2 days ago" for a date 2 days ago', () { + final twoDaysAgo = DateTimeExt.now().minusDays(2); + final result = DateFormatter.formatRecentDate(l10n, twoDaysAgo); + expect(result, l10n.twoDaysAgo); + }); + + test('should return formatted date for older dates', () { + final pastDate = DateTimeExt.now().minusDays(10); + final result = DateFormatter.formatRecentDate(l10n, pastDate); + final expectedFormat = DateFormat.yMMMMd().format(pastDate); + expect(result, expectedFormat); + }); + }); +} diff --git a/catalyst_voices/test/helpers/pump_app.dart b/catalyst_voices/test/helpers/pump_app.dart index 9edb76d5161..0e8ad8de6fe 100644 --- a/catalyst_voices/test/helpers/pump_app.dart +++ b/catalyst_voices/test/helpers/pump_app.dart @@ -1,10 +1,21 @@ +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_localized_locales/flutter_localized_locales.dart'; import 'package:flutter_test/flutter_test.dart'; extension PumpApp on WidgetTester { - Future pumpApp(Widget widget) { + Future pumpApp( + Widget widget, { + ThemeData? theme, + VoicesColorScheme voicesColors = const VoicesColorScheme.optional(), + }) { + final effectiveTheme = (theme ?? ThemeData()).copyWith( + extensions: [ + voicesColors, + ], + ); + return pumpWidget( MaterialApp( localizationsDelegates: const [ @@ -13,6 +24,7 @@ extension PumpApp on WidgetTester { ], supportedLocales: VoicesLocalizations.supportedLocales, localeListResolutionCallback: basicLocaleListResolution, + theme: effectiveTheme, home: widget, ), ); diff --git a/catalyst_voices/test/widgets/avatars/voices_avatar_test.dart b/catalyst_voices/test/widgets/avatars/voices_avatar_test.dart new file mode 100644 index 00000000000..1c98c6f849f --- /dev/null +++ b/catalyst_voices/test/widgets/avatars/voices_avatar_test.dart @@ -0,0 +1,146 @@ +import 'package:catalyst_voices/widgets/avatars/voices_avatar.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group(VoicesAvatar, () { + testWidgets('VoicesAvatar renders with default properties', (tester) async { + // Create the widget by wrapping it in a MaterialApp for theme access. + await tester.pumpWidget( + const MaterialApp( + home: Scaffold( + body: VoicesAvatar(icon: Icon(Icons.person)), + ), + ), + ); + + // Verify if CircleAvatar is rendered with the correct default radius. + final circleAvatarFinder = find.byType(CircleAvatar); + expect(circleAvatarFinder, findsOneWidget); + + final circleAvatarWidget = + tester.widget(circleAvatarFinder); + expect(circleAvatarWidget.radius, 20); + + // Verify the icon is rendered. + expect(find.byIcon(Icons.person), findsOneWidget); + }); + + testWidgets('VoicesAvatar applies custom radius and padding', + (tester) async { + await tester.pumpWidget( + const MaterialApp( + home: Scaffold( + body: VoicesAvatar( + icon: Icon(Icons.person), + radius: 30, + padding: EdgeInsets.all(16), + ), + ), + ), + ); + + // Verify if CircleAvatar is rendered with the correct custom radius. + final circleAvatarFinder = find.byType(CircleAvatar); + final circleAvatarWidget = + tester.widget(circleAvatarFinder); + expect(circleAvatarWidget.radius, 30); + + // Verify the Padding is applied correctly. + final paddingFinder = find.ancestor( + of: find.byType(Icon), + matching: find.byType(Padding), + ); + final paddingWidget = tester.firstWidget(paddingFinder); + expect(paddingWidget.padding, const EdgeInsets.all(16)); + }); + + testWidgets('VoicesAvatar uses custom foreground and background colors', + (tester) async { + const foregroundColor = Colors.red; + const backgroundColor = Colors.green; + + await tester.pumpWidget( + const MaterialApp( + home: Scaffold( + body: VoicesAvatar( + icon: Icon(Icons.person), + foregroundColor: foregroundColor, + backgroundColor: backgroundColor, + ), + ), + ), + ); + + // Verify the background color is correctly applied. + final circleAvatarFinder = find.byType(CircleAvatar); + final circleAvatarWidget = + tester.widget(circleAvatarFinder); + expect(circleAvatarWidget.backgroundColor, backgroundColor); + + // Verify the foreground color is correctly applied to the icon. + final iconThemeFinder = find.ancestor( + of: find.byType(Icon), + matching: find.byType(IconTheme), + ); + final iconThemeWidget = tester.firstWidget(iconThemeFinder); + + expect(iconThemeWidget.data.color, foregroundColor); + }); + + testWidgets('VoicesAvatar calls onTap when tapped', (tester) async { + var tapped = false; + + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: VoicesAvatar( + icon: const Icon(Icons.person), + onTap: () { + tapped = true; + }, + ), + ), + ), + ); + + // Tap the VoicesAvatar widget. + await tester.tap(find.byType(VoicesAvatar)); + await tester.pump(); + + // Verify the onTap callback is called. + expect(tapped, true); + }); + + testWidgets('VoicesAvatar applies theme colors by default', (tester) async { + final customTheme = ThemeData( + colorScheme: const ColorScheme.light( + primary: Colors.blue, + primaryContainer: Colors.blueGrey, + ), + ); + + await tester.pumpWidget( + MaterialApp( + theme: customTheme, + home: const Scaffold( + body: VoicesAvatar( + icon: Icon(Icons.person), + ), + ), + ), + ); + + // Verify the background color is from the theme's primaryContainer. + final circleAvatarFinder = find.byType(CircleAvatar); + final circleAvatarWidget = + tester.widget(circleAvatarFinder); + expect(circleAvatarWidget.backgroundColor, Colors.blueGrey); + + // Verify the foreground color is from the theme's primary. + final iconThemeFinder = find.byType(IconTheme); + final iconThemeWidget = tester.firstWidget(iconThemeFinder); + expect(iconThemeWidget.data.color, Colors.blue); + }); + }); +} diff --git a/catalyst_voices/test/widgets/common/columns_row_test.dart b/catalyst_voices/test/widgets/common/columns_row_test.dart new file mode 100644 index 00000000000..6454711974c --- /dev/null +++ b/catalyst_voices/test/widgets/common/columns_row_test.dart @@ -0,0 +1,132 @@ +import 'package:catalyst_voices/widgets/common/columns_row.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('ColumnsRow', () { + testWidgets( + 'basic layout', + (tester) async { + // Given + final children = List.generate( + 6, + (index) => Container(key: Key('child_$index')), + ); + + final widget = MaterialApp( + home: ColumnsRow( + columnsCount: 3, + children: children, + ), + ); + + // When + await tester.pumpWidget(widget); + + // Then + expect(find.byKey(const Key('child_0')), findsOneWidget); + expect(find.byKey(const Key('child_3')), findsOneWidget); + expect(find.byKey(const Key('child_5')), findsOneWidget); + }, + ); + + testWidgets( + 'renders correctly with default spacing', + (tester) async { + // Given + final children = List.generate( + 6, + (index) => Text('Item $index'), + ); + + final widget = MaterialApp( + home: ColumnsRow( + columnsCount: 2, + children: children, + ), + ); + + // When + await tester.pumpWidget(widget); + + // Then + for (var i = 0; i < children.length; i++) { + expect(find.text('Item $i'), findsOneWidget); + } + + expect(find.byType(Column), findsNWidgets(2)); + }, + ); + + testWidgets( + 'correctly arranges children in specified columns', + (tester) async { + // Given + const mainAxisSpacing = 12.0; + const crossAxisSpacing = 8.0; + final children = List.generate( + 8, + (index) => Text('Item $index'), + ); + + final widget = MaterialApp( + home: ColumnsRow( + columnsCount: 3, + mainAxisSpacing: mainAxisSpacing, + crossAxisSpacing: crossAxisSpacing, + children: children, + ), + ); + + // When + await tester.pumpWidget(widget); + + // Then + for (var i = 0; i < children.length; i++) { + expect(find.text('Item $i'), findsOneWidget); + } + + // Check the structure of the Column widgets + expect(find.byType(Column), findsNWidgets(3)); + + final columnWidgets = find.byType(Column).evaluate().toList(); + expect(columnWidgets.length, 3); + + final firstColumnChildren = columnWidgets[0].widget as Column; + final secondColumnChildren = columnWidgets[1].widget as Column; + final thirdColumnChildren = columnWidgets[2].widget as Column; + + expect((firstColumnChildren.children.first as Text).data, 'Item 0'); + expect( + (firstColumnChildren.children[1] as SizedBox).height, + crossAxisSpacing, + ); + expect((firstColumnChildren.children[2] as Text).data, 'Item 1'); + expect( + (firstColumnChildren.children[3] as SizedBox).height, + crossAxisSpacing, + ); + expect((firstColumnChildren.children[4] as Text).data, 'Item 2'); + + expect((secondColumnChildren.children.first as Text).data, 'Item 3'); + expect( + (secondColumnChildren.children[1] as SizedBox).height, + crossAxisSpacing, + ); + expect((secondColumnChildren.children[2] as Text).data, 'Item 4'); + expect( + (secondColumnChildren.children[3] as SizedBox).height, + crossAxisSpacing, + ); + expect((secondColumnChildren.children[4] as Text).data, 'Item 5'); + + expect((thirdColumnChildren.children.first as Text).data, 'Item 6'); + expect( + (thirdColumnChildren.children[1] as SizedBox).height, + crossAxisSpacing, + ); + expect((thirdColumnChildren.children[2] as Text).data, 'Item 7'); + }, + ); + }); +} diff --git a/catalyst_voices/test/widgets/common/link_text_test.dart b/catalyst_voices/test/widgets/common/link_text_test.dart new file mode 100644 index 00000000000..c058a07f10d --- /dev/null +++ b/catalyst_voices/test/widgets/common/link_text_test.dart @@ -0,0 +1,44 @@ +import 'package:catalyst_voices/widgets/common/link_text.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../helpers/helpers.dart'; + +void main() { + group(LinkText, () { + testWidgets('renders correctly and triggers onTap', (tester) async { + // Given + final tapNotifier = ValueNotifier(false); + final widget = Scaffold( + body: LinkText( + 'This is a link', + onTap: () { + tapNotifier.value = true; + }, + ), + ); + + // When + await tester.pumpApp(widget); + await tester.pumpAndSettle(); + + // Then + // Verify that the text and underline are rendered correctly + expect(find.text('This is a link'), findsOne); + expect( + find.byWidgetPredicate( + (widget) => + widget is Text && + widget.data == 'This is a link' && + widget.style?.decoration == TextDecoration.underline, + ), + findsOne, + ); + + // Simulate a tap and verify that the onTap callback is called + await tester.tap(find.text('This is a link')); + await tester.pump(); + expect(tapNotifier.value, true); + }); + }); +} diff --git a/catalyst_voices/test/widgets/common/navigation_location_test.dart b/catalyst_voices/test/widgets/common/navigation_location_test.dart new file mode 100644 index 00000000000..2a7fc43a62c --- /dev/null +++ b/catalyst_voices/test/widgets/common/navigation_location_test.dart @@ -0,0 +1,55 @@ +import 'package:catalyst_voices/widgets/common/navigation_location.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../helpers/helpers.dart'; + +void main() { + group(NavigationLocation, () { + testWidgets( + 'renders correctly', + (tester) async { + // Given + const navigationLocation = NavigationLocation( + parts: ['Home', 'Products', 'Cart'], + ); + + // When + await tester.pumpApp(navigationLocation); + await tester.pumpAndSettle(); + + // Then + expect( + find.byWidgetPredicate( + (widget) => + widget is RichText && + widget.text.toPlainText() == 'Home / Products / Cart', + description: 'Finds RichText widget with matching text', + ), + findsOne, + ); + }, + ); + + testWidgets( + 'handles empty parts list', + (tester) async { + // Given + const navigationLocation = NavigationLocation(parts: []); + + // When + await tester.pumpApp(navigationLocation); + await tester.pumpAndSettle(); + + // Then + expect( + find.byWidgetPredicate( + (widget) => widget is RichText && widget.text.toPlainText() == '', + description: 'Finds empty RichText', + ), + findsOne, + ); + }, + ); + }); +} diff --git a/catalyst_voices/test/widgets/common/tab_bar_stack_view_test.dart b/catalyst_voices/test/widgets/common/tab_bar_stack_view_test.dart new file mode 100644 index 00000000000..b7594d1ab9e --- /dev/null +++ b/catalyst_voices/test/widgets/common/tab_bar_stack_view_test.dart @@ -0,0 +1,99 @@ +import 'package:catalyst_voices/widgets/common/tab_bar_stack_view.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../helpers/helpers.dart'; + +void main() { + group(TabBarStackView, () { + testWidgets( + 'DefaultTabController is used when controller not specified explicitly', + (tester) async { + // Given + const widget = Scaffold( + body: DefaultTabController( + length: 2, + child: Column( + children: [ + TabBar( + tabs: [ + Tab(text: 'one'), + Tab(text: 'two'), + ], + ), + TabBarStackView( + children: [ + Text('One Body'), + Text('Two Body'), + ], + ), + ], + ), + ), + ); + + // When + await tester.pumpApp(widget); + await tester.pumpAndSettle(); + + // Then + expect(find.text('One Body'), findsOne); + expect(find.text('Two Body'), findsNothing); + + await tester.tap(find.text('two')); + await tester.pump(); + + expect(find.text('One Body'), findsNothing); + expect(find.text('Two Body'), findsOne); + }, + ); + + testWidgets( + 'controller is used when specified explicitly', + (tester) async { + // Given + const vsync = TestVSync(); + final controller = TabController(length: 2, vsync: vsync); + addTearDown(controller.dispose); + + final widget = Scaffold( + body: DefaultTabController( + length: 2, + child: Column( + children: [ + TabBar( + controller: controller, + tabs: const [ + Tab(text: 'one'), + Tab(text: 'two'), + ], + ), + TabBarStackView( + controller: controller, + children: const [ + Text('One Body'), + Text('Two Body'), + ], + ), + ], + ), + ), + ); + + // When + await tester.pumpApp(widget); + await tester.pumpAndSettle(); + + // Then + expect(find.text('One Body'), findsOne); + expect(find.text('Two Body'), findsNothing); + + controller.animateTo(1); + await tester.pumpAndSettle(); + + expect(find.text('One Body'), findsNothing); + expect(find.text('Two Body'), findsOne); + }, + ); + }); +} diff --git a/catalyst_voices/test/widgets/headers/section_header_test.dart b/catalyst_voices/test/widgets/headers/section_header_test.dart new file mode 100644 index 00000000000..a9b8a67b22d --- /dev/null +++ b/catalyst_voices/test/widgets/headers/section_header_test.dart @@ -0,0 +1,85 @@ +import 'package:catalyst_voices/widgets/headers/section_header.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../helpers/helpers.dart'; + +void main() { + group(SectionHeader, () { + testWidgets( + 'renders correctly with default values', + (tester) async { + // Given + const widget = SectionHeader(title: Text('Title')); + + // When + await tester.pumpApp(widget); + await tester.pumpAndSettle(); + + // Then + expect(find.text('Title'), findsOneWidget); + }, + ); + + testWidgets( + 'leading and trailing are rendered', + (tester) async { + // Given + final widget = SectionHeader( + leading: IconButton( + key: const ValueKey('BackIconButtonKey'), + onPressed: () {}, + icon: const Icon(Icons.arrow_left), + ), + title: const Text('Title'), + trailing: [ + IconButton( + key: const ValueKey('SettingsIconButtonKey'), + onPressed: () {}, + icon: const Icon(Icons.settings), + ), + ], + ); + + // When + await tester.pumpApp(widget); + await tester.pumpAndSettle(); + + // Then + expect(find.byIcon(Icons.arrow_left), findsOneWidget); + expect(find.byIcon(Icons.settings), findsOneWidget); + }, + ); + + testWidgets( + 'DefaultTextStyle is configured correctly', + (tester) async { + // Given + const widget = SectionHeader(title: Text('Title')); + const voicesColors = VoicesColorScheme.optional( + textOnPrimary: Colors.deepOrange, + ); + + // When + await tester.pumpApp( + widget, + voicesColors: voicesColors, + ); + await tester.pumpAndSettle(); + + final defaultTextStyleWidget = tester.firstWidget( + find.ancestor( + of: find.text('Title'), + matching: find.byType(DefaultTextStyle), + ), + ); + + // Then + expect(defaultTextStyleWidget.style.color, Colors.deepOrange); + expect(defaultTextStyleWidget.maxLines, 1); + expect(defaultTextStyleWidget.overflow, TextOverflow.ellipsis); + }, + ); + }); +} diff --git a/catalyst_voices/test/widgets/indicators/voices_status_indicator_test.dart b/catalyst_voices/test/widgets/indicators/voices_status_indicator_test.dart new file mode 100644 index 00000000000..bd7469e25fe --- /dev/null +++ b/catalyst_voices/test/widgets/indicators/voices_status_indicator_test.dart @@ -0,0 +1,84 @@ +import 'package:catalyst_voices/widgets/indicators/voices_status_indicator.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('VoicesStatusIndicator', () { + testWidgets( + 'VoicesStatusIndicator with Success type builds correctly', + (tester) async { + // Arrange + const status = 'QR VERIFIED'; + const title = 'Your QR code verified successfully'; + const body = 'You can now use your QR-code 
to login into Catalyst.'; + + const colors = VoicesColorScheme.optional( + successContainer: Colors.green, + ); + + // Act + await tester.pumpWidget( + MaterialApp( + theme: ThemeData(extensions: const [colors]), + home: const VoicesStatusIndicator( + status: Text(status), + title: Text(title), + body: Text(body), + type: VoicesStatusIndicatorType.success, + ), + ), + ); + + // Assert + expect(find.text(status), findsOneWidget); + expect(find.text(title), findsOneWidget); + expect(find.text(body), findsOneWidget); + final container = + tester.firstWidget(find.byType(Container)) as Container; + expect( + (container.decoration! as BoxDecoration).color, + equals(colors.successContainer), + ); + }, + ); + + testWidgets( + 'VoicesStatusIndicator with Error type builds correctly', + (tester) async { + // Arrange + const status = 'Error'; + const title = 'An error occurred'; + const body = 'Please try again later.'; + + const colors = VoicesColorScheme.optional( + errorContainer: Colors.red, + ); + + // Act + await tester.pumpWidget( + MaterialApp( + theme: ThemeData(extensions: const [colors]), + home: const VoicesStatusIndicator( + status: Text(status), + title: Text(title), + body: Text(body), + type: VoicesStatusIndicatorType.error, + ), + ), + ); + + // Assert + expect(find.text(status), findsOneWidget); + expect(find.text(title), findsOneWidget); + expect(find.text(body), findsOneWidget); + final container = + tester.firstWidget(find.byType(Container)) as Container; + expect( + (container.decoration! as BoxDecoration).color, + equals(colors.errorContainer), + ); + }, + ); + }); +} diff --git a/catalyst_voices/test/widgets/menu/voices_menu_test.dart b/catalyst_voices/test/widgets/menu/voices_menu_test.dart new file mode 100644 index 00000000000..e024a04a275 --- /dev/null +++ b/catalyst_voices/test/widgets/menu/voices_menu_test.dart @@ -0,0 +1,98 @@ +import 'package:catalyst_voices/widgets/menu/voices_menu.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import '../../helpers/helpers.dart'; + +void main() { + final menu = [ + MenuItem( + id: 1, + label: 'Rename', + icon: CatalystVoicesIcons.pencil, + ), + SubMenuItem( + id: 2, + label: 'Move Private Team', + icon: CatalystVoicesIcons.switch_horizontal, + children: [ + MenuItem( + id: 3, + label: 'Team 1: The Vikings', + ), + MenuItem( + id: 4, + label: 'Team 2: Pure Hearts', + ), + ], + ), + MenuItem( + id: 5, + label: 'Move to public', + icon: CatalystVoicesIcons.switch_horizontal, + showDivider: true, + enabled: false, + ), + MenuItem( + id: 6, + label: 'Delete', + icon: CatalystVoicesIcons.trash, + ), + ]; + + group(VoicesMenu, () { + testWidgets('displays first level menu correctly', (tester) async { + // Given + final widget = VoicesMenu( + menuItems: menu, + child: const Text('sample menu'), + ); + + // When + await tester.pumpApp(widget); + await tester.pumpAndSettle(); + + final gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); + await gesture.addPointer(location: Offset.zero); + addTearDown(gesture.removePointer); + await tester.pump(); + await gesture.moveTo(tester.getCenter(find.byType(Text))); + await tester.tap(find.text('sample menu')); + await tester.pumpAndSettle(); + + // Then + expect(find.byType(SubmenuButton), findsExactly(2)); + expect(find.byType(MenuItemButton), findsExactly(3)); + expect(find.text('Rename'), findsOne); + }); + + testWidgets('displays nested menu correctly', (tester) async { + // Given + final widget = VoicesMenu( + menuItems: menu, + child: const Text('sample menu'), + ); + + // When + await tester.pumpApp(widget); + await tester.pumpAndSettle(); + + final gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); + await gesture.addPointer(location: Offset.zero); + addTearDown(gesture.removePointer); + await tester.pump(); + await gesture.moveTo(tester.getCenter(find.byType(Text))); + await tester.tap(find.text('sample menu')); + await tester.pumpAndSettle(); + + await tester.tap(find.text('Move Private Team')); + await tester.pumpAndSettle(); + + // Then + expect(find.byType(SubmenuButton), findsExactly(2)); + expect(find.byType(MenuItemButton), findsExactly(5)); + expect(find.text('Team 1: The Vikings'), findsOne); + }); + }); +} diff --git a/catalyst_voices/test/widgets/menu/voices_wallet_tile_test.dart b/catalyst_voices/test/widgets/menu/voices_wallet_tile_test.dart new file mode 100644 index 00000000000..b206ef076a9 --- /dev/null +++ b/catalyst_voices/test/widgets/menu/voices_wallet_tile_test.dart @@ -0,0 +1,171 @@ +import 'package:catalyst_voices/widgets/menu/voices_wallet_tile.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../helpers/pump_app.dart'; + +void main() { + group('VoicesWalletTile Tests', () { + testWidgets('renders correctly with icon and name', (tester) async { + const testName = Text('Test Wallet'); + + await tester.pumpApp( + Scaffold( + body: VoicesWalletTile( + iconSrc: _testIcon, + name: testName, + ), + ), + ); + await tester.pumpAndSettle(); + + expect(find.byType(Image), findsOneWidget); + expect(find.byType(Text), findsOneWidget); + expect(find.text('Test Wallet'), findsOneWidget); + }); + + testWidgets('renders placeholder when icon is null', (tester) async { + await tester.pumpApp( + Scaffold( + body: VoicesWalletTile( + name: const Text('Test Wallet'), + ), + ), + ); + await tester.pumpAndSettle(); + + expect(find.byType(_IconPlaceholder), findsOneWidget); + }); + + testWidgets('does not render name when it is null', (tester) async { + await tester.pumpApp( + Scaffold( + body: VoicesWalletTile( + iconSrc: _testIcon, + ), + ), + ); + await tester.pumpAndSettle(); + + expect(find.byType(Text), findsNothing); + }); + + testWidgets('triggers onTap callback when tapped', (tester) async { + bool tapped = false; + + await tester.pumpApp( + Scaffold( + body: VoicesWalletTile( + iconSrc: _testIcon, + name: const Text('Test Wallet'), + onTap: () { + tapped = true; + }, + ), + ), + ); + await tester.pumpAndSettle(); + + await tester.tap(find.byType(ListTile)); + await tester.pumpAndSettle(); + + expect(tapped, isTrue); + }); + + testWidgets('shows placeholder on image load failure', (tester) async { + await tester.pumpApp( + Scaffold( + body: VoicesWalletTile( + iconSrc: 'https://example.com/non_existent_icon.png', + name: const Text('Test Wallet'), + ), + ), + ); + await tester.pumpAndSettle(); + + // Simulate a loading error. + final imageFinder = find.byType(Image); + final imageWidget = tester.widget(imageFinder); + final errorBuilder = imageWidget.errorBuilder; + if (errorBuilder != null) { + await tester.pumpApp( + Scaffold( + body: errorBuilder( + tester.element(imageFinder), + Exception('Error'), + StackTrace.current, + ), + ), + ); + } + + expect(find.byType(_IconPlaceholder), findsOneWidget); + }); + }); +} + +const _testIcon = + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAACX' + 'BIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAxlSURBVH' + 'gB7Z0JdFTVGcf/M1kJkMWEAAGyCEpQ3A+R2lJltRVpWTxVRCC0BQWkkB5pWVoNHtmqshShKi' + 'qrWtqyCFLaQ1kUjxpoS0UqiSIMCUGCIYRAyDJJpvf/kjedebx57817b8L6O+fxJjN35s37z3' + 'e/+917v3txwEY8Hk+8OE0WxxRxxOuVr6iowL59+1BcXIyCggLpzIPIZ5nY2FikpKRI5w4dOk' + 'iPe/TogczMTOk5A7jEMcvhcKyEjThgE0I8CpcLDeFkwXjs2LHjIpHMQkGzsrLQp08fSVQdQV' + '2wUUjLAgrhHhCn58TxQKAye/fuxZo1ayThKGKoGTJkiCRm3759tYq5xNFbCOmCBUwL2FRdKd' + 'wUtdcpFEXj0RyiqUHLnDhxomSVfByAXCHiLJjElIBCvHRx2iWOdOVrl4NwSije4MGDJTED4I' + 'JJawxaQCHeaHFaBBVft2nTJixdutQ232Y3skVSTBXK0egbFyEIghJQiMcqm6t8noLNnDlT8n' + 'VXAvSN06ZNC1Stg6rShgUU4q0Qp2zl86tXr8ayZcsum+pqlNatW2P69OmBrHGREDHHwMcYE1' + 'BNPApG4SjglcyoUaMka1RhpRBxjN77dQVUE49VdtKkScjPz8fVAIPxJUuWqFVpXRE1BRTiLY' + 'QiTKF42dnZl21DYRaKt3LlyqBFdAZ6oanBuCbEIxr3lt2khSqqFtjULVtk8AJXFRqWmC0scZ' + 'XyyYsEbAqS98MnzrtWxJMJICLjxLuUwbaagEeh6GEMHTq02RuMsAgPolp4pMfuagfctbaNex' + 'iCDQtFVAxMuNAoYrn8RLjvq011Pd33uXnz5oVUPGe4B13vqUJa9yp06lqDtqk1SE5zo2VcvV' + '+5c2XhKCmMREVpGL76dwyOHGghjmg01IVGWN4zwzRFiJOOxv6/N0b0Xr2p6h71Lc2u2YwZMx' + 'AKMrMu4I7e59BzUAVib6iDGUqLI3FwTww+2hiPo5+3QCiggIwVFbDfvJsPfAX0q7r0d8OGDb' + 'O1h+EQV+vxgwr0GXEGN919AXZCi3zvlTY4+FFL2Al7LBs2bFD6w91CwN58IAkoxMsWpxW+JR' + 'goc9DTLtJuqcZPppYg8157hVNyeH8M1s5qh6Ivo2AXHKylP1SQw4EHWUA/67Oz6oaLxmDguF' + 'L8cOxpRER60By4axzY+loSti5PREO9PT5yzpw5yn4zG5KMsCbry/Z9hdZ37tw5WCU2sR5PvV' + 'yMXo+UIywMzUaYaBpp6TfeUYVDn7REzQUnrMJGhQJGRXktO1ocNfxkvyjbrvG8Np3cmLbWhe' + '69zuNS0f27lZi6ohBJ4rtYhZpwkFjBZAqY7luI1dcqbUUYMnXFMXGuhRVqq52Wrad95xo888' + 'YxW0TkyJOiUY33iwPlKUYrJLStQ87rhUhMCe4LFx+Owud7WqHwv9E4LhqAkmORqHM3+q+omA' + 'YRH7qR0M6NW4VVdbu3Eildagx/NmtDzmtFmD8qVcSR4TAL3Rqt0HdqwCF8oNez9+/f35KAEV' + 'EeyfI631llqLynwYGPN8fi4/fiUbA3Bh6DbQzDIV7jwZ+exl0ilnQYNNIvhD/8/fhOlno1DG' + 'vy8vK8f3svvXPnTsvWN/jpU4bFY9w2d2Qa3pqRgvw84+IRlj28vwWWTuqIFx7LQFF+tKH33f' + 'KdSgx8shRWoBX6Tl14BbQa83UWLd6DY87olvN4HHhfhBi/G5WGr/db7z24DkZj9vB0/H1Foq' + 'EfYeC40+hyt7EfORC+jYkkIFXduHEjzMJY74nnToqqpH0H9Glrn2+HjYvb2Do4wLjvTy8m45' + '0X2kluQQtnmAePTSuRzmahBcqNiSQgGw8r3Pfjs0jNrNYswxtb+3x77F6nmzJjmp3vJuDdOW' + '11LTFDDFz0frQcZqHByZpJAlqpvrS+AaPLdMtteTURe9bHIdTseCdB6oXocf+jZyxZoZ+AVu' + 'ZzGawy1tKCDn/Lq/o3ZRe81mEx5KVFh5tqcHc/80G+bHROmqOV1rfvE9rWR7+36tn2IRu3U7' + '2m8K9vz26re807e5vvrlIz+kHnoUOHYJb45DrcdI92i5a3NQ4nvrZvZMQohYei8aGOy7hnQA' + 'WiWzbALKzG4VYakNYJ9XCztxAgkj1f5sTf3rwBRnGGcSBA2y81iPutdxuz5h1rb8BtvSoRm1' + 'Sv+nq9sNBk0UspzDf3A9MKw61U36KCKEzKuhl2MPjpUmnYy6kjIG/6fdEgbV7WBnrQ8n/Vrw' + 'tCBUdonCdOnMClJjrGg4ef0heP0EIHjT+ta6nNAbVz2jHuZxUKpxeE+5UX4YezGccXA8Ha67' + 'xW5npDhfNKS0u7nJAsENexxHUBLXJdQIs4NdL/r6MDR6evW6AFaHyXhQXWuRHUkD4nyz3mu7' + 'C2wcwtJxftXWpqq5zY+nqSoSwCltn8hyTvjN2lhNqFMw/OLOm3VkuzcMzlU+Ps6XAs+HkqSl' + 'yRup/FYf7NS5N0exiStTYYE6/TzTWYsrwQLWPVBxPctU7MfTzN9GgRa294x44dYZZvj0cgTG' + 'gTKOclScwNf/+Rcvz5pWQYgQMF9eYy3VQZkF2G+DaBP5A/RkWZ+XlirsFz8h+zVJ4Nw/5/tN' + 'Is008MuHa82fgkuF10yqxB1sCzmmUK9sXg/BnznWrWXiebYisNyYEPtQUMF9b5+IyTluYfgo' + 'XzNKNzT0hnLXasTYBZqJnUiPAPnXW1muRtjcWpogjNMl2zLkjDVc3FoAmlyLhde5bwxOEoHM' + 'rTnjfRQq65koBMIDQLW8UP/qg/6vyj8aXCH+pPvFulz/AzGDj2tG65bW8mGm6M1JCNThLQwD' + 'J5TbavSdBNr+Co/4jflki5gqGi74gyDJ9Zoju2yO/6yRbz90v8LJB+0EpjwtZz3fy2ur9oo2' + '/6BkOnnLI1WzUi2oNhOacwfEYJnAayI1bltrdkfUy0lA3O25UbOXIkrEB/8tflibrlaInMT5' + 'n+tgup3aphFc4K/nqVCw+JauswoAmr7tEDxpKRAuHbZvilt/Xs2dNSVn6ksIRfLi8SN1VpqD' + 'y7Y/t3tca2NxJx5DPjiUYU6tb7KnHf4HJkPVRhSDjy5T9jsGBsqpRLYxa2vtu3b///d/EVkO' + 'm9PKwQJwJXpvYmB5kRyqTK/E9b4gtxlJ2MkHovtdWNN8rqnpxWK/UsUm+pxu33n0f7G4OLLf' + 'l580el4ayFBEuiTDb3E5ATTEyytDrMn5xai2feCj5LVQnTex0ODyJbWPOX7DG9/LNUfFuk36' + 'XUQm0NHX2gt1lkY2LVF5JThZF4cUyaoT6wFkzttSreN0eibBGP0PIUnQ4XLTAXPpn6tEIuLr' + 'Rjti4uqR7jXipGZpYxn2g3n+1uhRW/ScG5MutzoErf18QYWiDXBftZocb+KkFxtjQMC8d2wq' + 'YlbYTjbr6xW3e1U7rmK7/oaIt4REUTF7ePklcqcWX6Qt9XuT7Yzm1MmIA54tmT6HKntfRaPZ' + 'jp/5cFyTheYF9CE8MW7qmgYIxXQCJE3AWf/a9Ctdjwjt7n8fCToq96m71C5u+NwQfrErB3m7' + 'UehhIGzOvXr7/Y9zkcGXzgK+ADaNzOyQuTqefOnYtQkHFbNXoNK0f375033VpzzcenW+Pwr+' + '2tdBMqzaKyRo5kyCvX/SJKISL94WTf57jgOpR7w3CY68bbq9D5rip0EUdcUp0Ig9xonVDn87' + '0cuFDhxEnRqrOFZ8x4VATeX/2nRUgTNwPsKcPtoXLlP5QCMgOc+yWky8+xVaY/tJKIaYbwSB' + 'HCNLmxmiqH1N9uTjhYynXCCrxVV+b6phMqBLPpxEWxRVOBHAMfeFWica+z1LbHUw3OmrbHnG' + 'Xwg68adMRT3RZPb+unleI02ve5a3Drp8VCvCmB3qfZPRBvzBanVWoX6tatG64W5D1iVMRbpS' + 'Ue0e1fBRKRwaXKdiBXHLwHDfGy9d4fzAaMF8WIhCvcGSteaZmu7GFMmDAhkBEYEo8EuwVoLh' + 'R7LBD6Q4po5zYpoYSzkLNnzw7UIPoFynqY2YSWPoEiXo2b0OYEu0G37dsgE4pIMS8XIVldOV' + 'DMI8D0rQvNtQ2yL4GqNKF4XEZ2KS3SgHBkMRp37jU1YW3HVvDp0LBGQt/Iw44tVfSgUJzjpm' + 'g6GRe70ejvdsMCdv5nBNlotMb0QGXk/4yAYtqxxYoMfRsHPSmcgSwLUxtuB8L2IQ4jQspQUC' + '7Yo5hcd0ZB5fXLyrBIbjF55tG1a1fpHERaCoVjdV1ktrqq8T+Ol4X0Bf+NFwAAAABJRU5Erk' + 'Jggg=='; + +typedef _IconPlaceholder = CircleAvatar; diff --git a/catalyst_voices/test/widgets/rich_text/voices_rich_text_test.dart b/catalyst_voices/test/widgets/rich_text/voices_rich_text_test.dart new file mode 100644 index 00000000000..48f63dd2fd6 --- /dev/null +++ b/catalyst_voices/test/widgets/rich_text/voices_rich_text_test.dart @@ -0,0 +1,32 @@ +import 'package:catalyst_voices/widgets/rich_text/voices_rich_text.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:catalyst_voices_localization/generated/catalyst_voices_localizations.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_localized_locales/flutter_localized_locales.dart'; +import 'package:flutter_quill/flutter_quill.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../helpers/helpers.dart'; + +void main() { + group(VoicesRichText, () { + testWidgets('renders correctly', (tester) async { + // Given + final widget = MaterialApp( + theme: ThemeBuilder.buildTheme(BrandKey.catalyst), + localizationsDelegates: const [ + ...VoicesLocalizations.localizationsDelegates, + LocaleNamesLocalizationsDelegate(), + ], + home: VoicesRichText(), + ); + + // When + await tester.pumpApp(widget); + await tester.pumpAndSettle(); + + // Then + expect(find.byType(QuillEditor), findsOneWidget); + }); + }); +} diff --git a/catalyst_voices/test/widgets/seed_phrase/seed_phrases_sequencer_test.dart b/catalyst_voices/test/widgets/seed_phrase/seed_phrases_sequencer_test.dart new file mode 100644 index 00000000000..d8461f9e141 --- /dev/null +++ b/catalyst_voices/test/widgets/seed_phrase/seed_phrases_sequencer_test.dart @@ -0,0 +1,84 @@ +import 'package:catalyst_voices/widgets/seed_phrase/seed_phrases_sequencer.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../helpers/helpers.dart'; + +void main() { + group('SeedPhrasesSequencer', () { + const words = ['real', 'mission', 'secure', 'renew', 'key', 'audit']; + + testWidgets( + 'clicking word in picker triggers on change callback', + (tester) async { + // Given + final selectedWords = {}; + final word = words[0]; + final expectedWords = {word}; + + final sequencer = SeedPhrasesSequencer( + words: words, + onChanged: (value) { + selectedWords + ..clear() + ..addAll(value); + }, + ); + final wordPickerKey = ValueKey('PickerSeedPhrase${word}CellKey'); + + // When + await tester.pumpApp(sequencer); + await tester.pumpAndSettle(); + await tester.tap(find.byKey(wordPickerKey)); + + // Then + expect(selectedWords, expectedWords); + }, + ); + + testWidgets( + 'clicking last word in completer removes word from list', + (tester) async { + // Given + final selectedWords = {}; + final expectedWords = {words[0]}; + + final sequencer = SeedPhrasesSequencer( + words: words, + onChanged: (value) { + selectedWords + ..clear() + ..addAll(value); + }, + ); + + final pickerKeys = >[ + ValueKey('PickerSeedPhrase${words[0]}CellKey'), + ValueKey('PickerSeedPhrase${words[1]}CellKey'), + ]; + final completerKeys = >[ + const ValueKey('CompleterSeedPhrase${1}CellKey'), + ]; + + // When + await tester.pumpApp(sequencer); + await tester.pumpAndSettle(); + + // Adds items to selectedWords + for (final key in pickerKeys) { + await tester.tap(find.byKey(key)); + } + + await tester.pumpAndSettle(); + + // Removes from selectedWords + for (final key in completerKeys) { + await tester.tap(find.byKey(key)); + } + + // Then + expect(selectedWords, expectedWords); + }, + ); + }); +} diff --git a/catalyst_voices/test/widgets/text_field/voices_text_field_test.dart b/catalyst_voices/test/widgets/text_field/voices_text_field_test.dart new file mode 100644 index 00000000000..b968fe0a13e --- /dev/null +++ b/catalyst_voices/test/widgets/text_field/voices_text_field_test.dart @@ -0,0 +1,248 @@ +import 'package:catalyst_voices/widgets/text_field/voices_text_field.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('VoicesTextField Widget Tests', () { + testWidgets('renders correctly with default parameters', (tester) async { + await tester.pumpWidget( + const _MaterialApp( + child: VoicesTextField(), + ), + ); + + // Verify the TextField is rendered + expect(find.byType(TextFormField), findsOneWidget); + }); + + testWidgets('displays label text when provided', (tester) async { + const labelText = 'Test Label'; + + await tester.pumpWidget( + const _MaterialApp( + child: VoicesTextField( + decoration: VoicesTextFieldDecoration(labelText: labelText), + ), + ), + ); + + // Verify the label text is rendered + expect(find.text(labelText), findsOneWidget); + }); + + testWidgets('handles text input and updates controller', (tester) async { + final controller = TextEditingController(); + + await tester.pumpWidget( + _MaterialApp( + child: VoicesTextField( + controller: controller, + ), + ), + ); + + // Enter text into the TextField + await tester.enterText(find.byType(TextFormField), 'Hello World'); + + // Verify that the controller's text is updated + expect(controller.text, 'Hello World'); + }); + + testWidgets('applies custom decorations correctly', (tester) async { + const hintText = 'Enter your text here'; + const errorText = 'Error message'; + + await tester.pumpWidget( + const _MaterialApp( + child: VoicesTextField( + decoration: VoicesTextFieldDecoration( + hintText: hintText, + errorText: errorText, + ), + ), + ), + ); + + // Verify that hint, helper, and error texts are applied + expect(find.text(hintText), findsOneWidget); + expect(find.text(errorText), findsOneWidget); + }); + + testWidgets('validates input and displays error correctly', (tester) async { + const errorText = 'Invalid input'; + + await tester.pumpWidget( + _MaterialApp( + child: VoicesTextField( + validator: (value) => const VoicesTextFieldValidationResult( + status: VoicesTextFieldStatus.error, + errorMessage: errorText, + ), + ), + ), + ); + + // Enter invalid text into the TextField + await tester.enterText(find.byType(TextFormField), 'Invalid'); + + // Trigger validation by losing focus + await tester.tap(find.byType(TextFormField)); + await tester.pump(); + + // Verify that the error message is displayed + expect(find.text(errorText), findsOneWidget); + }); + + testWidgets('displays correct suffix icon based on validation result', + (tester) async { + await tester.pumpWidget( + _MaterialApp( + child: VoicesTextField( + validator: VoicesTextFieldValidationResult.success(), + ), + ), + ); + + // Enter valid text into the TextField + await tester.enterText(find.byType(TextFormField), 'Valid'); + await tester.pump(); + + // Verify that the success icon is displayed + expect(find.byIcon(CatalystVoicesIcons.check_circle), findsOneWidget); + }); + + testWidgets('renders correctly when disabled', (tester) async { + await tester.pumpWidget( + const _MaterialApp( + child: VoicesTextField( + enabled: false, + decoration: + VoicesTextFieldDecoration(labelText: 'Disabled TextField'), + ), + ), + ); + + // Verify that the TextField is rendered as disabled + final textField = + tester.widget(find.byType(TextFormField)); + expect(textField.enabled, isFalse); + }); + }); + + group('VoicesTextField Validator Logic Tests', () { + testWidgets('displays error when validation fails', (tester) async { + const errorMessage = 'This field is required'; + + // Define a validator that returns an error if the input is empty + VoicesTextFieldValidationResult validator(String value) { + if (value.isEmpty) { + return const VoicesTextFieldValidationResult( + status: VoicesTextFieldStatus.error, + errorMessage: errorMessage, + ); + } + return const VoicesTextFieldValidationResult( + status: VoicesTextFieldStatus.none, + ); + } + + await tester.pumpWidget( + _MaterialApp( + child: VoicesTextField( + validator: validator, + ), + ), + ); + + // Enter empty text into the TextField to trigger validation + await tester.enterText(find.byType(TextFormField), ''); + await tester.pump(); + + // Verify that the error message is displayed + expect(find.text(errorMessage), findsOneWidget); + + // Enter valid text into the TextField + await tester.enterText(find.byType(TextFormField), 'Valid input'); + await tester.pump(); + + // Verify that the error message is no longer displayed + expect(find.text(errorMessage), findsNothing); + }); + + testWidgets('displays success when validation passes', (tester) async { + // Define a validator that always returns success + VoicesTextFieldValidationResult validator(value) { + return const VoicesTextFieldValidationResult( + status: VoicesTextFieldStatus.success, + ); + } + + await tester.pumpWidget( + _MaterialApp( + child: VoicesTextField( + validator: validator, + ), + ), + ); + + // Enter text into the TextField to trigger validation + await tester.enterText(find.byType(TextFormField), 'Valid input'); + await tester.pump(); + + // Verify that the success icon is displayed + expect(find.byIcon(CatalystVoicesIcons.check_circle), findsOneWidget); + }); + + testWidgets('displays warning when validation returns warning', + (tester) async { + const warningMessage = 'This is a warning'; + + // Define a validator that returns a warning for specific input + VoicesTextFieldValidationResult validator(String value) { + if (value == 'warning') { + return const VoicesTextFieldValidationResult( + status: VoicesTextFieldStatus.warning, + errorMessage: warningMessage, + ); + } + return const VoicesTextFieldValidationResult( + status: VoicesTextFieldStatus.none, + ); + } + + await tester.pumpWidget( + _MaterialApp( + child: VoicesTextField( + validator: validator, + ), + ), + ); + + // Enter the text that triggers a warning + await tester.enterText(find.byType(TextFormField), 'warning'); + await tester.pump(); + + // Verify that the warning message is displayed + expect(find.text(warningMessage), findsOneWidget); + + // Verify that the warning icon is displayed + expect(find.byIcon(Icons.warning_outlined), findsOneWidget); + }); + }); +} + +class _MaterialApp extends StatelessWidget { + final Widget child; + + const _MaterialApp({required this.child}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + theme: ThemeBuilder.buildTheme(BrandKey.catalyst), + home: Scaffold(body: child), + ); + } +} diff --git a/catalyst_voices/test/widgets/tooltips/voices_plain_tooltip_test.dart b/catalyst_voices/test/widgets/tooltips/voices_plain_tooltip_test.dart new file mode 100644 index 00000000000..c39c1dc61ca --- /dev/null +++ b/catalyst_voices/test/widgets/tooltips/voices_plain_tooltip_test.dart @@ -0,0 +1,82 @@ +import 'package:catalyst_voices/widgets/tooltips/voices_plain_tooltip.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../helpers/helpers.dart'; + +void main() { + group('VoicesPlainTooltip', () { + testWidgets('displays the correct message', (tester) async { + // Given + const message = 'This is a tooltip message.'; + const child = Icon(Icons.info); + + const widget = VoicesPlainTooltip( + message: message, + child: child, + ); + + // When + await tester.pumpApp(widget); + await tester.pumpAndSettle(); + + final gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); + await gesture.addPointer(location: Offset.zero); + addTearDown(gesture.removePointer); + await tester.pump(); + await gesture.moveTo(tester.getCenter(find.byType(Icon))); + await tester.pumpAndSettle(); + + // Then + expect(find.text(message), findsOneWidget); + }); + + testWidgets('is not displayed without hover', (tester) async { + // Given + const message = 'This is a tooltip message.'; + const child = Icon(Icons.info); + + const widget = VoicesPlainTooltip( + message: message, + child: child, + ); + + // When + await tester.pumpApp(widget); + await tester.pumpAndSettle(); + + // Then + expect(find.text(message), findsNothing); + }); + + testWidgets('constrains the text width', (tester) async { + // Given + const message = 'This is a very long tooltip message.'; + const child = Icon(Icons.info); + + const widget = VoicesPlainTooltip( + message: message, + child: child, + ); + + // When + await tester.pumpApp(widget); + await tester.pumpAndSettle(); + + final gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); + await gesture.addPointer(location: Offset.zero); + addTearDown(gesture.removePointer); + await tester.pump(); + await gesture.moveTo(tester.getCenter(find.byType(Icon))); + await tester.pumpAndSettle(); + + // Then + final size = tester.getSize( + find.byKey(const ValueKey('VoicesPlainTooltipContentKey')), + ); + + expect(size.width, 200.0); + }); + }); +} diff --git a/catalyst_voices/test/widgets/tooltips/voices_rich_tooltip_test.dart b/catalyst_voices/test/widgets/tooltips/voices_rich_tooltip_test.dart new file mode 100644 index 00000000000..ed1931152fb --- /dev/null +++ b/catalyst_voices/test/widgets/tooltips/voices_rich_tooltip_test.dart @@ -0,0 +1,146 @@ +import 'package:catalyst_voices/widgets/buttons/voices_text_button.dart'; +import 'package:catalyst_voices/widgets/tooltips/voices_rich_tooltip.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../helpers/helpers.dart'; + +void main() { + group('VoicesRichTooltip', () { + testWidgets('renders title and message', (tester) async { + // Given + const title = 'Tooltip Title'; + const message = 'This is a tooltip message.'; + + const widget = VoicesRichTooltip( + title: title, + message: message, + child: Icon(Icons.info), + ); + + // When + await tester.pumpApp(widget); + await tester.pumpAndSettle(); + + final gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); + await gesture.addPointer(location: Offset.zero); + addTearDown(gesture.removePointer); + await tester.pump(); + await gesture.moveTo(tester.getCenter(find.byType(Icon))); + await tester.pumpAndSettle(); + + // Then + // Find the Text widgets for title and message + final titleFinder = find.text(title); + final messageFinder = find.text(message); + + // Expect them to be present in the widget tree + expect(tester.widget(titleFinder), isNotNull); + expect(tester.widget(messageFinder), isNotNull); + }); + }); + + testWidgets('renders actions', (tester) async { + // Given + const title = 'Tooltip Title'; + const message = 'This is a tooltip message.'; + final actions = [ + VoicesRichTooltipActionData( + name: 'Edit', + onTap: () => debugPrint('Edit tapped'), + ), + VoicesRichTooltipActionData( + name: 'Delete', + onTap: () => debugPrint('Delete tapped'), + ), + ]; + + final widget = VoicesRichTooltip( + title: title, + message: message, + actions: actions, + child: const Icon(Icons.info), + ); + + // When + await tester.pumpApp(widget); + await tester.pumpAndSettle(); + + final gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); + await gesture.addPointer(location: Offset.zero); + addTearDown(gesture.removePointer); + await tester.pump(); + await gesture.moveTo(tester.getCenter(find.byType(Icon))); + await tester.pumpAndSettle(); + + // Then + final actionButtons = find + .byWidgetPredicate((widget) => widget is VoicesTextButton) + .evaluate(); + + expect(actionButtons.length, actions.length); + + // Optionally, verify the text content of each button + for (var i = 0; i < actions.length; i++) { + expect(find.text(actions[i].name), findsOne); + } + }); + + testWidgets('dismisses on tap without actions', (tester) async { + // Given + const title = 'Tooltip Title'; + const message = 'This is a tooltip message.'; + + final tapped = ValueNotifier(false); + + final widget = Padding( + padding: const EdgeInsets.all(8), + child: VoicesRichTooltip( + title: title, + message: message, + child: TextButton( + onPressed: () => tapped.value = true, + child: const Text('Trigger'), + ), + ), + ); + + // When + await tester.pumpApp(widget); + await tester.pumpAndSettle(); + + // Tap the trigger button + final gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); + await gesture.addPointer(location: Offset.zero); + addTearDown(gesture.removePointer); + await tester.pump(); + await gesture.moveTo(tester.getCenter(find.byType(TextButton))); + await tester.pumpAndSettle(); + + await tester.tap(find.text('Trigger')); + await tester.pump(); + + expect( + find.byKey(const ValueKey('VoicesRichTooltipContentKey')), + findsOne, + ); + + // Simulate a tap on the tooltip itself + await tester.tapAt(tester.getCenter(find.byType(Tooltip))); + await tester.pump(); + + await gesture.moveTo(Offset.zero); + await tester.pumpAndSettle(); + + // Then + // Expect the Tooltip to be dismissed (no longer rendered) + expect( + find.byKey(const ValueKey('VoicesRichTooltipContentKey')), + findsNothing, + ); + + // Expect the trigger button to be tapped + expect(tapped.value, true); + }); +} diff --git a/catalyst_voices/test_driver/Earthfile b/catalyst_voices/test_driver/Earthfile index 6cea7c8fa60..092d407d314 100644 --- a/catalyst_voices/test_driver/Earthfile +++ b/catalyst_voices/test_driver/Earthfile @@ -1,45 +1,43 @@ -VERSION 0.8 +# TODO(minikin): Temporary disabled until we have useful integration tests. -IMPORT ../ AS catalyst-voices +# IMPORT ../ AS catalyst-voices -# TODO(kukko3) - fix and enable -disabled-integration-test-web: - FROM catalyst-voices+build-web - ARG TARGETARCH - ARG browser - LET driver_port = 4444 +# integration-test-web: +# FROM catalyst-voices+build-web +# ARG TARGETARCH +# ARG browser +# LET driver_port = 4444 - IF [ $browser = "chrome" ] - LET driver = "chromedriver" - END +# IF [ $browser = "chrome" ] +# LET driver = "chromedriver" +# END - IF [ $browser = "firefox" ] - LET driver = "geckodriver" - END - # Commenting out Edge tests as they are failing due to: - # https://github.com/flutter/flutter/issues/76213 - # https://github.com/flutter/flutter/issues/142021 - #IF [ $browser = "edge" && $TARGETARCH = "amd64" ]] - # LET driver = "msedgedriver" - #END - RUN ($driver --port=$driver_port > $driver.log &) && \ - flutter drive --driver=test_driver/integration_tests.dart \ - --target=integration_test/main.dart \ - --flavor development -d web-server --profile \ - --browser-name=$browser --driver-port=$driver_port || echo fail > fail - # Using WAIT instead of TRY because TRY/CATCH/FINALLY does not (currently) support expanding args for SAVE ARTIFACT paths - WAIT - SAVE ARTIFACT $driver.log AS LOCAL $driver.log - END - IF [ -f fail ] - RUN echo ""$browser" integration test failed" && \ - echo "Printing "$driver" logs..." && \ - cat $driver.log && \ - exit 1 - END +# IF [ $browser = "firefox" ] +# LET driver = "geckodriver" +# END +# # Commenting out Edge tests as they are failing due to: +# # https://github.com/flutter/flutter/issues/76213 +# # https://github.com/flutter/flutter/issues/142021 +# #IF [ $browser = "edge" && $TARGETARCH = "amd64" ]] +# # LET driver = "msedgedriver" +# #END +# RUN ($driver --port=$driver_port > $driver.log &) && \ +# flutter drive --driver=test_driver/integration_tests.dart \ +# --target=integration_test/main.dart \ +# --flavor development -d web-server --profile \ +# --browser-name=$browser --driver-port=$driver_port || echo fail > fail +# # Using WAIT instead of TRY because TRY/CATCH/FINALLY does not (currently) support expanding args for SAVE ARTIFACT paths +# WAIT +# SAVE ARTIFACT $driver.log AS LOCAL $driver.log +# END +# IF [ -f fail ] +# RUN echo ""$browser" integration test failed" && \ +# echo "Printing "$driver" logs..." && \ +# cat $driver.log && \ +# exit 1 +# END -# TODO(kukko3) - fix and enable -disabled-test-web-all: - BUILD +disabled-integration-test-web \ - --browser=chrome \ - --browser=firefox +# test-web-all: +# BUILD +integration-test-web \ +# --browser=chrome \ +# --browser=firefox diff --git a/catalyst_voices/uikit_example/Earthfile b/catalyst_voices/uikit_example/Earthfile index 6a31dab57b9..66c8fdeb305 100644 --- a/catalyst_voices/uikit_example/Earthfile +++ b/catalyst_voices/uikit_example/Earthfile @@ -1,7 +1,7 @@ VERSION 0.8 IMPORT ../ AS catalyst-voices -IMPORT github.com/input-output-hk/catalyst-ci/earthly/flutter:v3.1.21 AS flutter-ci +IMPORT github.com/input-output-hk/catalyst-ci/earthly/flutter:v3.1.26 AS flutter-ci # local-build-web - build web version of UIKit example. # Prefixed by "local" to make sure it's not auto triggered, the target was diff --git a/catalyst_voices/uikit_example/analysis_options.yaml b/catalyst_voices/uikit_example/analysis_options.yaml index d5015346bf1..eb691624826 100644 --- a/catalyst_voices/uikit_example/analysis_options.yaml +++ b/catalyst_voices/uikit_example/analysis_options.yaml @@ -1,4 +1,4 @@ -include: package:catalyst_analysis/analysis_options.1.0.0.yaml +include: package:catalyst_analysis/analysis_options.yaml analyzer: exclude: [ diff --git a/catalyst_voices/uikit_example/assets/images/robot_avatar.png b/catalyst_voices/uikit_example/assets/images/robot_avatar.png new file mode 100644 index 00000000000..da16d1849ae Binary files /dev/null and b/catalyst_voices/uikit_example/assets/images/robot_avatar.png differ diff --git a/catalyst_voices/uikit_example/lib/examples/treasury_space/campaign_builder_panel.dart b/catalyst_voices/uikit_example/lib/examples/treasury_space/campaign_builder_panel.dart new file mode 100644 index 00000000000..fbaf1c9aca6 --- /dev/null +++ b/catalyst_voices/uikit_example/lib/examples/treasury_space/campaign_builder_panel.dart @@ -0,0 +1,49 @@ +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:flutter/material.dart'; + +class CampaignBuilderPanel extends StatelessWidget { + final VoicesNodeMenuController setupCampaignController; + final List setupCampaignItems; + + const CampaignBuilderPanel({ + required this.setupCampaignController, + required this.setupCampaignItems, + }); + + @override + Widget build(BuildContext context) { + return SpaceSidePanel( + isLeft: true, + name: 'Campaign builder', + onCollapseTap: () {}, + tabs: [ + SpaceSidePanelTab( + name: 'Segments', + body: Column( + children: [ + VoicesNodeMenu( + name: 'Setup Campaign', + controller: setupCampaignController, + items: setupCampaignItems, + onSelectionChanged: _updateSetupMenuSelection, + onExpandChanged: _updateSetupMenuExpand, + ), + ], + ), + ), + ], + ); + } + + void _updateSetupMenuSelection(int? value) { + if (value == null) { + return; + } + + setupCampaignController.selected = value; + } + + void _updateSetupMenuExpand(bool value) { + setupCampaignController.isExpanded = value; + } +} diff --git a/catalyst_voices/uikit_example/lib/examples/treasury_space/campaign_comments_panel.dart b/catalyst_voices/uikit_example/lib/examples/treasury_space/campaign_comments_panel.dart new file mode 100644 index 00000000000..e5663f465d2 --- /dev/null +++ b/catalyst_voices/uikit_example/lib/examples/treasury_space/campaign_comments_panel.dart @@ -0,0 +1,21 @@ +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:flutter/material.dart'; + +class CampaignCommentsPanel extends StatelessWidget { + const CampaignCommentsPanel(); + + @override + Widget build(BuildContext context) { + return SpaceSidePanel( + isLeft: false, + name: 'Campaign comments', + onCollapseTap: () {}, + tabs: [ + SpaceSidePanelTab( + name: 'Comments', + body: Offstage(), + ), + ], + ); + } +} diff --git a/catalyst_voices/uikit_example/lib/examples/treasury_space/campaign_details.dart b/catalyst_voices/uikit_example/lib/examples/treasury_space/campaign_details.dart new file mode 100644 index 00000000000..80b5d6312dc --- /dev/null +++ b/catalyst_voices/uikit_example/lib/examples/treasury_space/campaign_details.dart @@ -0,0 +1,101 @@ +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; +import 'package:flutter/material.dart'; +import 'package:uikit_example/examples/treasury_space/voices_treasury_space.dart'; + +class CampaignDetails extends StatelessWidget { + final VoicesNodeMenuController campaignSetupController; + final List steps; + + const CampaignDetails({ + super.key, + required this.campaignSetupController, + required this.steps, + }); + + @override + Widget build(BuildContext context) { + return ListView( + padding: const EdgeInsets.only(top: 10), + children: [ + _ListenableCampaignSetupDetails( + key: ValueKey('CampaignSetupDetailsKey'), + controller: campaignSetupController, + steps: steps, + ), + ], + ); + } +} + +class _ListenableCampaignSetupDetails extends StatelessWidget { + final VoicesNodeMenuController controller; + final List steps; + + const _ListenableCampaignSetupDetails({ + super.key, + required this.controller, + required this.steps, + }); + + @override + Widget build(BuildContext context) { + return ValueListenableBuilder( + valueListenable: controller, + builder: (context, value, child) { + return _CampaignSetupDetails( + campaignSetupSteps: steps, + selected: controller.value.selectedItemId, + isExpanded: controller.value.isExpanded, + onChevronTap: () { + controller.isExpanded = !controller.value.isExpanded; + }, + ); + }, + ); + } +} + +class _CampaignSetupDetails extends StatelessWidget { + final List campaignSetupSteps; + final int? selected; + final bool isExpanded; + final VoidCallback? onChevronTap; + + const _CampaignSetupDetails({ + required this.campaignSetupSteps, + this.selected, + this.isExpanded = false, + this.onChevronTap, + }); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + SegmentHeader( + leading: ChevronExpandButton( + onTap: onChevronTap, + isExpanded: isExpanded, + ), + name: 'Setup Campaign', + isHighlighted: isExpanded, + ), + if (isExpanded) + ...campaignSetupSteps.map( + (step) { + return WorkspaceTileContainer( + key: ValueKey('WorkspaceStep${step.id}TileKey'), + isSelected: step.id == selected, + name: step.name, + headerActions: [ + VoicesTextButton(child: Text('Edit')), + ], + content: Text(step.desc), + ); + }, + ) + ].separatedBy(SizedBox(height: 12)).toList(), + ); + } +} diff --git a/catalyst_voices/uikit_example/lib/examples/treasury_space/voices_treasury_drawer.dart b/catalyst_voices/uikit_example/lib/examples/treasury_space/voices_treasury_drawer.dart new file mode 100644 index 00000000000..d0fea242df7 --- /dev/null +++ b/catalyst_voices/uikit_example/lib/examples/treasury_space/voices_treasury_drawer.dart @@ -0,0 +1,116 @@ +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:flutter/material.dart'; + +class VoicesTreasuryDrawer extends StatelessWidget { + const VoicesTreasuryDrawer({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return VoicesDrawer( + children: [ + _Treasury(), + ], + bottom: VoicesDrawerSpaceChooser( + currentSpace: Space.treasury, + onChanged: (value) {}, + ), + ); + } +} + +class _Treasury extends StatefulWidget { + const _Treasury(); + + @override + State<_Treasury> createState() => _TreasuryState(); +} + +class _TreasuryState extends State<_Treasury> { + bool _isExpanded = true; + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + _TreasuryHeader( + onTap: () { + setState(() { + _isExpanded = !_isExpanded; + }); + }, + isExpanded: _isExpanded, + ), + if (_isExpanded) ...[ + SectionHeader( + leading: SizedBox(width: 12), + title: Text('Individual private campaigns'), + ), + VoicesDrawerNavItem( + name: 'Fund name 1', + status: ProposalStatus.ready, + ), + VoicesDrawerNavItem( + name: 'Campaign 1', + status: ProposalStatus.draft, + ), + VoicesDrawerNavItem( + name: 'What happens with a campaign title that is longer that', + status: ProposalStatus.draft, + ), + ], + ], + ); + } +} + +class _TreasuryHeader extends StatelessWidget { + final VoidCallback? onTap; + final bool isExpanded; + + const _TreasuryHeader({ + this.onTap, + this.isExpanded = false, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + return GestureDetector( + onTap: onTap, + child: Container( + padding: + EdgeInsets.symmetric(vertical: 14).add(EdgeInsets.only(left: 16)), + child: Row( + children: [ + VoicesAvatar( + icon: Icon(CatalystVoicesIcons.cash), + foregroundColor: theme.colors.iconsSuccess, + backgroundColor: theme.colors.successContainer, + ), + SizedBox(width: 12), + Expanded( + child: Text( + 'Treasury', + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: theme.textTheme.titleMedium + ?.copyWith(color: theme.colors.textPrimary), + ), + ), + ChevronExpandButton( + isExpanded: isExpanded, + onTap: onTap, + ), + ], + ), + ), + ); + } +} diff --git a/catalyst_voices/uikit_example/lib/examples/treasury_space/voices_treasury_space.dart b/catalyst_voices/uikit_example/lib/examples/treasury_space/voices_treasury_space.dart new file mode 100644 index 00000000000..14e8a5d7af0 --- /dev/null +++ b/catalyst_voices/uikit_example/lib/examples/treasury_space/voices_treasury_space.dart @@ -0,0 +1,99 @@ +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:flutter/material.dart'; +import 'package:uikit_example/examples/treasury_space/campaign_builder_panel.dart'; +import 'package:uikit_example/examples/treasury_space/campaign_comments_panel.dart'; +import 'package:uikit_example/examples/treasury_space/campaign_details.dart'; +import 'package:uikit_example/examples/treasury_space/voices_treasury_drawer.dart'; + +final class CampaignSetupStep extends VoicesNodeMenuItem { + final String desc; + + const CampaignSetupStep({ + required super.id, + required String name, + required this.desc, + }) : super(label: name); + + /// Just syntax sugar. Semantically it makes more sense to have `name`. + String get name => label; +} + +const _campaignSetupSteps = [ + CampaignSetupStep( + id: 0, + name: 'Campaign title', + desc: 'F14 / Promote Social Entrepreneurs and' + ' a longer title up-to 60 characters', + ), + CampaignSetupStep( + id: 1, + name: 'Other topic 1', + desc: 'Other topic 1', + ), + CampaignSetupStep( + id: 2, + name: 'Other topic 2', + desc: 'Other topic 2', + ), + CampaignSetupStep( + id: 3, + name: 'Other topic 3', + desc: 'Other topic 3', + ), +]; + +class VoicesTreasurySpace extends StatefulWidget { + static const String route = '/treasury-space'; + + const VoicesTreasurySpace({ + super.key, + }); + + @override + State createState() => _VoicesTreasurySpaceState(); +} + +class _VoicesTreasurySpaceState extends State { + final _setupCampaignController = VoicesNodeMenuController( + VoicesNodeMenuData( + selectedItemId: _campaignSetupSteps.first.id, + isExpanded: true, + ), + ); + + @override + void dispose() { + _setupCampaignController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Theme( + data: ThemeBuilder.buildTheme(BrandKey.catalyst), + child: Builder( + builder: (context) { + return Scaffold( + appBar: VoicesAppBar( + backgroundColor: + Theme.of(context).colors.onSurfaceNeutralOpaqueLv0, + ), + drawer: VoicesTreasuryDrawer(), + body: SpaceScaffold( + left: CampaignBuilderPanel( + setupCampaignController: _setupCampaignController, + setupCampaignItems: _campaignSetupSteps, + ), + right: CampaignCommentsPanel(), + child: CampaignDetails( + campaignSetupController: _setupCampaignController, + steps: _campaignSetupSteps, + ), + ), + ); + }, + ), + ); + } +} diff --git a/catalyst_voices/uikit_example/lib/examples/voices_avatar_example.dart b/catalyst_voices/uikit_example/lib/examples/voices_avatar_example.dart new file mode 100644 index 00000000000..f0f70b64330 --- /dev/null +++ b/catalyst_voices/uikit_example/lib/examples/voices_avatar_example.dart @@ -0,0 +1,44 @@ +import 'package:catalyst_voices/widgets/avatars/voices_avatar.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:flutter/material.dart'; +import 'package:uikit_example/generated/assets.gen.dart'; + +class VoicesAvatarExample extends StatelessWidget { + static const String route = '/avatars-example'; + + const VoicesAvatarExample({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Voices Avatars')), + body: Padding( + padding: const EdgeInsets.all(32), + child: Wrap( + spacing: 16, + runSpacing: 16, + children: [ + const VoicesAvatar( + icon: Icon(CatalystVoicesIcons.check), + ), + VoicesAvatar( + icon: const Text('A'), + onTap: () {}, + ), + VoicesAvatar( + icon: const Icon(CatalystVoicesIcons.light_bulb), + foregroundColor: Theme.of(context).colors.iconsSecondary, + backgroundColor: + Theme.of(context).colors.iconsSecondary?.withOpacity(0.16), + ), + VoicesAvatar( + icon: Image.asset(UiKitAssets.images.robotAvatar.path), + padding: EdgeInsets.zero, + ), + ], + ), + ), + ); + } +} diff --git a/catalyst_voices/uikit_example/lib/examples/voices_badge_example.dart b/catalyst_voices/uikit_example/lib/examples/voices_badge_example.dart new file mode 100644 index 00000000000..3fd662d88d1 --- /dev/null +++ b/catalyst_voices/uikit_example/lib/examples/voices_badge_example.dart @@ -0,0 +1,42 @@ +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:flutter/material.dart'; + +class VoicesBadgeExample extends StatelessWidget { + static const String route = '/badge-example'; + + const VoicesBadgeExample({super.key}); + + @override + Widget build(BuildContext context) { + final colors = [ + Theme.of(context).colorScheme.error, + Theme.of(context).colors.success!, + ]; + + return Scaffold( + appBar: AppBar(title: const Text('Voices Badges')), + body: Padding( + padding: const EdgeInsets.all(32), + child: Wrap( + spacing: 16, + runSpacing: 16, + children: [ + for (final color in colors) ...[ + Badge( + label: const Text('3'), + backgroundColor: color, + ), + Badge( + label: const Text('32'), + backgroundColor: color, + ), + Badge( + backgroundColor: color, + ), + ], + ], + ), + ), + ); + } +} diff --git a/catalyst_voices/uikit_example/lib/examples/voices_buttons_example.dart b/catalyst_voices/uikit_example/lib/examples/voices_buttons_example.dart index 8beec56fbc6..49aabcb89ea 100644 --- a/catalyst_voices/uikit_example/lib/examples/voices_buttons_example.dart +++ b/catalyst_voices/uikit_example/lib/examples/voices_buttons_example.dart @@ -1,6 +1,6 @@ import 'package:catalyst_voices/widgets/widgets.dart'; import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; -import 'package:collection/collection.dart'; +import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; import 'package:flutter/material.dart'; enum _ButtonType { @@ -100,7 +100,7 @@ class _ButtonRow extends StatelessWidget { Widget build(BuildContext context) { return Row( children: _ButtonState.values - .map((state) { + .map((state) { return _buildButton( type, state, @@ -112,12 +112,7 @@ class _ButtonRow extends StatelessWidget { : null, ); }) - .expandIndexed( - (index, element) => [ - if (index != 0) const SizedBox(width: 16), - element, - ], - ) + .separatedBy(const SizedBox(width: 16)) .toList(), ); } diff --git a/catalyst_voices/uikit_example/lib/examples/voices_chip_example.dart b/catalyst_voices/uikit_example/lib/examples/voices_chip_example.dart index 0daffecc6e9..8b43755c2cb 100644 --- a/catalyst_voices/uikit_example/lib/examples/voices_chip_example.dart +++ b/catalyst_voices/uikit_example/lib/examples/voices_chip_example.dart @@ -25,6 +25,7 @@ class VoicesChipExample extends StatelessWidget { ]; return Scaffold( + appBar: AppBar(title: const Text('Voices Chips')), body: Padding( padding: const EdgeInsets.all(32), child: Wrap( diff --git a/catalyst_voices/uikit_example/lib/examples/voices_fab_example.dart b/catalyst_voices/uikit_example/lib/examples/voices_fab_example.dart new file mode 100644 index 00000000000..c2c109c6abe --- /dev/null +++ b/catalyst_voices/uikit_example/lib/examples/voices_fab_example.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; + +class VoicesFabExample extends StatelessWidget { + static const String route = '/fab-example'; + + const VoicesFabExample({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Voices Floating Action Button')), + body: Padding( + padding: const EdgeInsets.all(32), + child: Wrap( + spacing: 16, + runSpacing: 16, + children: [ + FloatingActionButton.small( + heroTag: 'small', + child: const Icon(Icons.edit), + onPressed: () {}, + ), + FloatingActionButton( + heroTag: 'regular', + child: const Icon(Icons.edit), + onPressed: () {}, + ), + FloatingActionButton.large( + heroTag: 'large', + child: const Icon(Icons.edit), + onPressed: () {}, + ), + FloatingActionButton.extended( + heroTag: 'extended', + icon: const Icon(Icons.edit), + label: const Text('Label'), + onPressed: () {}, + ), + ], + ), + ), + ); + } +} diff --git a/catalyst_voices/uikit_example/lib/examples/voices_headers_examples.dart b/catalyst_voices/uikit_example/lib/examples/voices_headers_examples.dart new file mode 100644 index 00000000000..301889ffb0f --- /dev/null +++ b/catalyst_voices/uikit_example/lib/examples/voices_headers_examples.dart @@ -0,0 +1,45 @@ +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; +import 'package:flutter/material.dart'; + +class VoicesHeadersExamples extends StatelessWidget { + static const String route = '/headers-example'; + + const VoicesHeadersExamples({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Headers')), + body: SizedBox( + width: 360, + child: Column( + children: [ + const SectionHeader(title: Text('Proposal stages')), + SectionHeader( + leading: VoicesIconButton( + child: const Icon(CatalystVoicesIcons.arrow_narrow_left), + onTap: () {}, + ), + title: const Text('Proposal stages'), + ), + SectionHeader( + title: const Text('Proposal stages'), + trailing: [ + VoicesIconButton( + child: const Icon(CatalystVoicesIcons.cake), + onTap: () {}, + ), + VoicesIconButton( + child: const Icon(CatalystVoicesIcons.arrow_narrow_right), + onTap: () {}, + ), + ], + ), + ].separatedBy(const SizedBox(height: 16)).toList(), + ), + ), + ); + } +} diff --git a/catalyst_voices/uikit_example/lib/examples/voices_indicators_example.dart b/catalyst_voices/uikit_example/lib/examples/voices_indicators_example.dart new file mode 100644 index 00000000000..217af014376 --- /dev/null +++ b/catalyst_voices/uikit_example/lib/examples/voices_indicators_example.dart @@ -0,0 +1,193 @@ +import 'package:catalyst_voices/widgets/common/affix_decorator.dart'; +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; +import 'package:flutter/material.dart'; + +class VoicesIndicatorsExample extends StatelessWidget { + static const String route = '/indicators-example'; + + const VoicesIndicatorsExample({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Voices Indicators')), + body: ListView( + padding: const EdgeInsets.symmetric(horizontal: 42, vertical: 24), + children: const [ + Text('Status Indicator'), + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: VoicesStatusIndicator( + status: AffixDecorator( + prefix: Icon(Icons.check), + child: Text('QR VERIFIED'), + ), + title: Text('Your QR code verified successfully'), + body: Text( + 'You can now use your QR-code 
to login into Catalyst.', + ), + type: VoicesStatusIndicatorType.success, + ), + ), + SizedBox(width: 50), + Expanded( + child: VoicesStatusIndicator( + status: AffixDecorator( + prefix: Icon(Icons.close), + child: Text('QR FAILED'), + ), + title: Text('Upload failed or QR code not recognized!'), + body: Text( + 'Are you sure your upload didn’t get interrupted or that ' + 'you provided 
a Catalyst QR code? ' + '

Please try again.', + ), + type: VoicesStatusIndicatorType.error, + ), + ), + ], + ), + Text('Process Stepper Indicator'), + _Steps(), + Text('Linear - Indeterminate'), + VoicesLinearProgressIndicator(), + VoicesLinearProgressIndicator(showTrack: false), + Text('Linear - Fixed'), + VoicesLinearProgressIndicator(value: 0.25), + VoicesLinearProgressIndicator(value: 0.25, showTrack: false), + Text('Circular - Indeterminate'), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + VoicesCircularProgressIndicator(), + SizedBox(width: 16), + VoicesCircularProgressIndicator(showTrack: false), + ], + ), + Text('Circular - Fixed'), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + VoicesCircularProgressIndicator(value: 0.75), + SizedBox(width: 16), + VoicesCircularProgressIndicator(value: 0.75, showTrack: false), + ], + ), + ].separatedByIndexed( + (index, value) { + return switch (value.runtimeType) { + Text => const SizedBox(height: 8), + VoicesLinearProgressIndicator => const SizedBox(height: 16), + _ => const SizedBox(height: 22), + }; + }, + ).toList(), + ), + ); + } +} + +enum _OnboardingStep { + one, + two, + three, + four, + five; + + ProcessProgressStep asStep() { + return ProcessProgressStep( + value: this, + name: stepName, + ); + } + + String get stepName { + return switch (this) { + _OnboardingStep.one => 'Step 1', + _OnboardingStep.two => 'Step 2', + _OnboardingStep.three => 'Step 3', + _OnboardingStep.four => 'Step 4', + _OnboardingStep.five => 'Step 5', + }; + } +} + +class _Steps extends StatefulWidget { + const _Steps(); + + @override + State<_Steps> createState() => _StepsState(); +} + +class _StepsState extends State<_Steps> { + final completedSteps = <_OnboardingStep>{}; + _OnboardingStep? currentStep = _OnboardingStep.one; + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + ProcessProgressIndicator( + steps: _OnboardingStep.values.map((e) => e.asStep()).toList(), + completed: completedSteps, + current: currentStep, + ), + const SizedBox(height: 12), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + VoicesFilledButton( + onTap: completedSteps.isEmpty + ? null + : () => setState(_goToPreviousStep), + child: const Text('Previous'), + ), + const SizedBox(width: 16), + VoicesFilledButton( + onTap: completedSteps.containsAll(_OnboardingStep.values) + ? null + : () => setState(_goToNextStep), + child: const Text('Next'), + ), + ], + ), + ], + ); + } + + void _goToNextStep() { + final currentStep = this.currentStep; + if (currentStep != null) { + completedSteps.add(currentStep); + + final index = _OnboardingStep.values.indexOf(currentStep); + if (index < _OnboardingStep.values.length - 1) { + this.currentStep = _OnboardingStep.values[index + 1]; + } + } else { + this.currentStep = _OnboardingStep.values.first; + } + } + + void _goToPreviousStep() { + final currentStep = this.currentStep; + if (currentStep != null) { + completedSteps.remove(currentStep); + + final index = _OnboardingStep.values.indexOf(currentStep); + if (index > 0) { + final newStep = _OnboardingStep.values[index - 1]; + + completedSteps.remove(newStep); + this.currentStep = newStep; + } + } + } +} diff --git a/catalyst_voices/uikit_example/lib/examples/voices_list_tile_example.dart b/catalyst_voices/uikit_example/lib/examples/voices_list_tile_example.dart new file mode 100644 index 00000000000..71c7ec08deb --- /dev/null +++ b/catalyst_voices/uikit_example/lib/examples/voices_list_tile_example.dart @@ -0,0 +1,125 @@ +import 'package:catalyst_voices/widgets/app_bar/voices_app_bar.dart'; +import 'package:catalyst_voices/widgets/menu/voices_expandable_list_tile.dart'; +import 'package:catalyst_voices/widgets/menu/voices_list_tile.dart'; +import 'package:catalyst_voices/widgets/menu/voices_wallet_tile.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:flutter/material.dart'; + +const _base64Icon = + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAACX' + 'BIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAxlSURBVH' + 'gB7Z0JdFTVGcf/M1kJkMWEAAGyCEpQ3A+R2lJltRVpWTxVRCC0BQWkkB5pWVoNHtmqshShKi' + 'qrWtqyCFLaQ1kUjxpoS0UqiSIMCUGCIYRAyDJJpvf/kjedebx57817b8L6O+fxJjN35s37z3' + 'e/+917v3txwEY8Hk+8OE0WxxRxxOuVr6iowL59+1BcXIyCggLpzIPIZ5nY2FikpKRI5w4dOk' + 'iPe/TogczMTOk5A7jEMcvhcKyEjThgE0I8CpcLDeFkwXjs2LHjIpHMQkGzsrLQp08fSVQdQV' + '2wUUjLAgrhHhCn58TxQKAye/fuxZo1ayThKGKoGTJkiCRm3759tYq5xNFbCOmCBUwL2FRdKd' + 'wUtdcpFEXj0RyiqUHLnDhxomSVfByAXCHiLJjElIBCvHRx2iWOdOVrl4NwSije4MGDJTED4I' + 'JJawxaQCHeaHFaBBVft2nTJixdutQ232Y3skVSTBXK0egbFyEIghJQiMcqm6t8noLNnDlT8n' + 'VXAvSN06ZNC1Stg6rShgUU4q0Qp2zl86tXr8ayZcsum+pqlNatW2P69OmBrHGREDHHwMcYE1' + 'BNPApG4SjglcyoUaMka1RhpRBxjN77dQVUE49VdtKkScjPz8fVAIPxJUuWqFVpXRE1BRTiLY' + 'QiTKF42dnZl21DYRaKt3LlyqBFdAZ6oanBuCbEIxr3lt2khSqqFtjULVtk8AJXFRqWmC0scZ' + 'XyyYsEbAqS98MnzrtWxJMJICLjxLuUwbaagEeh6GEMHTq02RuMsAgPolp4pMfuagfctbaNex' + 'iCDQtFVAxMuNAoYrn8RLjvq011Pd33uXnz5oVUPGe4B13vqUJa9yp06lqDtqk1SE5zo2VcvV' + '+5c2XhKCmMREVpGL76dwyOHGghjmg01IVGWN4zwzRFiJOOxv6/N0b0Xr2p6h71Lc2u2YwZMx' + 'AKMrMu4I7e59BzUAVib6iDGUqLI3FwTww+2hiPo5+3QCiggIwVFbDfvJsPfAX0q7r0d8OGDb' + 'O1h+EQV+vxgwr0GXEGN919AXZCi3zvlTY4+FFL2Al7LBs2bFD6w91CwN58IAkoxMsWpxW+JR' + 'goc9DTLtJuqcZPppYg8157hVNyeH8M1s5qh6Ivo2AXHKylP1SQw4EHWUA/67Oz6oaLxmDguF' + 'L8cOxpRER60By4axzY+loSti5PREO9PT5yzpw5yn4zG5KMsCbry/Z9hdZ37tw5WCU2sR5PvV' + 'yMXo+UIywMzUaYaBpp6TfeUYVDn7REzQUnrMJGhQJGRXktO1ocNfxkvyjbrvG8Np3cmLbWhe' + '69zuNS0f27lZi6ohBJ4rtYhZpwkFjBZAqY7luI1dcqbUUYMnXFMXGuhRVqq52Wrad95xo888' + 'YxW0TkyJOiUY33iwPlKUYrJLStQ87rhUhMCe4LFx+Owud7WqHwv9E4LhqAkmORqHM3+q+omA' + 'YRH7qR0M6NW4VVdbu3Eildagx/NmtDzmtFmD8qVcSR4TAL3Rqt0HdqwCF8oNez9+/f35KAEV' + 'EeyfI631llqLynwYGPN8fi4/fiUbA3Bh6DbQzDIV7jwZ+exl0ilnQYNNIvhD/8/fhOlno1DG' + 'vy8vK8f3svvXPnTsvWN/jpU4bFY9w2d2Qa3pqRgvw84+IRlj28vwWWTuqIFx7LQFF+tKH33f' + 'KdSgx8shRWoBX6Tl14BbQa83UWLd6DY87olvN4HHhfhBi/G5WGr/db7z24DkZj9vB0/H1Foq' + 'EfYeC40+hyt7EfORC+jYkkIFXduHEjzMJY74nnToqqpH0H9Glrn2+HjYvb2Do4wLjvTy8m45' + '0X2kluQQtnmAePTSuRzmahBcqNiSQgGw8r3Pfjs0jNrNYswxtb+3x77F6nmzJjmp3vJuDdOW' + '11LTFDDFz0frQcZqHByZpJAlqpvrS+AaPLdMtteTURe9bHIdTseCdB6oXocf+jZyxZoZ+AVu' + 'ZzGawy1tKCDn/Lq/o3ZRe81mEx5KVFh5tqcHc/80G+bHROmqOV1rfvE9rWR7+36tn2IRu3U7' + '2m8K9vz26re807e5vvrlIz+kHnoUOHYJb45DrcdI92i5a3NQ4nvrZvZMQohYei8aGOy7hnQA' + 'WiWzbALKzG4VYakNYJ9XCztxAgkj1f5sTf3rwBRnGGcSBA2y81iPutdxuz5h1rb8BtvSoRm1' + 'Sv+nq9sNBk0UspzDf3A9MKw61U36KCKEzKuhl2MPjpUmnYy6kjIG/6fdEgbV7WBnrQ8n/Vrw' + 'tCBUdonCdOnMClJjrGg4ef0heP0EIHjT+ta6nNAbVz2jHuZxUKpxeE+5UX4YezGccXA8Ha67' + 'xW5npDhfNKS0u7nJAsENexxHUBLXJdQIs4NdL/r6MDR6evW6AFaHyXhQXWuRHUkD4nyz3mu7' + 'C2wcwtJxftXWpqq5zY+nqSoSwCltn8hyTvjN2lhNqFMw/OLOm3VkuzcMzlU+Ps6XAs+HkqSl' + 'yRup/FYf7NS5N0exiStTYYE6/TzTWYsrwQLWPVBxPctU7MfTzN9GgRa294x44dYZZvj0cgTG' + 'gTKOclScwNf/+Rcvz5pWQYgQMF9eYy3VQZkF2G+DaBP5A/RkWZ+XlirsFz8h+zVJ4Nw/5/tN' + 'Is008MuHa82fgkuF10yqxB1sCzmmUK9sXg/BnznWrWXiebYisNyYEPtQUMF9b5+IyTluYfgo' + 'XzNKNzT0hnLXasTYBZqJnUiPAPnXW1muRtjcWpogjNMl2zLkjDVc3FoAmlyLhde5bwxOEoHM' + 'rTnjfRQq65koBMIDQLW8UP/qg/6vyj8aXCH+pPvFulz/AzGDj2tG65bW8mGm6M1JCNThLQwD' + 'J5TbavSdBNr+Co/4jflki5gqGi74gyDJ9Zoju2yO/6yRbz90v8LJB+0EpjwtZz3fy2ur9oo2' + '/6BkOnnLI1WzUi2oNhOacwfEYJnAayI1bltrdkfUy0lA3O25UbOXIkrEB/8tflibrlaInMT5' + 'n+tgup3aphFc4K/nqVCw+JauswoAmr7tEDxpKRAuHbZvilt/Xs2dNSVn6ksIRfLi8SN1VpqD' + 'y7Y/t3tca2NxJx5DPjiUYU6tb7KnHf4HJkPVRhSDjy5T9jsGBsqpRLYxa2vtu3b///d/EVkO' + 'm9PKwQJwJXpvYmB5kRyqTK/E9b4gtxlJ2MkHovtdWNN8rqnpxWK/UsUm+pxu33n0f7G4OLLf' + 'l580el4ayFBEuiTDb3E5ATTEyytDrMn5xai2feCj5LVQnTex0ODyJbWPOX7DG9/LNUfFuk36' + 'XUQm0NHX2gt1lkY2LVF5JThZF4cUyaoT6wFkzttSreN0eibBGP0PIUnQ4XLTAXPpn6tEIuLr' + 'Rjti4uqR7jXipGZpYxn2g3n+1uhRW/ScG5MutzoErf18QYWiDXBftZocb+KkFxtjQMC8d2wq' + 'YlbYTjbr6xW3e1U7rmK7/oaIt4REUTF7ePklcqcWX6Qt9XuT7Yzm1MmIA54tmT6HKntfRaPZ' + 'jp/5cFyTheYF9CE8MW7qmgYIxXQCJE3AWf/a9Ctdjwjt7n8fCToq96m71C5u+NwQfrErB3m7' + 'UehhIGzOvXr7/Y9zkcGXzgK+ADaNzOyQuTqefOnYtQkHFbNXoNK0f375033VpzzcenW+Pwr+' + '2tdBMqzaKyRo5kyCvX/SJKISL94WTf57jgOpR7w3CY68bbq9D5rip0EUdcUp0Ig9xonVDn87' + '0cuFDhxEnRqrOFZ8x4VATeX/2nRUgTNwPsKcPtoXLlP5QCMgOc+yWky8+xVaY/tJKIaYbwSB' + 'HCNLmxmiqH1N9uTjhYynXCCrxVV+b6phMqBLPpxEWxRVOBHAMfeFWica+z1LbHUw3OmrbHnG' + 'Xwg68adMRT3RZPb+unleI02ve5a3Drp8VCvCmB3qfZPRBvzBanVWoX6tatG64W5D1iVMRbpS' + 'Ue0e1fBRKRwaXKdiBXHLwHDfGy9d4fzAaMF8WIhCvcGSteaZmu7GFMmDAhkBEYEo8EuwVoLh' + 'R7LBD6Q4po5zYpoYSzkLNnzw7UIPoFynqY2YSWPoEiXo2b0OYEu0G37dsgE4pIMS8XIVldOV' + 'DMI8D0rQvNtQ2yL4GqNKF4XEZ2KS3SgHBkMRp37jU1YW3HVvDp0LBGQt/Iw44tVfSgUJzjpm' + 'g6GRe70ejvdsMCdv5nBNlotMb0QGXk/4yAYtqxxYoMfRsHPSmcgSwLUxtuB8L2IQ4jQspQUC' + '7Yo5hcd0ZB5fXLyrBIbjF55tG1a1fpHERaCoVjdV1ktrqq8T+Ol4X0Bf+NFwAAAABJRU5Erk' + 'Jggg=='; + +class VoicesListTileExample extends StatelessWidget { + static const String route = '/list-tile-example'; + + const VoicesListTileExample({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: VoicesAppBar(), + body: SizedBox( + width: 300, + child: ListView( + padding: const EdgeInsets.all(16), + children: [ + VoicesExpandableListTile( + title: const Text('My Dashboard'), + leading: const Icon(CatalystVoicesIcons.home), + trailing: const Icon(CatalystVoicesIcons.eye), + expandedChildren: [ + VoicesListTile( + trailing: const Icon(CatalystVoicesIcons.eye), + title: const Text('My Catalyst Proposals'), + onTap: () {}, + ), + VoicesListTile( + trailing: const Icon(CatalystVoicesIcons.eye), + title: const Text('My Actions'), + onTap: () {}, + ), + VoicesListTile( + trailing: const Icon(CatalystVoicesIcons.eye), + title: const Text('Catalyst Campaign Timeline'), + onTap: () {}, + ), + ], + ), + const Divider(), + VoicesListTile( + leading: const Icon(CatalystVoicesIcons.user), + trailing: const Icon(CatalystVoicesIcons.eye), + title: const Text('Catalyst Roles'), + onTap: () {}, + ), + VoicesWalletTile( + iconSrc: _base64Icon, + name: const Text('Wallet Extension'), + onTap: () {}, + ), + ], + ), + ), + ); + } +} diff --git a/catalyst_voices/uikit_example/lib/examples/voices_menu_example.dart b/catalyst_voices/uikit_example/lib/examples/voices_menu_example.dart new file mode 100644 index 00000000000..39724dccdda --- /dev/null +++ b/catalyst_voices/uikit_example/lib/examples/voices_menu_example.dart @@ -0,0 +1,154 @@ +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; +import 'package:flutter/material.dart'; + +class VoicesMenuExample extends StatefulWidget { + static const String route = '/menu-example'; + + const VoicesMenuExample({super.key}); + + @override + State createState() => _VoicesMenuExampleState(); +} + +class _VoicesMenuExampleState extends State { + final _problemSensingController = VoicesNodeMenuController(); + + @override + void dispose() { + _problemSensingController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Voices Menu')), + body: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const _MenuExample1(), + const _MenuExample2(), + VoicesNodeMenu( + name: 'Problem-sensing stage', + controller: _problemSensingController, + items: [ + VoicesNodeMenuItem(id: 0, label: 'Start'), + VoicesNodeMenuItem(id: 1, label: 'Vote'), + VoicesNodeMenuItem(id: 2, label: 'Results'), + ], + onSelectionChanged: (value) { + _problemSensingController.selected = value; + }, + onExpandChanged: (value) { + _problemSensingController.isExpanded = value; + }, + ), + ].separatedBy(const SizedBox(height: 12)).toList(), + ), + ), + Expanded( + child: Container(), + ), + ], + ), + ); + } +} + +class _MenuExample1 extends StatelessWidget { + const _MenuExample1(); + + @override + Widget build(BuildContext context) { + return VoicesMenu( + onTap: (menuItem) => debugPrint('Selected label: ${menuItem.label}'), + menuItems: [ + MenuItem( + id: 1, + label: 'Rename', + icon: CatalystVoicesIcons.pencil, + ), + SubMenuItem( + id: 4, + label: 'Move Private Team', + icon: CatalystVoicesIcons.switch_horizontal, + children: [ + MenuItem(id: 5, label: 'Team 1: The Vikings'), + MenuItem(id: 6, label: 'Team 2: Pure Hearts'), + ], + ), + MenuItem( + id: 2, + label: 'Move to public', + icon: CatalystVoicesIcons.switch_horizontal, + showDivider: true, + enabled: false, + ), + MenuItem( + id: 3, + label: 'Delete', + icon: CatalystVoicesIcons.trash, + ), + ], + child: const SizedBox( + height: 56, + width: 200, + child: Align( + alignment: Alignment.centerLeft, + child: Text('My first proposal'), + ), + ), + ); + } +} + +class _MenuExample2 extends StatelessWidget { + const _MenuExample2(); + + @override + Widget build(BuildContext context) { + return VoicesMenu( + onTap: (menuItem) => debugPrint('Selected label: ${menuItem.label}'), + menuItems: [ + MenuItem( + id: 1, + label: 'Rename', + icon: CatalystVoicesIcons.pencil, + ), + SubMenuItem( + id: 4, + label: 'Move Private Team', + icon: CatalystVoicesIcons.switch_horizontal, + children: [ + MenuItem(id: 5, label: 'Team 1: The Vikings'), + MenuItem(id: 6, label: 'Team 2: Pure Hearts'), + ], + ), + MenuItem( + id: 2, + label: 'Move to public', + icon: CatalystVoicesIcons.switch_horizontal, + showDivider: true, + ), + MenuItem( + id: 3, + label: 'Delete', + icon: CatalystVoicesIcons.trash, + ), + ], + child: const SizedBox( + height: 56, + width: 200, + child: Align( + alignment: Alignment.centerLeft, + child: Text('My second proposal'), + ), + ), + ); + } +} diff --git a/catalyst_voices/uikit_example/lib/examples/voices_modals_example.dart b/catalyst_voices/uikit_example/lib/examples/voices_modals_example.dart new file mode 100644 index 00000000000..e06777f9967 --- /dev/null +++ b/catalyst_voices/uikit_example/lib/examples/voices_modals_example.dart @@ -0,0 +1,33 @@ +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:flutter/material.dart'; + +class VoicesModalsExample extends StatelessWidget { + static const String route = '/modals-example'; + + const VoicesModalsExample({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: Text('Modals')), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + VoicesFilledButton( + child: Text('Desktop info dialog'), + onTap: () { + VoicesDialog.show( + context, + builder: (context) { + return VoicesDesktopInfoDialog(title: 'Desktop modal'); + }, + ); + }, + ), + ], + ), + ), + ); + } +} diff --git a/catalyst_voices/uikit_example/lib/examples/voices_navigation_example.dart b/catalyst_voices/uikit_example/lib/examples/voices_navigation_example.dart index 41c819393dc..cb46c46d931 100644 --- a/catalyst_voices/uikit_example/lib/examples/voices_navigation_example.dart +++ b/catalyst_voices/uikit_example/lib/examples/voices_navigation_example.dart @@ -1,9 +1,8 @@ import 'package:catalyst_voices/widgets/app_bar/actions/voices_app_bar_actions.dart'; -import 'package:catalyst_voices/widgets/app_bar/voices_app_bar.dart'; -import 'package:catalyst_voices/widgets/drawer/voices_drawer.dart'; import 'package:catalyst_voices/widgets/menu/voices_expandable_list_tile.dart'; -import 'package:catalyst_voices/widgets/menu/voices_list_tile.dart'; +import 'package:catalyst_voices/widgets/widgets.dart'; import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; import 'package:flutter/material.dart'; class VoicesNavigationExample extends StatelessWidget { @@ -91,87 +90,19 @@ class _DrawerChooser extends StatefulWidget { } class _DrawerChooserState extends State<_DrawerChooser> { - _Space _selectedSpace = _Space.ideas; + Space _selectedSpace = Space.discovery; @override Widget build(BuildContext context) { - return VoicesDrawerChooser<_Space>( - items: _Space.values, - selectedItem: _selectedSpace, - onSelected: _onSpaceSelected, - itemBuilder: _itemBuilder, - leading: IconButton( - icon: const Icon(CatalystVoicesIcons.all_spaces_menu, size: 20), - onPressed: () {}, - ), + return VoicesDrawerSpaceChooser( + onChanged: _onSpaceSelected, + currentSpace: _selectedSpace, ); } - void _onSpaceSelected(_Space space) { + void _onSpaceSelected(Space space) { setState(() { _selectedSpace = space; }); } - - Widget _itemBuilder({ - required BuildContext context, - required _Space item, - required bool isSelected, - }) { - if (isSelected) { - return VoicesDrawerChooserItem( - icon: item.icon(), - foregroundColor: item.foregroundColor(context), - backgroundColor: item.backgroundColor(context), - ); - } else { - return const VoicesDrawerChooserItemPlaceholder(); - } - } -} - -enum _Space { - ideas, - discovery, - proposals, - settings; - - IconData icon() { - switch (this) { - case _Space.ideas: - return CatalystVoicesIcons.light_bulb; - case _Space.discovery: - return CatalystVoicesIcons.shopping_cart; - case _Space.proposals: - return CatalystVoicesIcons.fund; - case _Space.settings: - return CatalystVoicesIcons.cog_gear; - } - } - - Color foregroundColor(BuildContext context) { - switch (this) { - case _Space.ideas: - return Theme.of(context).colorScheme.primary; - case _Space.discovery: - return Theme.of(context).colorScheme.secondary; - case _Space.proposals: - return Colors.white; - case _Space.settings: - return Colors.white; - } - } - - Color backgroundColor(BuildContext context) { - switch (this) { - case _Space.ideas: - return Theme.of(context).colorScheme.primaryContainer; - case _Space.discovery: - return Theme.of(context).colorScheme.secondaryContainer; - case _Space.proposals: - return Colors.orange; - case _Space.settings: - return Colors.green; - } - } } diff --git a/catalyst_voices/uikit_example/lib/examples/voices_proposal_card_example.dart b/catalyst_voices/uikit_example/lib/examples/voices_proposal_card_example.dart new file mode 100644 index 00000000000..aa595b9fbe7 --- /dev/null +++ b/catalyst_voices/uikit_example/lib/examples/voices_proposal_card_example.dart @@ -0,0 +1,65 @@ +import 'package:catalyst_cardano_serialization/catalyst_cardano_serialization.dart'; +import 'package:catalyst_voices/widgets/cards/pending_proposal_card.dart'; +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; +import 'package:flutter/material.dart'; + +final _description = """ +Zanzibar is becoming one of the hotspots for DID's through +World Mobile and PRISM, but its potential is only barely exploited. +Zanzibar is becoming one of the hotspots for DID's through World Mobile +and PRISM, but its potential is only barely exploited. +""" + .replaceAll('\n', ' '); + +class VoicesProposalCardExample extends StatelessWidget { + static const String route = '/proposal-card-example'; + + const VoicesProposalCardExample({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Proposal Card')), + body: Padding( + padding: const EdgeInsets.all(32), + child: Wrap( + spacing: 16, + runSpacing: 16, + children: [ + FundedProposalCard( + image: VoicesAssets.images.proposalBackground1, + proposal: FundedProposal( + id: 'f14/1', + fund: 'F14', + category: 'Cardano Use Cases / MVP', + title: 'Proposal Title that rocks the world', + fundedDate: DateTime(2025, 1, 28), + fundsRequested: Coin.fromAda(100000), + commentsCount: 0, + description: _description, + ), + ), + PendingProposalCard( + image: VoicesAssets.images.proposalBackground2, + proposal: PendingProposal( + id: 'f14/2', + fund: 'F14', + category: 'Cardano Use Cases / MVP', + title: 'Proposal Title that rocks the world', + lastUpdateDate: DateTime.now().minusDays(2), + fundsRequested: Coin.fromAda(100000), + commentsCount: 0, + description: _description, + completedSegments: 7, + totalSegments: 13, + ), + ), + ], + ), + ), + ); + } +} diff --git a/catalyst_voices/uikit_example/lib/examples/voices_rich_text_example.dart b/catalyst_voices/uikit_example/lib/examples/voices_rich_text_example.dart new file mode 100644 index 00000000000..dc8d20bf5d3 --- /dev/null +++ b/catalyst_voices/uikit_example/lib/examples/voices_rich_text_example.dart @@ -0,0 +1,37 @@ +import 'package:catalyst_voices/widgets/rich_text/voices_rich_text.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_quill/flutter_quill.dart'; + +class VoicesRichTextExample extends StatelessWidget { + static const String route = '/rich-text-example'; + + const VoicesRichTextExample({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Theme.of(context).colors.onSurfaceNeutralOpaqueLv0, + appBar: AppBar(title: const Text('Voices Rich Text')), + body: SingleChildScrollView( + child: VoicesRichText( + title: 'Rich text', + document: Document.fromJson(_textSample), + charsLimit: 800, + onSave: (document) => print('Saved document: $document'), + ), + ), + ); + } +} + +const _textSample = [ + {'insert': 'Sample Rich Text Editor'}, + { + 'attributes': {'header': 1}, + 'insert': '\n' + }, + {'insert': '\n'}, + {'insert': 'We will be able to create content now!'}, + {'insert': '\n'} +]; diff --git a/catalyst_voices/uikit_example/lib/examples/voices_seed_phrase_example.dart b/catalyst_voices/uikit_example/lib/examples/voices_seed_phrase_example.dart new file mode 100644 index 00000000000..f724d451285 --- /dev/null +++ b/catalyst_voices/uikit_example/lib/examples/voices_seed_phrase_example.dart @@ -0,0 +1,47 @@ +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:flutter/material.dart'; + +class VoicesSeedPhraseExample extends StatelessWidget { + static const String route = '/seed-phrase-example'; + + static const _words = [ + 'real', + 'mission', + 'secure', + 'renew', + 'key', + 'audit', + 'right', + 'gas', + 'house', + 'plant', + 'car', + 'sand', + ]; + + const VoicesSeedPhraseExample({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Seed Phrase')), + body: ListView( + padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 16), + children: [ + const Text('SeedPhrasesViewer'), + const SizedBox(height: 12), + const SeedPhrasesViewer(words: _words), + const SizedBox(height: 24), + const Text('SeedPhrasesSequencer'), + const SizedBox(height: 12), + SeedPhrasesSequencer( + words: _words, + onChanged: (value) {}, + ), + ], + ), + ); + } +} diff --git a/catalyst_voices/uikit_example/lib/examples/voices_separators_example.dart b/catalyst_voices/uikit_example/lib/examples/voices_separators_example.dart new file mode 100644 index 00000000000..68bb94d7c22 --- /dev/null +++ b/catalyst_voices/uikit_example/lib/examples/voices_separators_example.dart @@ -0,0 +1,69 @@ +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:flutter/material.dart'; + +class VoicesSeparatorsExample extends StatelessWidget { + static const String route = '/separators-example'; + + const VoicesSeparatorsExample({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Voices Switch')), + body: Column( + children: [ + ColoredBox( + color: Theme.of(context).colorScheme.primaryContainer, + child: const Padding( + padding: EdgeInsets.all(32), + child: Text('Paragraph'), + ), + ), + const VoicesDivider(), + ColoredBox( + color: Theme.of(context).colorScheme.primaryContainer, + child: const Padding( + padding: EdgeInsets.all(32), + child: Text('Paragraph'), + ), + ), + const VoicesTextDivider( + child: Text('Your account creation progress'), + ), + ColoredBox( + color: Theme.of(context).colorScheme.primaryContainer, + child: const Padding( + padding: EdgeInsets.all(32), + child: Text('Paragraph'), + ), + ), + const SizedBox(height: 28), + SizedBox( + height: 48, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + constraints: BoxConstraints.tight(const Size.square(48)), + color: Theme.of(context).colorScheme.primaryContainer, + ), + const VoicesVerticalDivider(), + Container( + constraints: BoxConstraints.tight(const Size.square(48)), + color: Theme.of(context).colorScheme.primaryContainer, + ), + const VoicesVerticalDivider(), + Container( + constraints: BoxConstraints.tight(const Size.square(48)), + color: Theme.of(context).colorScheme.primaryContainer, + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/catalyst_voices/uikit_example/lib/examples/voices_snackbar_example.dart b/catalyst_voices/uikit_example/lib/examples/voices_snackbar_example.dart index 25df7b925cd..bcc457d83b0 100644 --- a/catalyst_voices/uikit_example/lib/examples/voices_snackbar_example.dart +++ b/catalyst_voices/uikit_example/lib/examples/voices_snackbar_example.dart @@ -12,6 +12,7 @@ class VoicesSnackbarExample extends StatelessWidget { final screenWidth = MediaQuery.sizeOf(context).width; return Scaffold( + appBar: AppBar(title: const Text('Voices Snackbar')), body: Padding( padding: const EdgeInsets.all(32), child: Wrap( diff --git a/catalyst_voices/uikit_example/lib/examples/voices_spaces_example.dart b/catalyst_voices/uikit_example/lib/examples/voices_spaces_example.dart new file mode 100644 index 00000000000..beced4943ff --- /dev/null +++ b/catalyst_voices/uikit_example/lib/examples/voices_spaces_example.dart @@ -0,0 +1,110 @@ +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:flutter/material.dart'; +import 'package:uikit_example/widgets/session_header.dart'; +import 'package:uikit_example/widgets/user_header.dart'; + +class VoicesSpacesExample extends StatelessWidget { + static const String route = '/spaces-example'; + + const VoicesSpacesExample({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: const VoicesAppBar( + actions: [ + SessionHeader(), + UserHeader(), + ], + ), + drawer: const VoicesDrawer(children: []), + body: CustomScrollView( + slivers: [ + const SliverToBoxAdapter(child: _SpacesNavigationLocation()), + SliverPadding( + padding: const EdgeInsets.symmetric(horizontal: 32) + .add(const EdgeInsets.only(bottom: 32)), + sliver: SliverList( + delegate: SliverChildListDelegate( + [ + const _Segment(name: 'Segment 1'), + const SizedBox(height: 24), + const _Segment(name: 'Segment 2'), + const SizedBox(height: 24), + const _Segment(name: 'Segment 3'), + ], + ), + ), + ), + const SliverFillRemaining( + hasScrollBody: false, + child: Column( + children: [ + Spacer(), + StandardLinksPageFooter(), + ], + ), + ), + ], + ), + ); + } +} + +class _SpacesNavigationLocation extends StatelessWidget { + const _SpacesNavigationLocation(); + + @override + Widget build(BuildContext context) { + return const NavigationLocation( + parts: [ + 'Discovery Space', + 'Homepage', + ], + ); + } +} + +class _Segment extends StatelessWidget { + const _Segment({ + required this.name, + }); + + final String name; + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + return AspectRatio( + aspectRatio: 1376 / 673, + child: Container( + decoration: BoxDecoration( + color: theme.colors.onSurfaceNeutralOpaqueLv1, + border: Border.all(color: theme.colors.outlineBorderVariant!), + borderRadius: BorderRadius.circular(12), + ), + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + name, + style: theme.textTheme.titleLarge?.copyWith( + color: theme.colors.textOnPrimary, + ), + ), + const Spacer(), + VoicesFilledButton( + child: const Text('CTA to Model'), + onTap: () {}, + ), + ], + ), + ), + ); + } +} diff --git a/catalyst_voices/uikit_example/lib/examples/voices_switch_example.dart b/catalyst_voices/uikit_example/lib/examples/voices_switch_example.dart new file mode 100644 index 00000000000..b1405b6ed66 --- /dev/null +++ b/catalyst_voices/uikit_example/lib/examples/voices_switch_example.dart @@ -0,0 +1,56 @@ +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:flutter/material.dart'; + +class VoicesSwitchExample extends StatefulWidget { + static const String route = '/switch-example'; + + const VoicesSwitchExample({ + super.key, + }); + + @override + State createState() => _VoicesSwitchExampleState(); +} + +class _VoicesSwitchExampleState extends State { + final _switchStateMap = {}; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Voices Switch')), + body: SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: Column( + children: [ + VoicesSwitch( + value: _switchStateMap[0] ?? true, + onChanged: (value) { + setState(() { + _switchStateMap[0] = value; + }); + }, + ), + const SizedBox(height: 8), + VoicesSwitch( + value: _switchStateMap[1] ?? true, + thumbIcon: Icons.check, + onChanged: (value) { + setState(() { + _switchStateMap[1] = value; + }); + }, + ), + const SizedBox(height: 8), + const VoicesSwitch(value: false), + const SizedBox(height: 8), + const VoicesSwitch( + value: false, + thumbIcon: Icons.close, + ), + ], + ), + ), + ); + } +} diff --git a/catalyst_voices/uikit_example/lib/examples/voices_tabs_example.dart b/catalyst_voices/uikit_example/lib/examples/voices_tabs_example.dart new file mode 100644 index 00000000000..d6147faf0b8 --- /dev/null +++ b/catalyst_voices/uikit_example/lib/examples/voices_tabs_example.dart @@ -0,0 +1,36 @@ +import 'package:catalyst_voices/widgets/common/tab_bar_stack_view.dart'; +import 'package:flutter/material.dart'; + +class VoicesTabsExample extends StatelessWidget { + static const String route = '/tabs-example'; + + const VoicesTabsExample({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Tabs')), + body: DefaultTabController( + length: 2, + child: Column( + children: [ + const TabBar( + tabs: [ + Tab(text: 'Sections'), + Tab(text: 'Comments'), + ], + ), + Expanded( + child: TabBarStackView( + children: [ + Container(color: Colors.red), + Container(color: Colors.blue), + ], + ), + ), + ], + ), + ), + ); + } +} diff --git a/catalyst_voices/uikit_example/lib/examples/voices_text_field_example.dart b/catalyst_voices/uikit_example/lib/examples/voices_text_field_example.dart new file mode 100644 index 00000000000..6e71c044ffb --- /dev/null +++ b/catalyst_voices/uikit_example/lib/examples/voices_text_field_example.dart @@ -0,0 +1,221 @@ +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:flutter/material.dart'; + +class VoicesTextFieldExample extends StatefulWidget { + static const String route = '/text-field-example'; + + const VoicesTextFieldExample({super.key}); + + @override + State createState() => _VoicesTextFieldExampleState(); +} + +class _VoicesTextFieldExampleState extends State { + final _controller = TextEditingController(); + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Voices Text Field')), + body: SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24), + child: Wrap( + spacing: 24, + runSpacing: 24, + children: [ + SizedBox( + width: 200, + child: VoicesTextField( + controller: _controller, + decoration: const VoicesTextFieldDecoration( + labelText: 'Regular', + helperText: 'Supporting text', + hintText: 'Hint text', + suffixIcon: Icon(CatalystVoicesIcons.chevron_down), + ), + maxLength: 200, + ), + ), + SizedBox( + width: 200, + child: VoicesTextField( + controller: _controller, + decoration: const VoicesTextFieldDecoration( + labelText: 'Disabled', + helperText: 'Supporting text', + hintText: 'Hint text', + suffixIcon: Icon(CatalystVoicesIcons.chevron_down), + ), + maxLength: 200, + enabled: false, + ), + ), + SizedBox( + width: 200, + child: VoicesTextField( + controller: _controller, + decoration: const VoicesTextFieldDecoration( + labelText: 'Error text', + helperText: 'Supporting text', + hintText: 'Hint text', + errorText: 'Error text', + suffixIcon: Icon(Icons.error_outline), + ), + maxLength: 200, + ), + ), + SizedBox( + width: 200, + child: VoicesTextField( + controller: _controller, + decoration: const VoicesTextFieldDecoration( + labelText: 'Success', + helperText: 'Supporting text', + hintText: 'Hint text', + ), + maxLength: 200, + validator: VoicesTextFieldValidationResult.success(), + ), + ), + SizedBox( + width: 200, + child: VoicesTextField( + controller: _controller, + decoration: const VoicesTextFieldDecoration( + labelText: 'Warning', + helperText: 'Supporting text', + hintText: 'Hint text', + ), + maxLength: 200, + validator: + VoicesTextFieldValidationResult.warning('Warning message'), + ), + ), + SizedBox( + width: 200, + child: VoicesTextField( + controller: _controller, + decoration: const VoicesTextFieldDecoration( + labelText: 'Error', + helperText: 'Supporting text', + hintText: 'Hint text', + ), + maxLength: 200, + validator: + VoicesTextFieldValidationResult.error('Error message'), + ), + ), + SizedBox( + width: 200, + child: VoicesTextField( + controller: _controller, + decoration: const VoicesTextFieldDecoration( + labelText: 'Success / disabled', + helperText: 'Supporting text', + hintText: 'Hint text', + ), + maxLength: 200, + validator: VoicesTextFieldValidationResult.success(), + enabled: false, + ), + ), + SizedBox( + width: 200, + child: VoicesTextField( + controller: _controller, + decoration: const VoicesTextFieldDecoration( + labelText: 'Warning / disabled', + helperText: 'Supporting text', + hintText: 'Hint text', + ), + maxLength: 200, + validator: + VoicesTextFieldValidationResult.warning('Warning message'), + enabled: false, + ), + ), + SizedBox( + width: 200, + child: VoicesTextField( + controller: _controller, + decoration: const VoicesTextFieldDecoration( + labelText: 'Error / disabled', + helperText: 'Supporting text', + hintText: 'Hint text', + ), + maxLength: 200, + validator: + VoicesTextFieldValidationResult.error('Error message'), + enabled: false, + ), + ), + SizedBox( + width: 200, + child: VoicesTextField( + controller: _controller, + decoration: const VoicesTextFieldDecoration( + labelText: 'success / warning / error', + helperText: 'Supporting text', + hintText: 'Hint text', + ), + validator: (value) { + if (value == 'success') { + return const VoicesTextFieldValidationResult( + status: VoicesTextFieldStatus.success, + ); + } else if (value == 'warning') { + return const VoicesTextFieldValidationResult( + status: VoicesTextFieldStatus.warning, + errorMessage: 'Warning message', + ); + } else if (value == 'error') { + return const VoicesTextFieldValidationResult( + status: VoicesTextFieldStatus.error, + errorMessage: 'Error message', + ); + } else { + return const VoicesTextFieldValidationResult( + status: VoicesTextFieldStatus.none, + ); + } + }, + maxLength: 200, + ), + ), + SizedBox( + width: 200, + child: VoicesTextField( + controller: _controller, + decoration: const VoicesTextFieldDecoration( + labelText: 'Field label', + helperText: 'Supporting text', + hintText: 'Hint text', + ), + maxLength: 200, + minLines: 6, + maxLines: 10, + ), + ), + SizedBox( + width: 200, + child: VoicesTextField( + controller: _controller, + decoration: const VoicesTextFieldDecoration( + labelText: 'Resizable', + ), + maxLines: null, + ), + ), + ], + ), + ), + ); + } +} diff --git a/catalyst_voices/uikit_example/lib/examples/voices_tooltips_example.dart b/catalyst_voices/uikit_example/lib/examples/voices_tooltips_example.dart new file mode 100644 index 00000000000..6103636c413 --- /dev/null +++ b/catalyst_voices/uikit_example/lib/examples/voices_tooltips_example.dart @@ -0,0 +1,98 @@ +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; +import 'package:flutter/material.dart'; + +class VoicesTooltipsExample extends StatelessWidget { + static const String route = '/tooltips-example'; + + const VoicesTooltipsExample({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Tooltips')), + body: ListView( + padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 16), + children: [ + Center( + child: VoicesPlainTooltip( + message: 'Supporting text', + child: Container( + color: Colors.blue, + padding: const EdgeInsets.all(8), + child: const Text( + 'Plain Tooltip trigger', + style: TextStyle(color: Colors.white), + ), + ), + ), + ), + Center( + child: VoicesPlainTooltip( + message: 'This is very long supporting text. ' + 'This is very long supporting text. ' + 'This is very long supporting text. ' + 'This is very long supporting text.', + child: Container( + color: Colors.blue, + padding: const EdgeInsets.all(8), + child: const Text( + 'Big Plain Tooltip trigger', + style: TextStyle(color: Colors.white), + ), + ), + ), + ), + Center( + child: VoicesRichTooltip( + title: 'Title', + message: 'This is supporting text. This is supporting text.', + child: Container( + color: Colors.blue, + padding: const EdgeInsets.all(8), + child: const Text( + 'Rich Tooltip trigger', + style: TextStyle(color: Colors.white), + ), + ), + ), + ), + Center( + child: VoicesRichTooltip( + title: 'Title', + message: 'This is supporting text. This is supporting text.', + actions: [ + VoicesRichTooltipActionData( + name: 'Action', + onTap: () { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Tooltip action clicked!')), + ); + }, + ), + VoicesRichTooltipActionData( + name: 'Action', + onTap: () { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Tooltip action clicked!')), + ); + }, + ), + ], + child: Container( + color: Colors.blue, + padding: const EdgeInsets.all(8), + child: const Text( + 'Rich Tooltip [Actions] trigger', + style: TextStyle(color: Colors.white), + ), + ), + ), + ), + ].separatedBy(const SizedBox(height: 16)).toList(), + ), + ); + } +} diff --git a/catalyst_voices/uikit_example/lib/examples/voices_tree_view_example.dart b/catalyst_voices/uikit_example/lib/examples/voices_tree_view_example.dart new file mode 100644 index 00000000000..3a51e6d95c5 --- /dev/null +++ b/catalyst_voices/uikit_example/lib/examples/voices_tree_view_example.dart @@ -0,0 +1,40 @@ +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:flutter/material.dart'; + +class VoicesTreeViewExample extends StatelessWidget { + static const String route = '/tree-view-example'; + + const VoicesTreeViewExample({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('TreeView')), + body: const Column( + children: [ + SimpleTreeView( + isExpanded: true, + root: SimpleTreeViewRootRow( + leading: [ + Icon(Icons.check_box_outline_blank_rounded), + Icon(Icons.grid_view_rounded), + ], + child: Text('Problem-sensing stage'), + ), + children: [ + SimpleTreeViewChildRow( + isSelected: true, + child: Text('Start'), + ), + SimpleTreeViewChildRow(child: Text('Vote')), + SimpleTreeViewChildRow( + hasNext: false, + child: Text('Results'), + ), + ], + ), + ], + ), + ); + } +} diff --git a/catalyst_voices/uikit_example/lib/examples_list.dart b/catalyst_voices/uikit_example/lib/examples_list.dart index 8a4d33c49ce..59b38108b4d 100644 --- a/catalyst_voices/uikit_example/lib/examples_list.dart +++ b/catalyst_voices/uikit_example/lib/examples_list.dart @@ -1,17 +1,60 @@ import 'dart:async'; import 'package:catalyst_voices/widgets/menu/voices_list_tile.dart'; +import 'package:catalyst_voices/widgets/separators/voices_divider.dart'; +import 'package:catalyst_voices/widgets/separators/voices_text_divider.dart'; import 'package:flutter/material.dart'; +import 'package:uikit_example/examples/treasury_space/voices_treasury_space.dart'; +import 'package:uikit_example/examples/voices_avatar_example.dart'; +import 'package:uikit_example/examples/voices_badge_example.dart'; import 'package:uikit_example/examples/voices_buttons_example.dart'; import 'package:uikit_example/examples/voices_checkbox_example.dart'; import 'package:uikit_example/examples/voices_chip_example.dart'; +import 'package:uikit_example/examples/voices_fab_example.dart'; +import 'package:uikit_example/examples/voices_headers_examples.dart'; +import 'package:uikit_example/examples/voices_indicators_example.dart'; +import 'package:uikit_example/examples/voices_list_tile_example.dart'; +import 'package:uikit_example/examples/voices_menu_example.dart'; +import 'package:uikit_example/examples/voices_modals_example.dart'; import 'package:uikit_example/examples/voices_navigation_example.dart'; +import 'package:uikit_example/examples/voices_proposal_card_example.dart'; import 'package:uikit_example/examples/voices_radio_example.dart'; +import 'package:uikit_example/examples/voices_rich_text_example.dart'; +import 'package:uikit_example/examples/voices_seed_phrase_example.dart'; import 'package:uikit_example/examples/voices_segmented_button_example.dart'; +import 'package:uikit_example/examples/voices_separators_example.dart'; import 'package:uikit_example/examples/voices_snackbar_example.dart'; +import 'package:uikit_example/examples/voices_spaces_example.dart'; +import 'package:uikit_example/examples/voices_switch_example.dart'; +import 'package:uikit_example/examples/voices_tabs_example.dart'; +import 'package:uikit_example/examples/voices_text_field_example.dart'; +import 'package:uikit_example/examples/voices_tooltips_example.dart'; +import 'package:uikit_example/examples/voices_tree_view_example.dart'; class ExamplesListPage extends StatelessWidget { static List get examples { + return [ + ...screens, + ...uiKit, + ]; + } + + static List get screens { + return const [ + ExampleTile( + title: 'VoicesDiscoverySpaces', + route: VoicesSpacesExample.route, + page: VoicesSpacesExample(), + ), + ExampleTile( + title: 'VoicesTreasurySpace', + route: VoicesTreasurySpace.route, + page: VoicesTreasurySpace(), + ), + ]; + } + + static List get uiKit { return const [ ExampleTile( title: 'VoicesNavigation (AppBar + Drawer)', @@ -48,6 +91,91 @@ class ExamplesListPage extends StatelessWidget { route: VoicesCheckboxExample.route, page: VoicesCheckboxExample(), ), + ExampleTile( + title: 'Voices Switch', + route: VoicesSwitchExample.route, + page: VoicesSwitchExample(), + ), + ExampleTile( + title: 'Voices Separators', + route: VoicesSeparatorsExample.route, + page: VoicesSeparatorsExample(), + ), + ExampleTile( + title: 'Voices Indicators', + route: VoicesIndicatorsExample.route, + page: VoicesIndicatorsExample(), + ), + ExampleTile( + title: 'Voices List Tile', + route: VoicesListTileExample.route, + page: VoicesListTileExample(), + ), + ExampleTile( + title: 'Voices Avatars', + route: VoicesAvatarExample.route, + page: VoicesAvatarExample(), + ), + ExampleTile( + title: 'Voices Text Field', + route: VoicesTextFieldExample.route, + page: VoicesTextFieldExample(), + ), + ExampleTile( + title: 'Voices Badges', + route: VoicesBadgeExample.route, + page: VoicesBadgeExample(), + ), + ExampleTile( + title: 'Voices Floating Action Button', + route: VoicesFabExample.route, + page: VoicesFabExample(), + ), + ExampleTile( + title: 'Voices Seed Phrase', + route: VoicesSeedPhraseExample.route, + page: VoicesSeedPhraseExample(), + ), + ExampleTile( + title: 'Voices Tooltips', + route: VoicesTooltipsExample.route, + page: VoicesTooltipsExample(), + ), + ExampleTile( + title: 'Voices Menu', + route: VoicesMenuExample.route, + page: VoicesMenuExample(), + ), + ExampleTile( + title: 'Voices Tabs', + route: VoicesTabsExample.route, + page: VoicesTabsExample(), + ), + ExampleTile( + title: 'Voices Headers', + route: VoicesHeadersExamples.route, + page: VoicesHeadersExamples(), + ), + ExampleTile( + title: 'Voices TreeView', + route: VoicesTreeViewExample.route, + page: VoicesTreeViewExample(), + ), + ExampleTile( + title: 'Voices Modals', + route: VoicesModalsExample.route, + page: VoicesModalsExample(), + ), + ExampleTile( + title: 'Voices Rich Text', + route: VoicesRichTextExample.route, + page: VoicesRichTextExample(), + ), + ExampleTile( + title: 'Voices Proposal Card', + route: VoicesProposalCardExample.route, + page: VoicesProposalCardExample(), + ), ]; } @@ -57,13 +185,30 @@ class ExamplesListPage extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text('UI kit examples'), + title: const Text('UI examples'), ), body: ListView.separated( padding: const EdgeInsets.symmetric(vertical: 32), - itemCount: examples.length, - itemBuilder: (context, index) => examples[index], - separatorBuilder: (context, index) => const Divider(), + itemCount: examples.length + 1, + itemBuilder: (context, index) { + // leading dummy item for divider + if (index == 0) { + return const SizedBox.shrink(); + } + + return examples[index - 1]; + }, + separatorBuilder: (context, index) { + if (index == 0) { + return const VoicesTextDivider(child: Text('Screens')); + } + + if (index == screens.length) { + return const VoicesTextDivider(child: Text('UI Kit')); + } + + return const VoicesDivider(); + }, ), ); } diff --git a/catalyst_voices/uikit_example/lib/generated/assets.gen.dart b/catalyst_voices/uikit_example/lib/generated/assets.gen.dart new file mode 100644 index 00000000000..d625406ba01 --- /dev/null +++ b/catalyst_voices/uikit_example/lib/generated/assets.gen.dart @@ -0,0 +1,108 @@ +/// GENERATED CODE - DO NOT MODIFY BY HAND +/// ***************************************************** +/// FlutterGen +/// ***************************************************** + +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: directives_ordering,unnecessary_import,implicit_dynamic_list_literal,deprecated_member_use + +import 'package:flutter/widgets.dart'; + +class $AssetsImagesGen { + const $AssetsImagesGen(); + + /// File path: assets/images/robot_avatar.png + AssetGenImage get robotAvatar => + const AssetGenImage('assets/images/robot_avatar.png'); + + /// List of all assets + List get values => [robotAvatar]; +} + +class UiKitAssets { + UiKitAssets._(); + + static const $AssetsImagesGen images = $AssetsImagesGen(); +} + +class AssetGenImage { + const AssetGenImage( + this._assetName, { + this.size, + this.flavors = const {}, + }); + + final String _assetName; + + final Size? size; + final Set flavors; + + Image image({ + Key? key, + AssetBundle? bundle, + ImageFrameBuilder? frameBuilder, + ImageErrorWidgetBuilder? errorBuilder, + String? semanticLabel, + bool excludeFromSemantics = false, + double? scale, + double? width, + double? height, + Color? color, + Animation? opacity, + BlendMode? colorBlendMode, + BoxFit? fit, + AlignmentGeometry alignment = Alignment.center, + ImageRepeat repeat = ImageRepeat.noRepeat, + Rect? centerSlice, + bool matchTextDirection = false, + bool gaplessPlayback = false, + bool isAntiAlias = false, + String? package, + FilterQuality filterQuality = FilterQuality.low, + int? cacheWidth, + int? cacheHeight, + }) { + return Image.asset( + _assetName, + key: key, + bundle: bundle, + frameBuilder: frameBuilder, + errorBuilder: errorBuilder, + semanticLabel: semanticLabel, + excludeFromSemantics: excludeFromSemantics, + scale: scale, + width: width, + height: height, + color: color, + opacity: opacity, + colorBlendMode: colorBlendMode, + fit: fit, + alignment: alignment, + repeat: repeat, + centerSlice: centerSlice, + matchTextDirection: matchTextDirection, + gaplessPlayback: gaplessPlayback, + isAntiAlias: isAntiAlias, + package: package, + filterQuality: filterQuality, + cacheWidth: cacheWidth, + cacheHeight: cacheHeight, + ); + } + + ImageProvider provider({ + AssetBundle? bundle, + String? package, + }) { + return AssetImage( + _assetName, + bundle: bundle, + package: package, + ); + } + + String get path => _assetName; + + String get keyName => _assetName; +} diff --git a/catalyst_voices/uikit_example/lib/main.dart b/catalyst_voices/uikit_example/lib/main.dart index 624ab5e5429..2addc762f5f 100644 --- a/catalyst_voices/uikit_example/lib/main.dart +++ b/catalyst_voices/uikit_example/lib/main.dart @@ -1,6 +1,8 @@ +import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; import 'package:catalyst_voices_localization/generated/catalyst_voices_localizations.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_localized_locales/flutter_localized_locales.dart'; import 'package:uikit_example/examples_list.dart'; @@ -16,22 +18,31 @@ class UIKitExampleApp extends StatefulWidget { } class _UIKitExampleAppState extends State { + final SessionBloc _sessionBloc = SessionBloc(); + final UserProfileBloc _userProfileBloc = UserProfileBloc(); + ThemeMode _themeMode = ThemeMode.system; @override Widget build(BuildContext context) { - return MaterialApp( - title: 'UI kit examples', - supportedLocales: VoicesLocalizations.supportedLocales, - localizationsDelegates: const [ - ...VoicesLocalizations.localizationsDelegates, - LocaleNamesLocalizationsDelegate(), + return MultiBlocProvider( + providers: [ + BlocProvider.value(value: _sessionBloc), + BlocProvider.value(value: _userProfileBloc), ], - localeListResolutionCallback: basicLocaleListResolution, - theme: ThemeBuilder.buildTheme(BrandKey.catalyst), - darkTheme: ThemeBuilder.buildDarkTheme(BrandKey.catalyst), - themeMode: _themeMode, - onGenerateRoute: _onGenerateRoute, + child: MaterialApp( + title: 'UI kit examples', + supportedLocales: VoicesLocalizations.supportedLocales, + localizationsDelegates: const [ + ...VoicesLocalizations.localizationsDelegates, + LocaleNamesLocalizationsDelegate(), + ], + localeListResolutionCallback: basicLocaleListResolution, + theme: ThemeBuilder.buildTheme(BrandKey.catalyst), + darkTheme: ThemeBuilder.buildDarkTheme(BrandKey.catalyst), + themeMode: _themeMode, + onGenerateRoute: _onGenerateRoute, + ), ); } diff --git a/catalyst_voices/uikit_example/lib/widgets/session_header.dart b/catalyst_voices/uikit_example/lib/widgets/session_header.dart new file mode 100644 index 00000000000..287041e5809 --- /dev/null +++ b/catalyst_voices/uikit_example/lib/widgets/session_header.dart @@ -0,0 +1,64 @@ +import 'package:catalyst_voices/widgets/buttons/voices_filled_button.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +/// Displays current session state and a button to toggle it to a next state. +class SessionHeader extends StatelessWidget { + const SessionHeader(); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + return switch (state) { + VisitorSessionState() => const _VisitorSessionHeader(), + GuestSessionState() => const _GuestSessionHeader(), + ActiveUserSessionState() => const _ActiveUserSessionHeader(), + }; + }, + ); + } +} + +class _VisitorSessionHeader extends StatelessWidget { + const _VisitorSessionHeader(); + + @override + Widget build(BuildContext context) { + return VoicesFilledButton( + child: Text('Get Started'), + onTap: () => + context.read().add(const NextStateSessionEvent()), + ); + } +} + +class _GuestSessionHeader extends StatelessWidget { + const _GuestSessionHeader(); + + @override + Widget build(BuildContext context) { + return VoicesFilledButton( + trailing: Icon(CatalystVoicesIcons.lock_open), + child: Text('Unlock'), + onTap: () => + context.read().add(const NextStateSessionEvent()), + ); + } +} + +class _ActiveUserSessionHeader extends StatelessWidget { + const _ActiveUserSessionHeader(); + + @override + Widget build(BuildContext context) { + return VoicesFilledButton( + trailing: Icon(CatalystVoicesIcons.lock_closed), + child: Text('Lock'), + onTap: () => + context.read().add(const NextStateSessionEvent()), + ); + } +} diff --git a/catalyst_voices/uikit_example/lib/widgets/user_header.dart b/catalyst_voices/uikit_example/lib/widgets/user_header.dart new file mode 100644 index 00000000000..bfed83ea206 --- /dev/null +++ b/catalyst_voices/uikit_example/lib/widgets/user_header.dart @@ -0,0 +1,25 @@ +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class UserHeader extends StatelessWidget { + const UserHeader({super.key}); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + return VoicesOutlinedButton( + onTap: () => context + .read() + .add(const ToggleUserProfileEvent()), + child: Text(switch (state) { + VisitorUserProfileState() => 'Guest', + ActiveUserProfileState() => state.user.name, + }), + ); + }, + ); + } +} diff --git a/catalyst_voices/uikit_example/pubspec.yaml b/catalyst_voices/uikit_example/pubspec.yaml index 73636632910..3afda02f337 100644 --- a/catalyst_voices/uikit_example/pubspec.yaml +++ b/catalyst_voices/uikit_example/pubspec.yaml @@ -3,29 +3,50 @@ description: Demonstrates usage of UIKit. publish_to: "none" environment: - sdk: ">=3.3.4 <4.0.0" - flutter: ">=3.22.1" + sdk: ">=3.5.0 <4.0.0" + flutter: ">=3.24.1" dependencies: catalyst_voices: path: ../ catalyst_voices_assets: path: ../packages/catalyst_voices_assets + catalyst_voices_blocs: + path: ../packages/catalyst_voices_blocs catalyst_voices_brands: path: ../packages/catalyst_voices_brands catalyst_voices_localization: path: ../packages/catalyst_voices_localization + catalyst_voices_models: + path: ../packages/catalyst_voices_models + catalyst_voices_shared: + path: ../packages/catalyst_voices_shared collection: ^1.18.0 cupertino_icons: ^1.0.6 flutter: sdk: flutter + flutter_bloc: ^8.1.5 flutter_localized_locales: ^2.0.5 + flutter_quill: ^10.5.0 dev_dependencies: - catalyst_analysis: - path: ../../catalyst_voices_packages/catalyst_analysis + build_runner: ^2.4.12 + catalyst_analysis: ^2.0.0 + flutter_gen_runner: ^5.3.2 flutter_test: sdk: flutter flutter: uses-material-design: true + generate: true + assets: + - assets/images/ + +flutter_gen: + output: lib/generated/ + assets: + enabled: true + outputs: + class_name: UiKitAssets + package_parameter_enabled: false + style: dot-delimiter diff --git a/catalyst_voices/uikit_example/web/index.html b/catalyst_voices/uikit_example/web/index.html index f7941782af5..818d16800e5 100644 --- a/catalyst_voices/uikit_example/web/index.html +++ b/catalyst_voices/uikit_example/web/index.html @@ -1,38 +1,41 @@ - - + This is a placeholder for base href that will be replaced by the value of + the `--base-href` argument provided to `flutter build`. + --> + - - - + + + - - - - - + + + + + - - + + - uikit_example - + uikit_example + + - + diff --git a/catalyst_voices/web/index.html b/catalyst_voices/web/index.html index b623f548671..cc5dc45b290 100644 --- a/catalyst_voices/web/index.html +++ b/catalyst_voices/web/index.html @@ -33,6 +33,8 @@ Catalyst Voices + diff --git a/catalyst_voices_packages/README.md b/catalyst_voices_packages/README.md index ae8a66260f1..1e7c5ac0c25 100644 --- a/catalyst_voices_packages/README.md +++ b/catalyst_voices_packages/README.md @@ -14,19 +14,20 @@ A collection of Catalyst packages and plugins for Flutter and Dart. | Name | Pub | Documentation | Android | iOS | Web | macOS | Windows | Linux | |--------|-----|---------------| ------- |-----|-------|-----|---------|-------| -| [`catalyst_cardano_serialization`](catalyst_cardano_serialization) | ![pub package](https://img.shields.io/pub/v/catalyst_cardano_serialization.svg) | [📖](https://pub.dev/documentation/catalyst_cardano_serialization/latest/catalyst_cardano_serialization/catalyst_cardano_serialization-library.html) |✔️| ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | -| [`catalyst_analysis`](catalyst_analysis) | ![pub package](https://img.shields.io/pub/v/catalyst_analysis.svg) | [📖](https://pub.dev/documentation/catalyst_analysis/latest/) |✔️| ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | -| [`catalyst_cardano`](catalyst_cardano/catalyst_cardano) | ![pub package](https://img.shields.io/pub/v/catalyst_cardano.svg) | [📖](https://pub.dev/documentation/catalyst_cardano/latest/catalyst_cardano/catalyst_cardano-library.html) |✔️| ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | -| [`catalyst_cardano_platform_interface`](catalyst_cardano/catalyst_cardano_platform_interface) | ![pub package](https://img.shields.io/pub/v/catalyst_cardano_platform_interface.svg) | [📖](https://pub.dev/documentation/catalyst_cardano_platform_interface/latest/catalyst_cardano_platform_interface/catalyst_cardano_platform_interface-library.html) |✔️| ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | -| [`catalyst_cardano_web`](catalyst_cardano/catalyst_cardano_web) | ![pub package](https://img.shields.io/pub/v/catalyst_cardano_web.svg) | [📖](https://pub.dev/documentation/catalyst_cardano_web/latest/catalyst_cardano_web/catalyst_cardano_web-library.html) |N/A| N/A | ✔️ | N/A | N/A | N/A | N/A | -| [`catalyst_compression`](catalyst_compression/catalyst_compression) | ![pub package](https://img.shields.io/pub/v/catalyst_compression.svg) | [📖](https://pub.dev/documentation/catalyst_compression/latest/catalyst_compression/catalyst_compression-library.html) |✔️| ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | -| [`catalyst_compression_platform_interface`](catalyst_compression/catalyst_compression_platform_interface) | ![pub package](https://img.shields.io/pub/v/catalyst_compression_platform_interface.svg) | [📖](https://pub.dev/documentation/catalyst_compression_platform_interface/latest/catalyst_compression_platform_interface/catalyst_compression_platform_interface-library.html) |✔️| ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | -| [`catalyst_compression_web`](catalyst_compression/catalyst_compression_web) | ![pub package](https://img.shields.io/pub/v/catalyst_compression_web.svg) | [📖](https://pub.dev/documentation/catalyst_compression_web/latest/catalyst_compression_web/catalyst_compression_web-library.html) |N/A| N/A | ✔️ | N/A | N/A | N/A | N/A | +| [`catalyst_cardano_serialization`](catalyst_cardano_serialization) | ![pub package](https://img.shields.io/pub/v/catalyst_cardano_serialization.svg) | [📖](https://pub.dev/documentation/catalyst_cardano_serialization/latest/catalyst_cardano_serialization/catalyst_cardano_serialization-library.html) | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | +| [`catalyst_analysis`](catalyst_analysis) | ![pub package](https://img.shields.io/pub/v/catalyst_analysis.svg) | [📖](https://pub.dev/documentation/catalyst_analysis/latest/) | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | +| [`catalyst_cardano`](catalyst_cardano/catalyst_cardano) | ![pub package](https://img.shields.io/pub/v/catalyst_cardano.svg) | [📖](https://pub.dev/documentation/catalyst_cardano/latest/catalyst_cardano/catalyst_cardano-library.html) | N/A | N/A | ✔️ | N/A | N/A | N/A | N/A | +| [`catalyst_cardano_platform_interface`](catalyst_cardano/catalyst_cardano_platform_interface) | ![pub package](https://img.shields.io/pub/v/catalyst_cardano_platform_interface.svg) | [📖](https://pub.dev/documentation/catalyst_cardano_platform_interface/latest/catalyst_cardano_platform_interface/catalyst_cardano_platform_interface-library.html) | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | +| [`catalyst_cardano_web`](catalyst_cardano/catalyst_cardano_web) | ![pub package](https://img.shields.io/pub/v/catalyst_cardano_web.svg) | [📖](https://pub.dev/documentation/catalyst_cardano_web/latest/catalyst_cardano_web/catalyst_cardano_web-library.html) | N/A | N/A | ✔️ | N/A | N/A | N/A | N/A | +| [`catalyst_compression`](catalyst_compression/catalyst_compression) | ![pub package](https://img.shields.io/pub/v/catalyst_compression.svg) | [📖](https://pub.dev/documentation/catalyst_compression/latest/catalyst_compression/catalyst_compression-library.html) | N/A | N/A | ✔️ | N/A | N/A | N/A | N/A | +| [`catalyst_compression_platform_interface`](catalyst_compression/catalyst_compression_platform_interface) | ![pub package](https://img.shields.io/pub/v/catalyst_compression_platform_interface.svg) | [📖](https://pub.dev/documentation/catalyst_compression_platform_interface/latest/catalyst_compression_platform_interface/catalyst_compression_platform_interface-library.html) | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | +| [`catalyst_compression_web`](catalyst_compression/catalyst_compression_web) | ![pub package](https://img.shields.io/pub/v/catalyst_compression_web.svg) | [📖](https://pub.dev/documentation/catalyst_compression_web/latest/catalyst_compression_web/catalyst_compression_web-library.html) | N/A | N/A | ✔️ | N/A | N/A | N/A | N/A | +| [`catalyst_cose`](catalyst_cose) | ![pub package](https://img.shields.io/pub/v/catalyst_cose.svg) | [📖](https://pub.dev/documentation/catalyst_cose/latest/catalyst_cose/catalyst_cose-library.html) | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ## Requirements -* Flutter: 3.13.9+ -* Dart: 3.1.5+ +* Flutter: 3.24.1+ +* Dart: 3.5.0+ * Xcode: 15.0+ * Android Studio: Android Studio Electric Eel | 2022.1.1 + * [Melos](https://melos.invertase.dev) diff --git a/catalyst_voices_packages/catalyst_analysis/CHANGELOG.md b/catalyst_voices_packages/catalyst_analysis/CHANGELOG.md index 66af7ad701e..c917b34124b 100644 --- a/catalyst_voices_packages/catalyst_analysis/CHANGELOG.md +++ b/catalyst_voices_packages/catalyst_analysis/CHANGELOG.md @@ -1,3 +1,9 @@ +## 2.0.0 + +> Note: This release has breaking changes. + + - **BREAKING** **CHORE**: upgrade flutter to 3.24.1 and dart to 3.5.0 ([#725](https://github.com/input-output-hk/catalyst-voices/issues/725)). ([eb8a516e](https://github.com/input-output-hk/catalyst-voices/commit/eb8a516edbd25386c0fbe41501285870abf82543)) + ## 1.2.0 - **FIX**: readme versioning for catalyst packages ([#585](https://github.com/input-output-hk/catalyst-voices/issues/585)). ([e433b2db](https://github.com/input-output-hk/catalyst-voices/commit/e433b2dbba7a43c50f4411ea5279a623c221b66b)) diff --git a/catalyst_voices_packages/catalyst_analysis/README.md b/catalyst_voices_packages/catalyst_analysis/README.md index 3d640e6b59b..adad29feb53 100644 --- a/catalyst_voices_packages/catalyst_analysis/README.md +++ b/catalyst_voices_packages/catalyst_analysis/README.md @@ -31,10 +31,10 @@ include: package:catalyst_analysis/analysis_options.yaml ``` This will ensure you always use the latest version of the lints. -If you wish to restrict the lint version, specify a version of `analysis_options.yaml` instead: +If you wish to restrict the lint version, specify a version of `catalyst_analysis` in pubspec.yaml instead: ```yaml -include: package:catalyst_analysis/analysis_options.1.0.0.yaml +catalyst_analysis: ^1.0.0 ``` ## Suppressing Lints diff --git a/catalyst_voices_packages/catalyst_analysis/example/pubspec.yaml b/catalyst_voices_packages/catalyst_analysis/example/pubspec.yaml index c84d4d4aefc..677defd75d7 100644 --- a/catalyst_voices_packages/catalyst_analysis/example/pubspec.yaml +++ b/catalyst_voices_packages/catalyst_analysis/example/pubspec.yaml @@ -4,7 +4,7 @@ description: A project that showcases how to enable the recommended lints for Ca publish_to: none environment: - sdk: ">=3.3.4 <4.0.0" + sdk: ">=3.5.0 <4.0.0" dev_dependencies: catalyst_analysis: diff --git a/catalyst_voices_packages/catalyst_analysis/lib/analysis_options.1.0.0.yaml b/catalyst_voices_packages/catalyst_analysis/lib/analysis_options.1.0.0.yaml deleted file mode 100644 index 9cc2c40f1fb..00000000000 --- a/catalyst_voices_packages/catalyst_analysis/lib/analysis_options.1.0.0.yaml +++ /dev/null @@ -1,197 +0,0 @@ -analyzer: - language: - strict-casts: true - strict-inference: true - strict-raw-types: true - - exclude: - - test/.test_coverage.dart - - lib/generated_plugin_registrant.dart - -# The full list of lints: https://dart.dev/tools/linter-rules -linter: - rules: - - always_declare_return_types - - always_use_package_imports - - annotate_overrides - - annotate_redeclares - - avoid_bool_literals_in_conditional_expressions - - avoid_catching_errors - - avoid_double_and_int_checks - - avoid_dynamic_calls - - avoid_empty_else - - avoid_equals_and_hash_code_on_mutable_classes - - avoid_escaping_inner_quotes - - avoid_field_initializers_in_const_classes - - avoid_final_parameters - - avoid_function_literals_in_foreach_calls - - avoid_init_to_null - - avoid_js_rounded_ints - - avoid_multiple_declarations_per_line - - avoid_null_checks_in_equality_operators - - avoid_positional_boolean_parameters - - avoid_print - - avoid_private_typedef_functions - - avoid_redundant_argument_values - - avoid_relative_lib_imports - - avoid_renaming_method_parameters - - avoid_return_types_on_setters - - avoid_returning_null_for_future - - avoid_returning_null_for_void - - avoid_returning_this - - avoid_setters_without_getters - - avoid_shadowing_type_parameters - - avoid_single_cascade_in_expression_statements - - avoid_slow_async_io - - avoid_type_to_string - - avoid_types_as_parameter_names - - avoid_unnecessary_containers - - avoid_unused_constructor_parameters - - avoid_void_async - - avoid_web_libraries_in_flutter - - await_only_futures - - camel_case_extensions - - camel_case_types - - cancel_subscriptions - - cascade_invocations - - cast_nullable_to_non_nullable - - collection_methods_unrelated_type - - comment_references - - conditional_uri_does_not_exist - - constant_identifier_names - - control_flow_in_finally - - curly_braces_in_flow_control_structures - - dangling_library_doc_comments - - depend_on_referenced_packages - - deprecated_consistency - - directives_ordering - - discarded_futures - - empty_catches - - empty_constructor_bodies - - empty_statements - - eol_at_end_of_file - - exhaustive_cases - - file_names - - flutter_style_todos - - hash_and_equals - - implementation_imports - - implicit_reopen - - invalid_case_patterns - - join_return_with_assignment - - leading_newlines_in_multiline_strings - - library_names - - library_prefixes - - library_private_types_in_public_api - - lines_longer_than_80_chars - - literal_only_boolean_expressions - - missing_whitespace_between_adjacent_strings - - no_adjacent_strings_in_list - - no_default_cases - - no_duplicate_case_values - - no_leading_underscores_for_library_prefixes - - no_leading_underscores_for_local_identifiers - - no_logic_in_create_state - - no_runtimeType_toString - - no_self_assignments - - non_constant_identifier_names - - noop_primitive_operations - - null_check_on_nullable_type_parameter - - null_closures - - omit_local_variable_types - - one_member_abstracts - - only_throw_errors - - overridden_fields - - package_api_docs - - package_names - - package_prefixed_library_names - - parameter_assignments - - prefer_adjacent_string_concatenation - - prefer_asserts_in_initializer_lists - - prefer_asserts_with_message - - prefer_collection_literals - - prefer_conditional_assignment - - prefer_const_constructors - - prefer_const_constructors_in_immutables - - prefer_const_declarations - - prefer_const_literals_to_create_immutables - - prefer_constructors_over_static_methods - - prefer_contains - - prefer_final_fields - - prefer_final_in_for_each - - prefer_final_locals - - prefer_for_elements_to_map_fromIterable - - prefer_function_declarations_over_variables - - prefer_generic_function_type_aliases - - prefer_if_elements_to_conditional_expressions - - prefer_if_null_operators - - prefer_initializing_formals - - prefer_inlined_adds - - prefer_int_literals - - prefer_interpolation_to_compose_strings - - prefer_is_empty - - prefer_is_not_empty - - prefer_is_not_operator - - prefer_iterable_whereType - - prefer_null_aware_method_calls - - prefer_null_aware_operators - - prefer_single_quotes - - prefer_spread_collections - - prefer_typing_uninitialized_variables - - prefer_void_to_null - - provide_deprecation_message - - public_member_api_docs - - recursive_getters - - require_trailing_commas - - secure_pubspec_urls - - sized_box_for_whitespace - - sized_box_shrink_expand - - slash_for_doc_comments - - sort_child_properties_last - - sort_pub_dependencies - - sort_unnamed_constructors_first - - test_types_in_equals - - throw_in_finally - - tighten_type_of_initializing_formals - - type_annotate_public_apis - - type_init_formals - - unawaited_futures - - unnecessary_await_in_return - - unnecessary_brace_in_string_interps - - unnecessary_const - - unnecessary_constructor_name - - unnecessary_getters_setters - - unnecessary_lambdas - - unnecessary_late - - unnecessary_new - - unnecessary_null_aware_assignments - - unnecessary_null_checks - - unnecessary_null_in_if_null_operators - - unnecessary_nullable_for_final_variable_declarations - - unnecessary_overrides - - unnecessary_raw_strings - - unnecessary_statements - - unnecessary_string_escapes - - unnecessary_string_interpolations - - unnecessary_this - - unnecessary_to_list_in_spreads - - unrelated_type_equality_checks - - use_build_context_synchronously - - use_colored_box - - use_decorated_box - - use_enums - - use_full_hex_values_for_flutter_colors - - use_function_type_syntax_for_parameters - - use_if_null_to_convert_nulls_to_bools - - use_is_even_rather_than_modulo - - use_key_in_widget_constructors - - use_late_for_private_fields_and_variables - - use_named_constants - - use_raw_strings - - use_rethrow_when_possible - - use_setters_to_change_properties - - use_string_buffers - - use_super_parameters - - use_test_throws_matchers - - use_to_and_as_if_applicable - - valid_regexps - - void_checks \ No newline at end of file diff --git a/catalyst_voices_packages/catalyst_analysis/lib/analysis_options.yaml b/catalyst_voices_packages/catalyst_analysis/lib/analysis_options.yaml index 33c0663e284..ed64285ba04 100644 --- a/catalyst_voices_packages/catalyst_analysis/lib/analysis_options.yaml +++ b/catalyst_voices_packages/catalyst_analysis/lib/analysis_options.yaml @@ -1 +1,199 @@ -include: package:catalyst_analysis/analysis_options.1.0.0.yaml +analyzer: + language: + strict-casts: true + strict-inference: true + strict-raw-types: true + + exclude: + - test/.test_coverage.dart + - lib/generated_plugin_registrant.dart + +# The full list of lints: https://dart.dev/tools/linter-rules +linter: + rules: + - always_declare_return_types + - always_use_package_imports + - annotate_overrides + - annotate_redeclares + - avoid_bool_literals_in_conditional_expressions + - avoid_catching_errors + - avoid_double_and_int_checks + - avoid_dynamic_calls + - avoid_empty_else + - avoid_equals_and_hash_code_on_mutable_classes + - avoid_escaping_inner_quotes + - avoid_field_initializers_in_const_classes + - avoid_final_parameters + - avoid_function_literals_in_foreach_calls + - avoid_init_to_null + - avoid_js_rounded_ints + - avoid_multiple_declarations_per_line + - avoid_null_checks_in_equality_operators + - avoid_positional_boolean_parameters + - avoid_print + - avoid_private_typedef_functions + # TODO(dtscalac) below lint rule crashes in dart 3.5.0+, + # disabled until https://github.com/dart-lang/sdk/issues/56561 is addressed + - avoid_redundant_argument_values: false + - avoid_relative_lib_imports + - avoid_renaming_method_parameters + - avoid_return_types_on_setters + - avoid_returning_null_for_future + - avoid_returning_null_for_void + - avoid_returning_this + - avoid_setters_without_getters + - avoid_shadowing_type_parameters + - avoid_single_cascade_in_expression_statements + - avoid_slow_async_io + - avoid_type_to_string + - avoid_types_as_parameter_names + - avoid_unnecessary_containers + - avoid_unused_constructor_parameters + - avoid_void_async + - avoid_web_libraries_in_flutter + - await_only_futures + - camel_case_extensions + - camel_case_types + - cancel_subscriptions + - cascade_invocations + - cast_nullable_to_non_nullable + - collection_methods_unrelated_type + - comment_references + - conditional_uri_does_not_exist + - constant_identifier_names + - control_flow_in_finally + - curly_braces_in_flow_control_structures + - dangling_library_doc_comments + - depend_on_referenced_packages + - deprecated_consistency + - directives_ordering + - discarded_futures + - empty_catches + - empty_constructor_bodies + - empty_statements + - eol_at_end_of_file + - exhaustive_cases + - file_names + - flutter_style_todos + - hash_and_equals + - implementation_imports + - implicit_reopen + - invalid_case_patterns + - join_return_with_assignment + - leading_newlines_in_multiline_strings + - library_names + - library_prefixes + - library_private_types_in_public_api + - lines_longer_than_80_chars + - literal_only_boolean_expressions + - missing_whitespace_between_adjacent_strings + - no_adjacent_strings_in_list + - no_default_cases + - no_duplicate_case_values + - no_leading_underscores_for_library_prefixes + - no_leading_underscores_for_local_identifiers + - no_logic_in_create_state + - no_runtimeType_toString + - no_self_assignments + - non_constant_identifier_names + - noop_primitive_operations + - null_check_on_nullable_type_parameter + - null_closures + - omit_local_variable_types + - one_member_abstracts + - only_throw_errors + - overridden_fields + - package_api_docs + - package_names + - package_prefixed_library_names + - parameter_assignments + - prefer_adjacent_string_concatenation + - prefer_asserts_in_initializer_lists + - prefer_asserts_with_message + - prefer_collection_literals + - prefer_conditional_assignment + - prefer_const_constructors + - prefer_const_constructors_in_immutables + - prefer_const_declarations + - prefer_const_literals_to_create_immutables + - prefer_constructors_over_static_methods + - prefer_contains + - prefer_final_fields + - prefer_final_in_for_each + - prefer_final_locals + - prefer_for_elements_to_map_fromIterable + - prefer_function_declarations_over_variables + - prefer_generic_function_type_aliases + - prefer_if_elements_to_conditional_expressions + - prefer_if_null_operators + - prefer_initializing_formals + - prefer_inlined_adds + - prefer_int_literals + - prefer_interpolation_to_compose_strings + - prefer_is_empty + - prefer_is_not_empty + - prefer_is_not_operator + - prefer_iterable_whereType + - prefer_null_aware_method_calls + - prefer_null_aware_operators + - prefer_single_quotes + - prefer_spread_collections + - prefer_typing_uninitialized_variables + - prefer_void_to_null + - provide_deprecation_message + - public_member_api_docs + - recursive_getters + - require_trailing_commas + - secure_pubspec_urls + - sized_box_for_whitespace + - sized_box_shrink_expand + - slash_for_doc_comments + - sort_child_properties_last + - sort_pub_dependencies + - sort_unnamed_constructors_first + - test_types_in_equals + - throw_in_finally + - tighten_type_of_initializing_formals + - type_annotate_public_apis + - type_init_formals + - unawaited_futures + - unnecessary_await_in_return + - unnecessary_brace_in_string_interps + - unnecessary_const + - unnecessary_constructor_name + - unnecessary_getters_setters + - unnecessary_lambdas + - unnecessary_late + - unnecessary_new + - unnecessary_null_aware_assignments + - unnecessary_null_checks + - unnecessary_null_in_if_null_operators + - unnecessary_nullable_for_final_variable_declarations + - unnecessary_overrides + - unnecessary_raw_strings + - unnecessary_statements + - unnecessary_string_escapes + - unnecessary_string_interpolations + - unnecessary_this + - unnecessary_to_list_in_spreads + - unrelated_type_equality_checks + - use_build_context_synchronously + - use_colored_box + - use_decorated_box + - use_enums + - use_full_hex_values_for_flutter_colors + - use_function_type_syntax_for_parameters + - use_if_null_to_convert_nulls_to_bools + - use_is_even_rather_than_modulo + - use_key_in_widget_constructors + - use_late_for_private_fields_and_variables + - use_named_constants + - use_raw_strings + - use_rethrow_when_possible + - use_setters_to_change_properties + - use_string_buffers + - use_super_parameters + - use_test_throws_matchers + - use_to_and_as_if_applicable + - valid_regexps + - void_checks \ No newline at end of file diff --git a/catalyst_voices_packages/catalyst_analysis/pubspec.yaml b/catalyst_voices_packages/catalyst_analysis/pubspec.yaml index 4c876d8ed74..bf04681fb2b 100644 --- a/catalyst_voices_packages/catalyst_analysis/pubspec.yaml +++ b/catalyst_voices_packages/catalyst_analysis/pubspec.yaml @@ -1,9 +1,9 @@ name: catalyst_analysis -version: 1.2.0 +version: 2.0.0 description: Lint rules for Dart and Flutter used internally at Catalyst. repository: https://github.com/input-output-hk/catalyst-voices/tree/main/catalyst_voices_packages/catalyst_analysis issue_tracker: https://github.com/input-output-hk/catalyst-voices/issues topics: [lints, analyzer, analysis] environment: - sdk: ">=3.3.4 <4.0.0" + sdk: ">=3.5.0 <4.0.0" diff --git a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/CHANGELOG.md b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/CHANGELOG.md index 27e79d814fc..a9407e821fc 100644 --- a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/CHANGELOG.md +++ b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/CHANGELOG.md @@ -1,3 +1,24 @@ +## 0.3.0 + +> Note: This release has breaking changes. + + - **FIX**: catalyst cardano null utxos ([#746](https://github.com/input-output-hk/catalyst-voices/issues/746)). ([3f2f5925](https://github.com/input-output-hk/catalyst-voices/commit/3f2f592593efe306f85fb0e81ce07aa1ea90b7b6)) + - **FIX**: rbac txn inputs hash ([#688](https://github.com/input-output-hk/catalyst-voices/issues/688)). ([b644026f](https://github.com/input-output-hk/catalyst-voices/commit/b644026fa3b675591d071819eda185365257f0d1)) + - **BREAKING** **FEAT**: add initial support for Cardano Native scripts, Plutus scripts, advanced transaction outputs, and additional transaction body fields and witnesses ([#713](https://github.com/input-output-hk/catalyst-voices/issues/713)). ([74fcb725](https://github.com/input-output-hk/catalyst-voices/commit/74fcb725f221bb3acf3824a3dd18a073d0a321e0)) + - **BREAKING** **CHORE**: upgrade flutter to 3.24.1 and dart to 3.5.0 ([#725](https://github.com/input-output-hk/catalyst-voices/issues/725)). ([eb8a516e](https://github.com/input-output-hk/catalyst-voices/commit/eb8a516edbd25386c0fbe41501285870abf82543)) + +## 0.2.0+1 + + - Update a dependency to the latest release. + +## 0.2.0 + +> Note: This release has breaking changes. + + - **BREAKING** **FIX**: signData return type should be DataSignature, not VkeyWitness ([#647](https://github.com/input-output-hk/catalyst-voices/issues/647)). ([69dba1d2](https://github.com/input-output-hk/catalyst-voices/commit/69dba1d24022eb77cc03ac670dda0da047304766)) + - **BREAKING** **FIX**: X509 registration metadata encoding ([#640](https://github.com/input-output-hk/catalyst-voices/issues/640)). ([c45a2ac9](https://github.com/input-output-hk/catalyst-voices/commit/c45a2ac96b34c4215352ece5ef9bd2fa73b591e8)) + - **BREAKING** **FEAT**: COSE_SIGN1 signatures and verification ([#669](https://github.com/input-output-hk/catalyst-voices/issues/669)). ([f5a910ef](https://github.com/input-output-hk/catalyst-voices/commit/f5a910efe36442171521b9ec429aed4a46e05b83)) + ## 0.1.3 - **FIX**: catalyst cardano integration with Typhoon wallet ([#636](https://github.com/input-output-hk/catalyst-voices/issues/636)). ([2c6e270d](https://github.com/input-output-hk/catalyst-voices/commit/2c6e270ddcb95389ac417ffe5f60ccabc04b5931)) diff --git a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/README.md b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/README.md index 68e09df43f6..b089a8e8500 100644 --- a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/README.md +++ b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/README.md @@ -22,8 +22,8 @@ away all the complexities of using js_interop. ## Requirements -* Dart: 3.3.4+ -* Flutter: 3.22.1+ +* Dart: 3.5.0+ +* Flutter: 3.24.1+ ## Install diff --git a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/analysis_options.yaml b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/analysis_options.yaml index 886855b51aa..a2316514a4b 100644 --- a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/analysis_options.yaml +++ b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/analysis_options.yaml @@ -1,4 +1,4 @@ -include: package:catalyst_analysis/analysis_options.1.0.0.yaml +include: package:catalyst_analysis/analysis_options.yaml analyzer: exclude: [build/**, lib/*.g.dart, lib/generated/**] diff --git a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/example/analysis_options.yaml b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/example/analysis_options.yaml index d5015346bf1..eb691624826 100644 --- a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/example/analysis_options.yaml +++ b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/example/analysis_options.yaml @@ -1,4 +1,4 @@ -include: package:catalyst_analysis/analysis_options.1.0.0.yaml +include: package:catalyst_analysis/analysis_options.yaml analyzer: exclude: [ diff --git a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/example/lib/sign_and_submit_rbac_tx.dart b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/example/lib/sign_and_submit_rbac_tx.dart index a3a957a2b4c..9a40fffac96 100644 --- a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/example/lib/sign_and_submit_rbac_tx.dart +++ b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/example/lib/sign_and_submit_rbac_tx.dart @@ -46,7 +46,7 @@ E61E8EE7D77E9F7F9804E03EBC31B458 final _c509Cert = C509Certificate.fromHex( ''' -8B004301F50D6B524643207465737420 +004301F50D6B524643207465737420 43411A63B0CD001A6955B90047010123 456789AB01582102B1216AB96E5B3B33 40F5BDF02E693F16213A04525ED44450 @@ -80,8 +80,12 @@ Future _signAndSubmitRbacTx({ ), ); + if (utxos.isEmpty) { + throw Exception('Insufficient balance, please top up your wallet'); + } + final x509Envelope = await _buildMetadataEnvelope( - inputs: utxos, + utxos: utxos, ); final auxiliaryData = AuxiliaryData.fromCbor( @@ -119,14 +123,14 @@ Future _signAndSubmitRbacTx({ } Future> _buildMetadataEnvelope({ - required List inputs, + required List utxos, }) async { final keyPair = await Ed25519KeyPair.fromSeed(Ed25519PrivateKey.seeded(0).bytes); final x509Envelope = X509MetadataEnvelope.unsigned( purpose: UuidV4.fromString(_catalystUserRoleRegistrationPurpose), - txInputsHash: TransactionInputsHash.fromTransactionInputs(inputs), + txInputsHash: TransactionInputsHash.fromTransactionInputs(utxos), previousTransactionId: _transactionHash, chunkedData: RegistrationData( derCerts: [_derCert], @@ -205,7 +209,7 @@ Transaction _buildUnsignedRbacTx({ return Transaction( body: txBody, isValid: true, - witnessSet: const TransactionWitnessSet(vkeyWitnesses: {}), + witnessSet: const TransactionWitnessSet(), auxiliaryData: auxiliaryData, ); } diff --git a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/example/lib/sign_and_submit_tx.dart b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/example/lib/sign_and_submit_tx.dart index 22bdc078912..47a07f8fd2b 100644 --- a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/example/lib/sign_and_submit_tx.dart +++ b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/example/lib/sign_and_submit_tx.dart @@ -14,6 +14,10 @@ Future _signAndSubmitTx({ ), ); + if (utxos.isEmpty) { + throw Exception('Insufficient balance, please top up your wallet'); + } + final unsignedTx = _buildUnsignedTx( utxos: utxos, changeAddress: changeAddress, @@ -71,7 +75,7 @@ Transaction _buildUnsignedTx({ return Transaction( body: txBody, isValid: true, - witnessSet: const TransactionWitnessSet(vkeyWitnesses: {}), + witnessSet: const TransactionWitnessSet(), ); } diff --git a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/example/pubspec.yaml b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/example/pubspec.yaml index 60102451113..f03063d4075 100644 --- a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/example/pubspec.yaml +++ b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/example/pubspec.yaml @@ -5,15 +5,15 @@ publish_to: "none" version: 1.0.0+1 environment: - sdk: ">=3.3.4 <4.0.0" - flutter: ">=3.22.1" + sdk: ">=3.5.0 <4.0.0" + flutter: ">=3.24.1" dependencies: - catalyst_cardano: ^0.1.3 - catalyst_cardano_serialization: ^0.1.3 - catalyst_cardano_web: ^0.1.3 - catalyst_compression: ^0.1.1 - catalyst_compression_web: ^0.1.1 + catalyst_cardano: ^0.3.0 + catalyst_cardano_serialization: ^0.4.0 + catalyst_cardano_web: ^0.3.0 + catalyst_compression: ^0.3.0 + catalyst_compression_web: ^0.3.0 cbor: ^6.2.0 convert: ^3.1.1 cupertino_icons: ^1.0.6 @@ -21,8 +21,7 @@ dependencies: sdk: flutter dev_dependencies: - catalyst_analysis: - path: ../../../catalyst_analysis + catalyst_analysis: ^2.0.0 flutter_test: sdk: flutter diff --git a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/lib/src/catalyst_cardano.dart b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/lib/src/catalyst_cardano.dart index 1876de7499a..d1418ec2a29 100644 --- a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/lib/src/catalyst_cardano.dart +++ b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/lib/src/catalyst_cardano.dart @@ -2,7 +2,7 @@ import 'package:catalyst_cardano_platform_interface/catalyst_cardano_platform_in /// A Flutter plugin exposing the [CIP-30](https://cips.cardano.org/cip/CIP-30) /// and [CIP-95](https://cips.cardano.org/cip/CIP-95) APIs. -class CatalystCardano { +final class CatalystCardano { /// The default instance of [CatalystCardano] to use. static final CatalystCardano instance = CatalystCardano._(); diff --git a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/pubspec.yaml b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/pubspec.yaml index 3dc556b1719..4dd6fe31ebe 100644 --- a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/pubspec.yaml +++ b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/pubspec.yaml @@ -3,11 +3,11 @@ description: A Flutter plugin exposing the CIP-30 and CIP-95 APIs. Allows to com repository: https://github.com/input-output-hk/catalyst-voices/tree/main/catalyst_voices_packages/catalyst_cardano/catalyst_cardano issue_tracker: https://github.com/input-output-hk/catalyst-voices/issues topics: [blockchain, cardano, cryptocurrency, wallet] -version: 0.1.3 +version: 0.3.0 environment: - sdk: ">=3.3.4 <4.0.0" - flutter: ">=3.22.1" + sdk: ">=3.5.0 <4.0.0" + flutter: ">=3.24.1" flutter: plugin: @@ -16,15 +16,14 @@ flutter: default_package: catalyst_cardano_web dependencies: - catalyst_cardano_platform_interface: ^0.1.3 - catalyst_cardano_serialization: ^0.1.3 - catalyst_cardano_web: ^0.1.3 + catalyst_cardano_platform_interface: ^0.3.0 + catalyst_cardano_serialization: ^0.4.0 + catalyst_cardano_web: ^0.3.0 flutter: sdk: flutter dev_dependencies: - catalyst_analysis: - path: ../../catalyst_analysis + catalyst_analysis: ^2.0.0 flutter_test: sdk: flutter plugin_platform_interface: ^2.1.7 diff --git a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_platform_interface/CHANGELOG.md b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_platform_interface/CHANGELOG.md index d6a14bc7d9f..f2ee1a3daa3 100644 --- a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_platform_interface/CHANGELOG.md +++ b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_platform_interface/CHANGELOG.md @@ -1,3 +1,19 @@ +## 0.3.0 + +> Note: This release has breaking changes. + + - **BREAKING** **CHORE**: upgrade flutter to 3.24.1 and dart to 3.5.0 ([#725](https://github.com/input-output-hk/catalyst-voices/issues/725)). ([eb8a516e](https://github.com/input-output-hk/catalyst-voices/commit/eb8a516edbd25386c0fbe41501285870abf82543)) + +## 0.2.0+1 + + - Update a dependency to the latest release. + +## 0.2.0 + +> Note: This release has breaking changes. + + - **BREAKING** **FIX**: signData return type should be DataSignature, not VkeyWitness ([#647](https://github.com/input-output-hk/catalyst-voices/issues/647)). ([69dba1d2](https://github.com/input-output-hk/catalyst-voices/commit/69dba1d24022eb77cc03ac670dda0da047304766)) + ## 0.1.3 - **FIX**: catalyst cardano integration with Typhoon wallet ([#636](https://github.com/input-output-hk/catalyst-voices/issues/636)). ([2c6e270d](https://github.com/input-output-hk/catalyst-voices/commit/2c6e270ddcb95389ac417ffe5f60ccabc04b5931)) diff --git a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_platform_interface/analysis_options.yaml b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_platform_interface/analysis_options.yaml index 886855b51aa..a2316514a4b 100644 --- a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_platform_interface/analysis_options.yaml +++ b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_platform_interface/analysis_options.yaml @@ -1,4 +1,4 @@ -include: package:catalyst_analysis/analysis_options.1.0.0.yaml +include: package:catalyst_analysis/analysis_options.yaml analyzer: exclude: [build/**, lib/*.g.dart, lib/generated/**] diff --git a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_platform_interface/pubspec.yaml b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_platform_interface/pubspec.yaml index baef21938e8..9c45e17a5c3 100644 --- a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_platform_interface/pubspec.yaml +++ b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_platform_interface/pubspec.yaml @@ -3,21 +3,20 @@ description: A common platform interface for the catalyst_cardano plugin. repository: https://github.com/input-output-hk/catalyst-voices/tree/main/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_platform_interface issue_tracker: https://github.com/input-output-hk/catalyst-voices/issues topics: [blockchain, cardano, cryptocurrency, wallet] -version: 0.1.3 +version: 0.3.0 environment: - sdk: ">=3.3.4 <4.0.0" - flutter: ">=3.22.1" + sdk: ">=3.5.0 <4.0.0" + flutter: ">=3.24.1" dependencies: - catalyst_cardano_serialization: ^0.1.3 + catalyst_cardano_serialization: ^0.4.0 equatable: ^2.0.5 flutter: sdk: flutter plugin_platform_interface: ^2.1.7 dev_dependencies: - catalyst_analysis: - path: ../../catalyst_analysis + catalyst_analysis: ^2.0.0 flutter_test: sdk: flutter diff --git a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_web/CHANGELOG.md b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_web/CHANGELOG.md index d6a14bc7d9f..b8dd22d1795 100644 --- a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_web/CHANGELOG.md +++ b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_web/CHANGELOG.md @@ -1,3 +1,21 @@ +## 0.3.0 + +> Note: This release has breaking changes. + + - **FIX**: catalyst cardano null utxos ([#746](https://github.com/input-output-hk/catalyst-voices/issues/746)). ([3f2f5925](https://github.com/input-output-hk/catalyst-voices/commit/3f2f592593efe306f85fb0e81ce07aa1ea90b7b6)) + - **BREAKING** **CHORE**: upgrade flutter to 3.24.1 and dart to 3.5.0 ([#725](https://github.com/input-output-hk/catalyst-voices/issues/725)). ([eb8a516e](https://github.com/input-output-hk/catalyst-voices/commit/eb8a516edbd25386c0fbe41501285870abf82543)) + +## 0.2.0+1 + + - Update a dependency to the latest release. + +## 0.2.0 + +> Note: This release has breaking changes. + + - **BREAKING** **FIX**: signData return type should be DataSignature, not VkeyWitness ([#647](https://github.com/input-output-hk/catalyst-voices/issues/647)). ([69dba1d2](https://github.com/input-output-hk/catalyst-voices/commit/69dba1d24022eb77cc03ac670dda0da047304766)) + - **BREAKING** **FEAT**: COSE_SIGN1 signatures and verification ([#669](https://github.com/input-output-hk/catalyst-voices/issues/669)). ([f5a910ef](https://github.com/input-output-hk/catalyst-voices/commit/f5a910efe36442171521b9ec429aed4a46e05b83)) + ## 0.1.3 - **FIX**: catalyst cardano integration with Typhoon wallet ([#636](https://github.com/input-output-hk/catalyst-voices/issues/636)). ([2c6e270d](https://github.com/input-output-hk/catalyst-voices/commit/2c6e270ddcb95389ac417ffe5f60ccabc04b5931)) diff --git a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_web/analysis_options.yaml b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_web/analysis_options.yaml index 886855b51aa..a2316514a4b 100644 --- a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_web/analysis_options.yaml +++ b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_web/analysis_options.yaml @@ -1,4 +1,4 @@ -include: package:catalyst_analysis/analysis_options.1.0.0.yaml +include: package:catalyst_analysis/analysis_options.yaml analyzer: exclude: [build/**, lib/*.g.dart, lib/generated/**] diff --git a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_web/lib/catalyst_cardano_web.dart b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_web/lib/catalyst_cardano_web.dart index 23533d2b4ac..66acca4e1b5 100644 --- a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_web/lib/catalyst_cardano_web.dart +++ b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_web/lib/catalyst_cardano_web.dart @@ -10,7 +10,7 @@ import 'package:flutter_web_plugins/flutter_web_plugins.dart' show Registrar; /// /// This class implements the `package:catalyst_cardano` functionality /// for the web. -class CatalystCardanoWeb extends CatalystCardanoPlatform { +final class CatalystCardanoWeb extends CatalystCardanoPlatform { /// A constructor that allows tests to override the window object used by the /// plugin. CatalystCardanoWeb(); diff --git a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_web/lib/src/interop/catalyst_cardano_interop.dart b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_web/lib/src/interop/catalyst_cardano_interop.dart index 835fceff1d9..43fbaafc34b 100644 --- a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_web/lib/src/interop/catalyst_cardano_interop.dart +++ b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_web/lib/src/interop/catalyst_cardano_interop.dart @@ -73,7 +73,7 @@ extension type JSCardanoWalletApi(JSObject _) implements JSObject { ]); /// See [CardanoWalletApi.getUtxos]. - external JSPromise> getUtxos([ + external JSPromise>? getUtxos([ JSAny? amount, JSAny? paginate, ]); diff --git a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_web/lib/src/interop/catalyst_cardano_wallet_proxy.dart b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_web/lib/src/interop/catalyst_cardano_wallet_proxy.dart index ce14d5775c2..573ba46361b 100644 --- a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_web/lib/src/interop/catalyst_cardano_wallet_proxy.dart +++ b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_web/lib/src/interop/catalyst_cardano_wallet_proxy.dart @@ -176,23 +176,24 @@ class JSCardanoWalletApiProxy implements CardanoWalletApi { Paginate? paginate, }) async { try { - return await _delegate - .getUtxos( - amount != null - ? hex.encode(cbor.encode(amount.toCbor())).toJS - : makeUndefined(), - paginate != null ? JSPaginate.fromDart(paginate) : makeUndefined(), - ) - .toDart - .then( - (array) => array.toDart - .map( - (item) => TransactionUnspentOutput.fromCbor( - cbor.decode(hex.decode(item.toDart)), - ), - ) - .toList(), - ); + final utxos = _delegate.getUtxos( + amount != null + ? hex.encode(cbor.encode(amount.toCbor())).toJS + : makeUndefined(), + paginate != null ? JSPaginate.fromDart(paginate) : makeUndefined(), + ); + + if (utxos == null) return []; + + return await utxos.toDart.then( + (array) => array.toDart + .map( + (item) => TransactionUnspentOutput.fromCbor( + cbor.decode(hex.decode(item.toDart)), + ), + ) + .toList(), + ); } catch (ex) { throw _mapApiException(ex) ?? _mapPaginateException(ex) ?? diff --git a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_web/pubspec.yaml b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_web/pubspec.yaml index b28edc1c3a2..58f5a4158dc 100644 --- a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_web/pubspec.yaml +++ b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_web/pubspec.yaml @@ -3,11 +3,11 @@ description: Web platform implementation of catalyst_cardano. Allows to communic repository: https://github.com/input-output-hk/catalyst-voices/tree/main/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_web issue_tracker: https://github.com/input-output-hk/catalyst-voices/issues topics: [blockchain, cardano, cryptocurrency, wallet] -version: 0.1.3 +version: 0.3.0 environment: - sdk: ">=3.3.4 <4.0.0" - flutter: ">=3.22.1" + sdk: ">=3.5.0 <4.0.0" + flutter: ">=3.24.1" flutter: plugin: @@ -21,8 +21,8 @@ flutter: - assets/js/ dependencies: - catalyst_cardano_platform_interface: ^0.1.3 - catalyst_cardano_serialization: ^0.1.3 + catalyst_cardano_platform_interface: ^0.3.0 + catalyst_cardano_serialization: ^0.4.0 cbor: ^6.2.0 convert: ^3.1.1 flutter: @@ -32,7 +32,6 @@ dependencies: web: ^0.5.0 dev_dependencies: - catalyst_analysis: - path: ../../catalyst_analysis + catalyst_analysis: ^2.0.0 flutter_test: sdk: flutter diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/CHANGELOG.md b/catalyst_voices_packages/catalyst_cardano_serialization/CHANGELOG.md index 3e2409ce994..c11491ca454 100644 --- a/catalyst_voices_packages/catalyst_cardano_serialization/CHANGELOG.md +++ b/catalyst_voices_packages/catalyst_cardano_serialization/CHANGELOG.md @@ -1,3 +1,28 @@ +## 0.4.0 + +> Note: This release has breaking changes. + + - **FIX**: to be signed message should be a plain cbor sequence ([#701](https://github.com/input-output-hk/catalyst-voices/issues/701)). ([7c2dec6e](https://github.com/input-output-hk/catalyst-voices/commit/7c2dec6e2f91c1f18a39e7646ee3a5ca6a6e7249)) + - **FIX**: rbac txn inputs hash ([#688](https://github.com/input-output-hk/catalyst-voices/issues/688)). ([b644026f](https://github.com/input-output-hk/catalyst-voices/commit/b644026fa3b675591d071819eda185365257f0d1)) + - **FEAT**(catalyst_cardano_serialization): add CborEncodable interface for standardized CBOR handling ([#696](https://github.com/input-output-hk/catalyst-voices/issues/696)). ([4222926f](https://github.com/input-output-hk/catalyst-voices/commit/4222926f028460ddb100008806fe39a38ac3511c)) + - **BREAKING** **FIX**: required signers are the hash of the public key, not the public key itself ([#703](https://github.com/input-output-hk/catalyst-voices/issues/703)). ([a63c4686](https://github.com/input-output-hk/catalyst-voices/commit/a63c4686ee6e79aa65ace0cb4ed9b0c91e994320)) + - **BREAKING** **FEAT**: add initial support for Cardano Native scripts, Plutus scripts, advanced transaction outputs, and additional transaction body fields and witnesses ([#713](https://github.com/input-output-hk/catalyst-voices/issues/713)). ([74fcb725](https://github.com/input-output-hk/catalyst-voices/commit/74fcb725f221bb3acf3824a3dd18a073d0a321e0)) + - **BREAKING** **CHORE**: upgrade flutter to 3.24.1 and dart to 3.5.0 ([#725](https://github.com/input-output-hk/catalyst-voices/issues/725)). ([eb8a516e](https://github.com/input-output-hk/catalyst-voices/commit/eb8a516edbd25386c0fbe41501285870abf82543)) + +## 0.3.0 + +> Note: This release has breaking changes. + + - **BREAKING** **FIX**: cardano serialization must depend on flutter ([#679](https://github.com/input-output-hk/catalyst-voices/issues/679)). ([b7d5276b](https://github.com/input-output-hk/catalyst-voices/commit/b7d5276b238b4c7273997b004465e2ffb29f8436)) + +## 0.2.0 + +> Note: This release has breaking changes. + + - **FEAT**: add catv1 auth token generator ([#671](https://github.com/input-output-hk/catalyst-voices/issues/671)). ([79efc828](https://github.com/input-output-hk/catalyst-voices/commit/79efc82800a7e6aca3e8516bbb4866bd502e2f36)) + - **BREAKING** **FIX**: X509 registration metadata encoding ([#640](https://github.com/input-output-hk/catalyst-voices/issues/640)). ([c45a2ac9](https://github.com/input-output-hk/catalyst-voices/commit/c45a2ac96b34c4215352ece5ef9bd2fa73b591e8)) + - **BREAKING** **FEAT**: update transactions inputs hash size ([#643](https://github.com/input-output-hk/catalyst-voices/issues/643)). ([a729823d](https://github.com/input-output-hk/catalyst-voices/commit/a729823d9b2e0c369456f8e99f5b776f046e6d1f)) + ## 0.1.3 - **FEAT**: rbac metadata envelope ([#630](https://github.com/input-output-hk/catalyst-voices/issues/630)). ([150d5676](https://github.com/input-output-hk/catalyst-voices/commit/150d567636c4281c092d020d51882e638b16beb5)) diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/README.md b/catalyst_voices_packages/catalyst_cardano_serialization/README.md index bbdf1d6e9ab..7354c3eccef 100644 --- a/catalyst_voices_packages/catalyst_cardano_serialization/README.md +++ b/catalyst_voices_packages/catalyst_cardano_serialization/README.md @@ -23,8 +23,8 @@ and submission are outside of scope of this package. ## Requirements -* Dart: 3.3.4+ -* Flutter: 3.22.1+ +* Dart: 3.5.0+ +* Flutter: 3.24.1+ ## Install diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/analysis_options.yaml b/catalyst_voices_packages/catalyst_cardano_serialization/analysis_options.yaml index 886855b51aa..a2316514a4b 100644 --- a/catalyst_voices_packages/catalyst_cardano_serialization/analysis_options.yaml +++ b/catalyst_voices_packages/catalyst_cardano_serialization/analysis_options.yaml @@ -1,4 +1,4 @@ -include: package:catalyst_analysis/analysis_options.1.0.0.yaml +include: package:catalyst_analysis/analysis_options.yaml analyzer: exclude: [build/**, lib/*.g.dart, lib/generated/**] diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/example/main.dart b/catalyst_voices_packages/catalyst_cardano_serialization/example/main.dart index db39edb3fbc..bb01322031d 100644 --- a/catalyst_voices_packages/catalyst_cardano_serialization/example/main.dart +++ b/catalyst_voices_packages/catalyst_cardano_serialization/example/main.dart @@ -81,9 +81,7 @@ void main() { final unsignedTx = Transaction( body: txBody, isValid: true, - witnessSet: const TransactionWitnessSet( - vkeyWitnesses: {}, - ), + witnessSet: const TransactionWitnessSet(), ); final witnessSet = _signTransaction(unsignedTx); diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/lib/catalyst_cardano_serialization.dart b/catalyst_voices_packages/catalyst_cardano_serialization/lib/catalyst_cardano_serialization.dart index 7c7607b504e..0a57c968881 100644 --- a/catalyst_voices_packages/catalyst_cardano_serialization/lib/catalyst_cardano_serialization.dart +++ b/catalyst_voices_packages/catalyst_cardano_serialization/lib/catalyst_cardano_serialization.dart @@ -3,13 +3,18 @@ export 'src/builders/transaction_builder.dart'; export 'src/builders/witness_builder.dart'; export 'src/certificate.dart'; export 'src/cip95/drep.dart'; +export 'src/datum.dart'; export 'src/exceptions.dart'; export 'src/fees.dart'; export 'src/hashes.dart'; +export 'src/rbac/auth_token.dart'; export 'src/rbac/registration_data.dart'; export 'src/rbac/x509_metadata_envelope.dart'; +export 'src/redeemer.dart'; +export 'src/scripts.dart'; export 'src/signature.dart'; export 'src/transaction.dart'; +export 'src/transaction_output.dart'; export 'src/types.dart'; export 'src/utils/uuid.dart'; export 'src/witness.dart'; diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/address.dart b/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/address.dart index 5c2473fa77a..34f1b453983 100644 --- a/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/address.dart +++ b/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/address.dart @@ -10,7 +10,7 @@ import 'package:cbor/cbor.dart'; import 'package:equatable/equatable.dart'; /// [ShelleyAddress] supports bech32 encoded addresses as defined in CIP-19. -class ShelleyAddress extends Equatable { +class ShelleyAddress extends Equatable implements CborEncodable { /// The prefix of a base address. static const String defaultAddrHrp = 'addr'; @@ -72,6 +72,7 @@ class ShelleyAddress extends Equatable { } /// Serializes the type as cbor. + @override CborValue toCbor() => CborBytes(bytes); /// Returns the [NetworkId] related to this address. diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/builders/transaction_builder.dart b/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/builders/transaction_builder.dart index 518f4d827ab..b6470a4676a 100644 --- a/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/builders/transaction_builder.dart +++ b/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/builders/transaction_builder.dart @@ -23,7 +23,7 @@ final class TransactionBuilder extends Equatable { /// The list of transaction outputs which describes which address /// will receive what amount of [Coin]. - final List outputs; + final List outputs; /// The amount of lovelaces that will be charged as the fee /// for adding the transaction to the blockchain. @@ -40,7 +40,7 @@ final class TransactionBuilder extends Equatable { /// The list of public key hashes of addresses /// that are required to sign the transaction. - final Set? requiredSigners; + final Set? requiredSigners; /// Specifies on which network the code will run. final NetworkId? networkId; @@ -74,14 +74,15 @@ final class TransactionBuilder extends Equatable { /// Since in a Cardano transaction the input amount must match the output /// amount plus fee, the method must ensure that there are no unspent utxos. /// - /// The algorithm first tries to create a [TransactionOutput] which will - /// transfer any remaining [Coin] back to the [address]. The [address] - /// should be the change address of the wallet initiating the transaction. + /// The algorithm first tries to create a [ShelleyMultiAssetTransactionOutput] + /// which will transfer any remaining [Coin] back to the [address]. + /// The [address] should be the change address of the wallet initiating the + /// transaction. /// - /// If creating an extra [TransactionOutput] is not possible because - /// i.e. the remaining change is too small to cover for extra fee that such - /// extra output would generate then the transaction fee is increased to burn - /// any remaining change. + /// If creating an extra [ShelleyMultiAssetTransactionOutput] is not possible + /// because i.e. the remaining change is too small to cover for extra fee that + /// such extra output would generate then the transaction fee is increased to + /// burn any remaining change. /// /// Follows code style of Cardano Multiplatform Lib to make patching easy. TransactionBuilder withChangeAddressIfNeeded(ShelleyAddress address) { @@ -135,7 +136,7 @@ final class TransactionBuilder extends Equatable { /// The [output] must reach a minimum [Coin] value as calculated /// by [TransactionOutputBuilder.minimumAdaForOutput], /// otherwise [TxValueBelowMinUtxoValueException] is thrown. - TransactionBuilder withOutput(TransactionOutput output) { + TransactionBuilder withOutput(ShelleyMultiAssetTransactionOutput output) { final valueSize = cbor.encode(output.amount.toCbor()).length; if (valueSize > config.maxValueSize) { throw TxValueSizeExceededException( @@ -340,7 +341,7 @@ final class TransactionBuilder extends Equatable { /// The function is used to split native assets into /// multiple outputs if they don't fit in one output. bool _willAddingAssetMakeOutputOverflow({ - required TransactionOutput output, + required ShelleyMultiAssetTransactionOutput output, required Map currentAssets, required (PolicyId, AssetName, Coin) assetToAdd, }) { @@ -481,7 +482,7 @@ final class TransactionBuilder extends Equatable { } TransactionBuilder _copyWith({ - List? outputs, + List? outputs, Coin? fee, TransactionWitnessSetBuilder? witnessBuilder, }) { @@ -531,7 +532,7 @@ final class TransactionBuilderConfig extends Equatable { [feeAlgo, maxTxSize, maxValueSize, coinsPerUtxoByte]; } -/// Builder and utils around [TransactionOutput]. +/// Builder and utils around [ShelleyMultiAssetTransactionOutput]. final class TransactionOutputBuilder { /// Constant from figure 5 in Babbage spec meant to represent /// the size of the input in a UTXO. @@ -540,12 +541,12 @@ final class TransactionOutputBuilder { /// Prevents creating instances of [TransactionOutputBuilder]. const TransactionOutputBuilder._(); - /// Creates a new [TransactionOutput] that transfers + /// Creates a new [ShelleyMultiAssetTransactionOutput] that transfers /// the [multiAsset] to the address. /// /// Adds a minimum amount of [Coin] to the transaction to pass /// the [minimumAdaForOutput] validation. - static TransactionOutput withAssetAndMinRequiredCoin({ + static ShelleyMultiAssetTransactionOutput withAssetAndMinRequiredCoin({ required ShelleyAddress address, required MultiAsset multiAsset, required Coin coinsPerUtxoByte, @@ -574,7 +575,7 @@ final class TransactionOutputBuilder { /// Calculates the additional fee for adding the [output] to the [builder]. static Coin feeForOutput( TransactionBuilder builder, - TransactionOutput output, + ShelleyMultiAssetTransactionOutput output, ) { final prev = builder.withFee(const Coin(0)); final prevFee = prev.minFee(); @@ -591,7 +592,7 @@ final class TransactionOutputBuilder { /// /// The algorithm considers all of above cases. static Coin minimumAdaForOutput( - TransactionOutput output, + ShelleyMultiAssetTransactionOutput output, Coin coinsPerUtxoByte, ) { final outputSize = cbor.encode(output.toCbor()).length; diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/datum.dart b/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/datum.dart new file mode 100644 index 00000000000..a8c379615b0 --- /dev/null +++ b/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/datum.dart @@ -0,0 +1,139 @@ +import 'dart:typed_data'; +import 'package:catalyst_cardano_serialization/src/types.dart'; +import 'package:cbor/cbor.dart'; +import 'package:equatable/equatable.dart'; +import 'package:pinenacl/api.dart'; + +/// Enum representing the type of a datum option in Cardano. +/// A DatumOption can either be a datum hash or inline Plutus data. +enum DatumOptionType { + /// [datumHash]: Represents a hash of the datum, typically used to refer to + /// data that exists off-chain. + datumHash(0), + + /// [data]: Represents actual inline datum stored on-chain. + data(1); + + /// The value of the enum. + final int value; + + const DatumOptionType(this.value); +} + +/// Abstract base class representing a datum in a Cardano transaction output. +/// A datum is a piece of data that can be either a hash or inline plutus data. +/// +/// Implementations of this class must provide a way to encode the datum into +/// a CBOR. +sealed class Datum extends Equatable implements CborEncodable { + const Datum(); +} + +/// Class representing an optional datum in a Cardano transaction output. +/// A [DatumOption] wraps a [Datum], which can either be a datum hash or direct +/// data as inline datum. +/// +/// This class is used to support additional features in transaction outputs +/// that were introduced in Cardano after the Alonzo hard fork. +final class DatumOption extends Datum { + /// The actual datum wrapped by this [DatumOption]. + final Datum datum; + + /// Constructor for the [DatumOption] class, which requires a [Datum]. + const DatumOption(this.datum); + + /// Factory constructor to create a [DatumOption] from a CBOR-encoded value. + /// The CBOR value is expected to be a list where the first element is the + /// type (either datum hash or data) and the second element is the actual + /// encoded datum. + factory DatumOption.fromCbor(CborValue value) { + if (value is CborList) { + final type = DatumOptionType.values[(value[0] as CborSmallInt).toInt()]; + final datum = switch (type) { + DatumOptionType.datumHash => DatumHash.fromCbor(value[1]), + DatumOptionType.data => Data.fromCbor(value[1]), + }; + return DatumOption(datum); + } else { + throw ArgumentError.value( + value, + 'value', + 'Expected DatumOption as CborList', + ); + } + } + + /// Converts the [DatumOption] to a CBOR-encoded value. + /// The CBOR list contains an index indicating the type and the encoded datum. + @override + CborValue toCbor() { + final index = switch (datum) { + DatumHash _ => DatumOptionType.datumHash, + Data _ => DatumOptionType.data, + _ => throw ArgumentError.value(datum, 'datum', 'Invalid datum type'), + }; + return CborList([CborSmallInt(index.value), datum.toCbor()]); + } + + @override + List get props => [datum]; +} + +/// Class representing a datum hash in Cardano. +/// A [DatumHash] is a type of [Datum] that contains a cryptographic hash, which +/// refers to data stored off-chain. +/// +/// This is useful when you need to reference data without including the actual +/// data in the transaction output. +final class DatumHash extends Datum { + // TODO(ilap): Add length constraint and/or implement proper hash type. + /// Byte array representing the cryptographic hash. + final Uint8List hash; + + /// Constructor for the [DatumHash] class, which requires a hash. + const DatumHash(this.hash); + + /// Factory constructor to create a [DatumHash] from a CBOR-encoded value. + /// The CBOR value is expected to be a byte string. + factory DatumHash.fromCbor(CborValue value) { + if (value is CborBytes) { + return DatumHash(Uint8List.fromList(value.bytes)); + } + throw ArgumentError.value(value, 'value', 'Expected valid CborBytes'); + } + + /// Converts the [DatumHash] to a CBOR-encoded value. + /// The CBOR value is a byte string representing the hash. + @override + CborValue toCbor() => CborBytes(hash); + + @override + List get props => [hash]; +} + +/// Class representing data in a Cardano transaction output. +/// The [Data] class is a type of [Datum] that contains actual data stored +/// on-chain. +/// +/// This is useful when you want to include data directly in the transaction +/// output. +final class Data extends Datum { + // TODO(ilap): Requires a proper plutus data type implementation. + /// CBOR-encoded data associated with this [Data] object. + final CborValue data; + + /// Constructor for the [Data] class, which requires CBOR-encoded data. + const Data(this.data); + + /// Factory constructor to create a [Data] from a CBOR-encoded value. + factory Data.fromCbor(CborValue value) => Data(value); + + /// The CBOR value represents the actual data. + @override + CborValue toCbor() => data; + + /// Override of the [Equatable] properties for value comparison. + /// This allows comparing [Data] instances based on their [data] values. + @override + List get props => [data]; +} diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/hashes.dart b/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/hashes.dart index 20f04e94aa0..d90c4a8f679 100644 --- a/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/hashes.dart +++ b/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/hashes.dart @@ -4,14 +4,18 @@ import 'dart:typed_data'; import 'package:catalyst_cardano_serialization/src/certificate.dart'; import 'package:catalyst_cardano_serialization/src/exceptions.dart'; +import 'package:catalyst_cardano_serialization/src/redeemer.dart'; +import 'package:catalyst_cardano_serialization/src/signature.dart'; import 'package:catalyst_cardano_serialization/src/transaction.dart'; +import 'package:catalyst_cardano_serialization/src/types.dart'; import 'package:cbor/cbor.dart'; import 'package:convert/convert.dart'; +import 'package:equatable/equatable.dart'; import 'package:pinenacl/digests.dart'; /// Implements a common base of hash types that holds /// binary [bytes] of exact [length]. -abstract base class BaseHash { +abstract base class BaseHash extends Equatable implements CborEncodable { /// The raw [bytes] of a hash. final List bytes; @@ -27,6 +31,7 @@ abstract base class BaseHash { : this.fromBytes(bytes: (value as CborBytes).bytes); /// Serializes the type as cbor. + @override CborValue toCbor() => CborBytes(bytes); /// Constructs the [BaseHash] from a hex string representation @@ -43,25 +48,7 @@ abstract base class BaseHash { String toString() => toHex(); @override - int get hashCode => Object.hash(bytes, length); - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - if (other is! BaseHash) return false; - - // prevent subclasses of different types to be equal to each other, - // even if they hold the same bytes they represent different kinds - if (other.runtimeType != runtimeType) return false; - - if (length != other.length) return false; - - for (var i = 0; i < bytes.length; i++) { - if (bytes[i] != other.bytes[i]) return false; - } - - return true; - } + List get props => [bytes]; } /// Describes the Blake2b-256 hash of the transaction which serves as proof @@ -107,13 +94,13 @@ final class TransactionInputsHash extends BaseHash { /// Constructs the [TransactionInputsHash] from a [TransactionBody]. TransactionInputsHash.fromTransactionInputs( - List inputs, + List utxos, ) : super.fromBytes( bytes: Hash.blake2b( Uint8List.fromList( cbor.encode( CborList([ - for (final input in inputs) input.toCbor(), + for (final utxo in utxos) utxo.input.toCbor(), ]), ), ), @@ -191,3 +178,58 @@ final class CertificateHash extends BaseHash { @override int get length => _length; } + +/// Describes the Blake2b-224 hash of a [Ed25519PublicKey]. +final class Ed25519PublicKeyHash extends BaseHash { + static const int _length = 28; + + /// Constructs the [Ed25519PublicKeyHash] from raw [bytes]. + Ed25519PublicKeyHash.fromBytes({required super.bytes}) : super.fromBytes(); + + /// Constructs the [Ed25519PublicKeyHash] from a hex string representation + /// of [bytes]. + Ed25519PublicKeyHash.fromHex(super.string) : super.fromHex(); + + /// Constructs the [Ed25519PublicKeyHash] from a [Ed25519PublicKey]. + Ed25519PublicKeyHash.fromPublicKey(Ed25519PublicKey key) + : super.fromBytes( + bytes: Hash.blake2b( + Uint8List.fromList(key.bytes), + digestSize: _length, + ), + ); + + /// Deserializes the type from cbor. + Ed25519PublicKeyHash.fromCbor(super.value) : super.fromCbor(); + + @override + int get length => _length; +} + +/// Describes the Blake2b-256 hash of script data which is included +/// in the transaction body. +final class ScriptDataHash extends BaseHash { + static const int _length = 32; + + /// Constructs the [ScriptDataHash] from raw [bytes]. + ScriptDataHash.fromBytes({required super.bytes}) : super.fromBytes(); + + /// Constructs the [ScriptDataHash] from a hex string representation + /// of [bytes]. + ScriptDataHash.fromHex(super.string) : super.fromHex(); + + /// Constructs the [ScriptDataHash] from a [ScriptData]. + ScriptDataHash.fromScriptData(ScriptData data) + : super.fromBytes( + bytes: Hash.blake2b( + Uint8List.fromList(cbor.encode(data.toCbor())), + digestSize: _length, + ), + ); + + /// Deserializes the type from cbor. + ScriptDataHash.fromCbor(super.value) : super.fromCbor(); + + @override + int get length => _length; +} diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/rbac/auth_token.dart b/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/rbac/auth_token.dart new file mode 100644 index 00000000000..2b57b8ff0c6 --- /dev/null +++ b/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/rbac/auth_token.dart @@ -0,0 +1,67 @@ +import 'dart:convert'; + +import 'package:catalyst_cardano_serialization/catalyst_cardano_serialization.dart'; +import 'package:cbor/cbor.dart'; +import 'package:ulid/ulid.dart'; + +/// The Authentication Token is based loosely on JWT. +/// It consists of an Authentication Header attached to every authenticated +/// request, and an encoded signed. +/// +/// This token can be attached to either individual HTTP requests, +/// or to the beginning of a web socket connection. +/// +/// The authentication header is in the format: +/// +/// ```http +/// Authorization: Bearer catv1. +/// ``` +/// +/// ### Encoded Binary Token Format +/// +/// The Encoded Binary Token is a [CBOR sequence] that consists of 3 fields. +/// +/// * `kid` : The key identifier. +/// * `ulid` : A ULID which defines when the token was issued, +/// and a random nonce. +/// * `signature` : The signature over the `kid` and `ulid` fields. +final class AuthToken { + /// The token prefix which distinguishes this auth token from other + /// auth tokens and allows version via the v{} part. + static const String prefix = 'catv1'; + + /// Prevent creating instances. + const AuthToken._(); + + /// Generates a new auth token at a given [timestamp]. + /// + /// * The [kid] in most cases is going to be a [CertificateHash] + /// of the [privateKey] certificate. + /// * The [privateKey] must correspond to the [kid] specified. + /// * The [timestamp] is a [DateTime] when a given token has been generated. + static Future generate({ + required CertificateHash kid, + required Ed25519PrivateKey privateKey, + required DateTime timestamp, + }) async { + final ulid = CborBytes( + Ulid(millis: timestamp.millisecondsSinceEpoch).toBytes(), + ); + + final toBeSigned = [ + ...cbor.encode(kid.toCbor()), + ...cbor.encode(ulid), + ]; + + final signature = await privateKey.sign(toBeSigned); + + final cborToken = [ + ...cbor.encode(kid.toCbor()), + ...cbor.encode(ulid), + ...cbor.encode(signature.toCbor()), + ]; + + final base64Token = base64Encode(cborToken); + return '$prefix.$base64Token'; + } +} diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/rbac/registration_data.dart b/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/rbac/registration_data.dart index a9bdf06f1cb..c71001aba76 100644 --- a/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/rbac/registration_data.dart +++ b/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/rbac/registration_data.dart @@ -4,7 +4,7 @@ import 'package:cbor/cbor.dart'; import 'package:equatable/equatable.dart'; /// Defines the X509 Role Based Access Control transaction metadata. -final class RegistrationData extends Equatable { +final class RegistrationData extends Equatable implements CborEncodable { /// Un-ordered List of DER encoded x509 certificates. final List? derCerts; @@ -51,6 +51,7 @@ final class RegistrationData extends Equatable { } /// Serializes the type as cbor. + @override CborValue toCbor() => CborMap(_buildCborMap()); /// Builds a CborMap from the class properties. @@ -97,7 +98,7 @@ final class RegistrationData extends Equatable { /// /// The validity of the registration is as per the rules for roles defined /// by the dApp itself. -class RoleData extends Equatable { +class RoleData extends Equatable implements CborEncodable { /// All roles, except for Role 0, are defined by the dApp. /// /// Role 0 is the primary role and is used to sign the metadata and declare on-chain/off-chain identity linkage. @@ -219,6 +220,7 @@ class RoleData extends Equatable { } /// Serializes the type as cbor. + @override CborValue toCbor() { return CborMap({ const CborSmallInt(0): CborSmallInt(roleNumber), @@ -247,7 +249,7 @@ class RoleData extends Equatable { /// a given key in an earlier registration. /// /// Either [localRef] or [hash] must be set, but not both and not none. -class KeyReference extends Equatable { +class KeyReference extends Equatable implements CborEncodable { /// Offset reference to a key defined in this registration. /// More efficient than a key hash. final LocalKeyReference? localRef; @@ -289,6 +291,7 @@ class KeyReference extends Equatable { } /// Serializes the type as cbor. + @override CborValue toCbor() { return localRef?.toCbor() ?? hash!.toCbor(); } @@ -300,7 +303,7 @@ class KeyReference extends Equatable { /// Offset reference to a key defined in this registration. /// /// More efficient than a key hash. -class LocalKeyReference extends Equatable { +class LocalKeyReference extends Equatable implements CborEncodable { /// A type of referenced key. final LocalKeyReferenceType keyType; @@ -326,6 +329,7 @@ class LocalKeyReference extends Equatable { } /// Serializes the type as cbor. + @override CborValue toCbor() { return CborList([ CborSmallInt(keyType.tag), diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/redeemer.dart b/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/redeemer.dart new file mode 100644 index 00000000000..766d6e0691c --- /dev/null +++ b/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/redeemer.dart @@ -0,0 +1,47 @@ +import 'package:catalyst_cardano_serialization/catalyst_cardano_serialization.dart'; +import 'package:cbor/cbor.dart'; +import 'package:equatable/equatable.dart'; + +// TODO(ilap): implement proper redeemer class, this is just a placeholder. +/// Class representing a redeemer in Cardano. +/// A Redeemer is a piece of data that is used to validate a plutus script in a +/// transaction. +final class Redeemer extends Equatable implements CborEncodable { + /// redeemer placeholder, + final CborValue redeemer; + + /// The default constructor for [Redeemer]. + const Redeemer({required this.redeemer}); + + /// Factory constructor to create a [Redeemer] from a CBOR-encoded value. + /// The CBOR value is expected to be a list or a map. + factory Redeemer.fromCbor(CborValue value) => Redeemer(redeemer: value); + + /// Converts the [Redeemer] to a CBOR-encoded value. + @override + CborValue toCbor() => redeemer; + + @override + List get props => [redeemer]; +} + +// TODO(ilap): implement proper plutus data class, this is just a placeholder. +/// Class representing a script data in Cardano. +class ScriptData extends Equatable implements CborEncodable { + /// script data placeholder, + final CborValue data; + + /// The default constructor for [ScriptData]. + const ScriptData({required this.data}); + + /// Factory constructor to create a [Redeemer] from a CBOR-encoded value. + /// The CBOR value is expected to be a list or a map. + factory ScriptData.fromCbor(CborValue data) => ScriptData(data: data); + + /// Converts the [ScriptData] to a CBOR-encoded value. + @override + CborValue toCbor() => data; + + @override + List get props => [data]; +} diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/scripts.dart b/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/scripts.dart new file mode 100644 index 00000000000..1cda0ef9080 --- /dev/null +++ b/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/scripts.dart @@ -0,0 +1,515 @@ +import 'dart:typed_data'; +import 'package:catalyst_cardano_serialization/src/hashes.dart'; +import 'package:catalyst_cardano_serialization/src/types.dart'; +import 'package:cbor/cbor.dart'; +import 'package:equatable/equatable.dart'; +import 'package:pinenacl/digests.dart'; + +/// The size of the script hash in bytes. +const scriptHashSize = 28; + +/// Define an enum for the different types of native scripts +enum NativeScriptType { + /// Public key + pubkey(0), + + /// All + all(1), + + /// Any + any(2), + + /// N of K + nOfK(3), + + /// Invalid before + invalidBefore(4), + + /// Invalid after + invalidAfter(5); + + /// The value of the enum + final int value; + + const NativeScriptType(this.value); +} + +/// Define an enum for the different types of reference scripts +enum RefScriptType { + /// Native script + native(0), + + /// PlutusV1 script + plutusV1(1), + + /// PlutusV2 script + plutusV2(2), + + /// PlutusV3 script + plutusV3(3); + + /// The value of the enum + final int value; + + const RefScriptType(this.value); +} + +/// Define a sealed class for scripts that extends Equatable. +sealed class Script extends Equatable implements CborEncodable { + const Script(); + + /// Gets the tag for the script type. + /// + /// This tag helps identify different script types: + /// - `0` for `NativeScript` + /// - `1` for `PlutusV1Script` + /// - `2` for `PlutusV2Script` + /// - `3` for `PlutusV3Script` + /// + /// Throws an `ArgumentError` if the script type is not recognized. + /// + /// Returns: + /// - An integer representing the script type tag. + int get tag => switch (this) { + NativeScript _ => 0, + PlutusV1Script _ => 1, + PlutusV2Script _ => 2, + PlutusV3Script _ => 3, + _ => throw ArgumentError.value( + this, + 'script', + 'Invalid script type to hash.', + ), + }; + + /// Computes the hash of the script. + /// + /// This method converts the script to its CBOR representation, + /// adds a tag based on the script type, and then computes the + /// Blake2b hash of the resulting bytes. + Uint8List get hash { + final cborValue = toCbor(); + final bytesToHash = cborValue is CborBytes + ? _handleDoubleEncodedCbor(cborValue.bytes) + : cborValue; + + final cborBytes = cbor.encode(bytesToHash); + final bytes = Uint8List.fromList([tag, ...cborBytes]); + return Hash.blake2b(bytes, digestSize: scriptHashSize); + } + + CborValue _handleDoubleEncodedCbor(List bytes) { + final decoded = cbor.decode(bytes); + if (decoded is CborBytes) { + try { + return cbor.decode(decoded.bytes); + } catch (_) {} + } + return decoded; + } +} + +/// Abstract base class for native scripts, extending [Script]. +sealed class NativeScript extends Script { + const NativeScript(); + + /// Factory constructor to create a [NativeScript] from a CBOR value. + /// The type of native script is determined by the first element in the CBOR + /// list. If the first element is not a CborSmallInt, an exception is thrown. + factory NativeScript.fromCbor(CborValue value) { + if (value is CborList) { + try { + final type = + NativeScriptType.values[(value[0] as CborSmallInt).toInt()]; + return switch (type) { + NativeScriptType.pubkey => ScriptPubkey.fromCbor(value), + NativeScriptType.all => ScriptAll.fromCbor(value), + NativeScriptType.any => ScriptAny.fromCbor(value), + NativeScriptType.nOfK => ScriptNOfK.fromCbor(value), + NativeScriptType.invalidBefore => InvalidBefore.fromCbor(value), + NativeScriptType.invalidAfter => InvalidAfter.fromCbor(value), + }; + // ignore: avoid_catching_errors + } on RangeError catch (e) { + throw ArgumentError.value( + value, + 'type', + 'Invalid NativeScript type value $e.', + ); + } + } else { + throw ArgumentError.value( + value, + 'value', + 'Expected proper NativeScript as CborList', + ); + } + } + + /// Creates a NativeScript instance from a JSON object. + /// + /// The JSON object should have a 'type' field that indicates the type of + /// script to create. Depending on the script type, the JSON object may also + /// contain other fields with additional data. + /// + /// Supported script types: + /// - 'sig': Creates a ScriptPubkey instance. The JSON object should have a + /// 'keyHash' field that contains the key hash to use in the [ScriptPubkey]. + /// - 'all': Creates a ScriptAll instance. The JSON object should have a + /// 'scripts' field that contains a list of JSON objects representing the + /// scripts to include in the [ScriptAll]. + /// - 'any': Creates a ScriptAny instance. The JSON object should have a + /// 'scripts' field that contains a list of JSON objects representing the + /// scripts to include in the [ScriptAny]. + /// - 'atLeast': Creates a ScriptNOfK instance. The JSON object should have a + /// 'required' field that contains the number of scripts that must be + /// satisfied, and a 'scripts' field that contains a list of JSON objects + /// representing the scripts to include in the [ScriptNOfK]. + /// - 'before': Creates an InvalidBefore instance. The JSON object should have + /// a 'slot' field that contains the slot number to use in the + /// [InvalidBefore]. + /// - 'after': Creates an InvalidAfter instance. The JSON object should have a + /// 'slot' field that contains the slot number to use in the [InvalidAfter]. + /// + /// If the JSON object contains an unsupported script type, an ArgumentError + /// is thrown. + static NativeScript fromJSON(Map json) { + return switch (json['type']) { + 'sig' => + ScriptPubkey(Ed25519PublicKeyHash.fromHex(json['keyHash'] as String)), + 'all' => ScriptAll( + (json['scripts'] as List>) + .map(fromJSON) + .toList(), + ), + 'any' => ScriptAny( + (json['scripts'] as List>) + .map(fromJSON) + .toList(), + ), + 'atLeast' => ScriptNOfK( + json['required'] as int, + (json['scripts'] as List>) + .map(fromJSON) + .toList(), + ), + 'before' => InvalidAfter(json['slot'] as int), + 'after' => InvalidBefore(json['slot'] as int), + _ => throw ArgumentError('Unknown script type: ${json['type']}') + }; + } + + static void _invalidCborError(CborValue value) => + throw ArgumentError.value(value, 'value', 'Invalid NativeScript cbor'); + + /// Checks if a CBOR value is a valid generic native script. + static void _checkGenericNativeScriptValidity(CborValue value) { + if (value is! CborList || value.length != 2 || value[1] is! CborBytes) { + _invalidCborError(value); + } + } + + /// Checks if a CBOR value is a valid list-based native script. + static void _checkListNativeScriptValidity(CborValue value) { + if (value is! CborList || value.length != 2 || value[1] is! CborList) { + _invalidCborError(value); + } + } + + /// Checks if a CBOR value is a valid bounded native script. + static void _checkBoundedNativeScriptValidity(CborValue value) { + if (value is! CborList || value.length != 2 || value[1] is! CborSmallInt) { + _invalidCborError(value); + } + } +} + +/// Class representing a public key based native script. +class ScriptPubkey extends NativeScript { + /// Public key hash. + final Ed25519PublicKeyHash addrKeyHash; + + /// Constructor for the [ScriptPubkey] class. + const ScriptPubkey(this.addrKeyHash); + + /// Factory constructor to create a [ScriptPubkey] from a CBOR list. + factory ScriptPubkey.fromCbor(CborList value) { + NativeScript._checkGenericNativeScriptValidity(value); + + return ScriptPubkey(Ed25519PublicKeyHash.fromCbor(value[1])); + } + + /// Converts the [ScriptPubkey] to its CBOR format. + @override + CborValue toCbor() => CborList( + [ + CborSmallInt(NativeScriptType.pubkey.value), + CborBytes(addrKeyHash.bytes), + ], + ); + + /// Equatable props for value comparison. + @override + List get props => [addrKeyHash]; +} + +/// Class representing an "all" native script (AND operation). +class ScriptAll extends NativeScript { + /// List of the required signatures. + final List nativeScripts; + + /// Constructor for the [ScriptAll] class. + const ScriptAll(this.nativeScripts); + + /// Factory constructor to create a [ScriptAll] from a CBOR list. + factory ScriptAll.fromCbor(CborList value) { + NativeScript._checkListNativeScriptValidity(value); + final scripts = (value[1] as CborList) + .map((e) => NativeScript.fromCbor(e as CborList)) + .toList(); + return ScriptAll(scripts); + } + + /// Converts the [ScriptAll] to its CBOR format. + @override + CborValue toCbor() => CborList([ + CborSmallInt(NativeScriptType.all.value), + CborList(nativeScripts.map((s) => s.toCbor()).toList()), + ]); + + /// Equatable props for value comparison. + @override + List get props => [nativeScripts]; +} + +/// Class representing an "any" native script (OR operation). +class ScriptAny extends NativeScript { + /// List of the available signatures. + final List nativeScripts; + + /// Constructor for the [ScriptAny] class. + const ScriptAny(this.nativeScripts); + + /// Factory constructor to create a [ScriptAny] from a CBOR value. + factory ScriptAny.fromCbor(CborValue value) { + NativeScript._checkListNativeScriptValidity(value); + final scripts = ((value as CborList)[1] as CborList) + .map((e) => NativeScript.fromCbor(e as CborList)) + .toList(); + return ScriptAny(scripts); + } + + /// Converts the [ScriptAny] to its CBOR format. + @override + CborValue toCbor() => CborList([ + CborSmallInt(NativeScriptType.any.value), + CborList(nativeScripts.map((s) => s.toCbor()).toList()), + ]); + + /// Equatable props for value comparison. + @override + List get props => [nativeScripts]; +} + +/// Class representing an "n of k" native script (M of N operation). +class ScriptNOfK extends NativeScript { + /// The number of required signatures. + final int n; + + /// The list of native scripts. + final List nativeScripts; + + /// Creates a new [ScriptNOfK] with the given [n] and [nativeScripts]. + const ScriptNOfK(this.n, this.nativeScripts); + + /// Factory constructor to create a [ScriptNOfK] from a CBOR value. + factory ScriptNOfK.fromCbor(CborValue value) { + if (value is! CborList || + value.length != 3 || + value[1] is! CborSmallInt || + value[2] is! CborList) { + throw ArgumentError.value(value, 'value', 'Invalid ScriptNOfK'); + } + final n = (value[1] as CborSmallInt).value; + final scripts = (value[2] as CborList) + .map((e) => NativeScript.fromCbor(e as CborList)) + .toList(); + return ScriptNOfK(n, scripts); + } + + /// Converts the [ScriptNOfK] to its CBOR format. + @override + CborValue toCbor() => CborList([ + CborSmallInt(NativeScriptType.nOfK.value), + CborSmallInt(n), + CborList(nativeScripts.map((s) => s.toCbor()).toList()), + ]); + + /// Equatable props for value comparison. + @override + List get props => [n, nativeScripts]; +} + +/// Class representing an "invalid before" native script (time-locked). +class InvalidBefore extends NativeScript { + /// Converts the [InvalidBefore] to its CBOR format. + final int timestamp; + + /// Factory constructor to create an [InvalidBefore] from a CBOR list. + const InvalidBefore(this.timestamp); + + /// Factory constructor to create an [InvalidBefore] from a CBOR list. + factory InvalidBefore.fromCbor(CborList value) { + NativeScript._checkBoundedNativeScriptValidity(value); + return InvalidBefore((value[1] as CborSmallInt).value); + } + + /// Converts the [InvalidBefore] to its CBOR format. + @override + CborValue toCbor() => CborList([ + CborSmallInt(NativeScriptType.invalidBefore.value), + CborSmallInt(timestamp), + ]); + + /// Equatable props for value comparison. + @override + List get props => [timestamp]; +} + +/// Class representing an "invalid after" native script (time-locked). +class InvalidAfter extends NativeScript { + /// Converts the [InvalidAfter] to its CBOR format. + final int timestamp; + + /// Factory constructor to create an [InvalidAfter] from a CBOR list. + const InvalidAfter(this.timestamp); + + /// Factory constructor to create an [InvalidAfter] from a CBOR list. + factory InvalidAfter.fromCbor(CborList value) { + NativeScript._checkBoundedNativeScriptValidity(value); + return InvalidAfter((value[1] as CborSmallInt).value); + } + + /// Converts the [InvalidAfter] to its CBOR format. + @override + CborValue toCbor() => CborList([ + CborSmallInt(NativeScriptType.invalidAfter.value), + CborSmallInt(timestamp), + ]); + + /// Equatable props for value comparison. + @override + List get props => [timestamp]; +} + +/// Abstract base class for Plutus scripts, extending [Script]. +sealed class PlutusScript extends Script { + /// [PlutusScript] represented as encoded CBOR bytes. + final Uint8List bytes; + + // TODO(ilap): Check whether the Plutus script bytes are valid CBOR CborByte-s + // and throw an error if does not. + /// [PlutusScript] constructor. + const PlutusScript(this.bytes); + + /// Converts the [PlutusScript] to its CBOR format. + /// All Plutus scripts are serialized CBOR bytes. + @override + CborValue toCbor() => CborBytes(bytes); + + /// Validates if the CBOR value is a valid Plutus script. + static void _plutusScriptValidity(CborValue value) { + if (value is! CborBytes) { + throw ArgumentError.value(value, 'value', 'Invalid Plutus script cbor'); + } + } + + /// Equatable props for value comparison of all Plutus scripts. + @override + List get props => [bytes]; +} + +/// Class representing a Plutus V1 script. +class PlutusV1Script extends PlutusScript { + /// [PlutusV1Script] constructor. + const PlutusV1Script(super.bytes); + + /// Factory constructor to create an [PlutusV1Script] from a CBOR list. + factory PlutusV1Script.fromCbor(CborValue value) { + PlutusScript._plutusScriptValidity(value); + return PlutusV1Script(Uint8List.fromList((value as CborBytes).bytes)); + } +} + +/// Class representing a Plutus V2 script. +class PlutusV2Script extends PlutusScript { + /// [PlutusV2Script] constructor. + const PlutusV2Script(super.bytes); + + /// Factory constructor to create an [PlutusV2Script] from a CBOR list. + factory PlutusV2Script.fromCbor(CborValue value) { + PlutusScript._plutusScriptValidity(value); + return PlutusV2Script(Uint8List.fromList((value as CborBytes).bytes)); + } +} + +/// Class representing a Plutus V3 script. +class PlutusV3Script extends PlutusScript { + /// [PlutusV3Script] constructor. + const PlutusV3Script(super.bytes); + + /// Factory constructor to create an [PlutusV3Script] from a CBOR list. + factory PlutusV3Script.fromCbor(CborValue value) { + PlutusScript._plutusScriptValidity(value); + return PlutusV3Script(Uint8List.fromList((value as CborBytes).bytes)); + } +} + +/// Class representing a reference script in an transaction output. +class ScriptRef extends Script { + /// A reference script. + final Script script; + + /// Creates a new [ScriptRef] with the given [script]. + const ScriptRef(this.script); + + /// Factory constructor to create a [ScriptRef] from a CBOR list. + factory ScriptRef.fromCbor(CborValue value) { + if (value is CborList) { + final type = RefScriptType.values[(value[0] as CborSmallInt).toInt()]; + final script = switch (type) { + RefScriptType.native => NativeScript.fromCbor(value[1]), + RefScriptType.plutusV1 => PlutusV1Script.fromCbor(value[1]), + RefScriptType.plutusV2 => PlutusV2Script.fromCbor(value[1]), + RefScriptType.plutusV3 => PlutusV3Script.fromCbor(value[1]), + }; + return ScriptRef(script); + } else { + throw ArgumentError.value( + value, + 'value', + 'Expected ScriptRef as CborList', + ); + } + } + + @override + CborValue toCbor() { + final index = switch (script) { + NativeScript _ => RefScriptType.native, + PlutusV1Script _ => RefScriptType.plutusV1, + PlutusV2Script _ => RefScriptType.plutusV2, + PlutusV3Script _ => RefScriptType.plutusV3, + _ => throw ArgumentError.value( + script, + 'script', + 'Invalid script reference type', + ), + }; + return CborList([CborSmallInt(index.value), script.toCbor()]); + } + + /// Equatable props for value comparison. + @override + List get props => [script]; +} diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/transaction.dart b/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/transaction.dart index 1cfee573914..bba75e8ad5b 100644 --- a/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/transaction.dart +++ b/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/transaction.dart @@ -1,6 +1,5 @@ -import 'package:catalyst_cardano_serialization/src/address.dart'; import 'package:catalyst_cardano_serialization/src/hashes.dart'; -import 'package:catalyst_cardano_serialization/src/signature.dart'; +import 'package:catalyst_cardano_serialization/src/transaction_output.dart'; import 'package:catalyst_cardano_serialization/src/types.dart'; import 'package:catalyst_cardano_serialization/src/utils/cbor.dart'; import 'package:catalyst_cardano_serialization/src/witness.dart'; @@ -9,7 +8,7 @@ import 'package:equatable/equatable.dart'; /// Represents the signed transaction with a list of witnesses /// which are used to verify the validity of a transaction. -final class Transaction extends Equatable { +final class Transaction extends Equatable implements CborEncodable { /// The transaction body containing the inputs, outputs, fees, etc. final TransactionBody body; @@ -49,6 +48,7 @@ final class Transaction extends Equatable { } /// Serializes the type as cbor. + @override CborValue toCbor() { return CborList([ body.toCbor(), @@ -65,30 +65,58 @@ final class Transaction extends Equatable { /// Represents the details of a transaction including inputs, outputs, fee, etc. /// /// Does not contain the witnesses which are used to verify the transaction. -final class TransactionBody extends Equatable { - /// The transaction inputs. +final class TransactionBody extends Equatable implements CborEncodable { + /// The transaction inputs. tag: 0 final Set inputs; - /// The transaction outputs. - final List outputs; + /// The transaction outputs. tag: 1 + final List outputs; - /// The fee for the transaction. + /// The fee for the transaction. tag: 2 final Coin fee; - /// The absolute slot value before the tx becomes invalid. + // > Note: All properties below are optional. + /// The absolute slot value before the tx becomes invalid. tag: 3 final SlotBigNum? ttl; + /// Certificates in an ordered set. tag: 4 + /// Withdrawals map of stake address, coin. tag: 5 + /// The hash of the optional [AuxiliaryData] - /// which is the metadata of the transaction. + /// which is the metadata of the transaction. tag: 7 final AuxiliaryDataHash? auxiliaryDataHash; + /// Validity interval start as integer. tag: 8 + final SlotBigNum? validityStart; + + /// Mint as a non-zero uint64 multiasset. tag: 9 + final MultiAsset? mint; + + /// Tag 10 is unimplemented. + /// Script data hash28. tag: 11 + final ScriptDataHash? scriptDataHash; + + /// Tag 12 is unimplemented. + /// Collateral inputs as nonempty set. tag: 13 + final Set? collateralInputs; + /// The list of public key hashes of addresses /// that are required to sign the transaction. - final Set? requiredSigners; + /// Nonempty set of addr keyhash. tag: 14 + final Set? requiredSigners; - /// Specifies on which network the code will run. + /// Specifies on which network the code will run. Network ID 0/1. tag: 15 final NetworkId? networkId; + /// Collateral return's transaction output. tag: 16 + final ShelleyMultiAssetTransactionOutput? collateralReturn; + + /// Total collateral as coin (uint64). tag: 17 + final Coin? totalCollateral; + + /// Reference inputs as nonempty set of transaction inputs. tag: 18 + final Set? referenceInputs; + /// The default constructor for [TransactionBody]. const TransactionBody({ required this.inputs, @@ -96,56 +124,103 @@ final class TransactionBody extends Equatable { required this.fee, this.ttl, this.auxiliaryDataHash, + this.validityStart, + this.mint, + this.scriptDataHash, + this.collateralInputs, this.requiredSigners, this.networkId, + this.collateralReturn, + this.totalCollateral, + this.referenceInputs, }); /// Deserializes the type from cbor. factory TransactionBody.fromCbor(CborValue value) { - final map = value as CborMap; - final inputs = map[const CborSmallInt(0)]! as CborList; - final outputs = map[const CborSmallInt(1)]! as CborList; - final fee = map[const CborSmallInt(2)]!; - final ttl = map[const CborSmallInt(3)]; - final auxiliaryDataHash = map[const CborSmallInt(7)]; - final requiredSigners = map[const CborSmallInt(14)] as CborList?; - final networkId = map[const CborSmallInt(15)] as CborSmallInt?; - - return TransactionBody( - inputs: inputs.map(TransactionInput.fromCbor).toSet(), - outputs: outputs.map(TransactionOutput.fromCbor).toList(), - fee: Coin.fromCbor(fee), - ttl: ttl != null ? SlotBigNum.fromCbor(ttl) : null, - auxiliaryDataHash: auxiliaryDataHash != null - ? AuxiliaryDataHash.fromCbor(auxiliaryDataHash) - : null, - requiredSigners: requiredSigners?.map(Ed25519PublicKey.fromCbor).toSet(), - networkId: networkId != null ? NetworkId.fromId(networkId.value) : null, - ); + try { + final map = value as CborMap; + + return TransactionBody( + inputs: _extractList(map, 0, TransactionInput.fromCbor)!.toSet(), + outputs: _extractList(map, 1, TransactionOutput.fromCbor)!, + fee: _extractValue(map, 2, Coin.fromCbor)!, + ttl: _extractValue(map, 3, SlotBigNum.fromCbor), + auxiliaryDataHash: _extractValue(map, 7, AuxiliaryDataHash.fromCbor), + validityStart: _extractValue(map, 8, SlotBigNum.fromCbor), + mint: _extractValue(map, 9, MultiAsset.fromCbor), + scriptDataHash: _extractValue(map, 11, ScriptDataHash.fromCbor), + collateralInputs: + _extractList(map, 13, TransactionInput.fromCbor)?.toSet(), + requiredSigners: + _extractList(map, 14, Ed25519PublicKeyHash.fromCbor)?.toSet(), + networkId: _extractValue( + map, + 15, + (value) => NetworkId.fromId((value as CborSmallInt).value), + ), + collateralReturn: _extractValue(map, 16, TransactionOutput.fromCbor), + totalCollateral: _extractValue(map, 17, Coin.fromCbor), + referenceInputs: + _extractList(map, 18, TransactionInput.fromCbor)?.toSet(), + ); + } catch (e) { + throw ArgumentError('Invalid CBOR input: $e'); + } + } + + static List? _extractList( + CborMap map, + int key, + T Function(CborValue) fromCbor, + ) { + final list = map[CborSmallInt(key)] as CborList?; + return list?.map(fromCbor).toList(); + } + + static T? _extractValue( + CborMap map, + int key, + T Function(CborValue) fromCbor, + ) { + final value = map[CborSmallInt(key)]; + return value != null ? fromCbor(value) : null; } /// Serializes the type as cbor. + @override CborValue toCbor() { return CborMap({ - const CborSmallInt(0): CborList([ - for (final input in inputs) input.toCbor(), - ]), - const CborSmallInt(1): CborList([ - for (final output in outputs) output.toCbor(), - ]), + const CborSmallInt(0): _toCborList(inputs), + const CborSmallInt(1): _toCborList(outputs), const CborSmallInt(2): fee.toCbor(), if (ttl != null) const CborSmallInt(3): ttl!.toCbor(), if (auxiliaryDataHash != null) const CborSmallInt(7): auxiliaryDataHash!.toCbor(), + if (validityStart != null) const CborSmallInt(8): validityStart!.toCbor(), + if (mint != null) const CborSmallInt(9): mint!.toCbor(), + if (scriptDataHash != null) + const CborSmallInt(11): scriptDataHash!.toCbor(), + if (collateralInputs != null && collateralInputs!.isNotEmpty) + const CborSmallInt(13): _toCborList(collateralInputs!), if (requiredSigners != null && requiredSigners!.isNotEmpty) - const CborSmallInt(14): CborList([ - for (final signer in requiredSigners!) signer.toCbor(), - ]), + const CborSmallInt(14): _toCborList(requiredSigners!), if (networkId != null) const CborSmallInt(15): CborSmallInt(networkId!.id), + if (collateralReturn != null) + const CborSmallInt(16): collateralReturn!.toCbor(), + if (totalCollateral != null) + const CborSmallInt(17): totalCollateral!.toCbor(), + if (referenceInputs != null && referenceInputs!.isNotEmpty) + const CborSmallInt(18): _toCborList(referenceInputs!), }); } + CborList _toCborList(Iterable iterable) { + return CborList([ + for (final item in iterable) item.toCbor(), + ]); + } + @override List get props => [ inputs, @@ -153,14 +228,21 @@ final class TransactionBody extends Equatable { fee, ttl, auxiliaryDataHash, + validityStart, + mint, + scriptDataHash, + collateralInputs, requiredSigners, networkId, + collateralReturn, + totalCollateral, + referenceInputs, ]; } /// The transaction output of a previous transaction, /// acts as input for the next transaction. -final class TransactionInput extends Equatable { +final class TransactionInput extends Equatable implements CborEncodable { /// The hash of the given transaction. final TransactionHash transactionId; @@ -186,6 +268,7 @@ final class TransactionInput extends Equatable { } /// Serializes the type as cbor. + @override CborValue toCbor() { return CborList([ transactionId.toCbor(), @@ -197,94 +280,16 @@ final class TransactionInput extends Equatable { List get props => [transactionId, index]; } -/// The transaction output which describes which [address] -/// will receive what [amount] of [Coin]. -final class TransactionOutput extends Equatable { - /// The address associated with the transaction. - final ShelleyAddress address; - - /// The leftover change from the previous transaction that can be spent. - final Balance amount; - - /// The default constructor for [TransactionOutput]. - const TransactionOutput({ - required this.address, - required this.amount, - }); - - /// Deserializes the type from cbor. - factory TransactionOutput.fromCbor(CborValue value) { - return _tryFromCborMap(value) ?? _tryFromCborList(value)!; - } - - /// This format is simpler and does not utilize the advanced features - /// of the Alonzo era (e.g., datum, native assets, or smart contract scripts). - /// It is consistent with basic transactions that could exist in earlier eras - /// or basic Alonzo-era transactions. - static TransactionOutput? _tryFromCborMap(CborValue value) { - try { - final map = value as CborMap; - final address = map[const CborSmallInt(0)]!; - final amount = map[const CborSmallInt(1)]!; - - return TransactionOutput( - address: ShelleyAddress.fromCbor(address), - amount: Balance.fromCbor(amount), - ); - } catch (error) { - return null; - } - } - - /// This format shows the use of the EUTXO model introduced in the Alonzo era, - /// with support for native assets and possibly other advanced features. - static TransactionOutput? _tryFromCborList(CborValue value) { - try { - final list = value as CborList; - final address = list[0]; - final amount = list[1]; - - return TransactionOutput( - address: ShelleyAddress.fromCbor(address), - amount: Balance.fromCbor(amount), - ); - } catch (error) { - return null; - } - } - - /// Serializes the type as cbor. - CborValue toCbor() { - return CborList([ - address.toCbor(), - amount.toCbor(), - ]); - } - - /// Return a copy of this output with [address] and [amount] if present. - TransactionOutput copyWith({ - ShelleyAddress? address, - Balance? amount, - }) { - return TransactionOutput( - address: address ?? this.address, - amount: amount ?? this.amount, - ); - } - - @override - List get props => [address, amount]; -} - /// The UTXO that can be used as an input in a new transaction. -final class TransactionUnspentOutput extends Equatable { +final class TransactionUnspentOutput extends Equatable + implements CborEncodable { /// The transaction output of a previous transaction, /// acts as input for the next transaction. final TransactionInput input; /// The transaction output which assigns the owner of given address /// with leftover change from previous transaction. - final TransactionOutput output; + final ShelleyMultiAssetTransactionOutput output; /// The default constructor for [TransactionUnspentOutput]. const TransactionUnspentOutput({ @@ -305,6 +310,7 @@ final class TransactionUnspentOutput extends Equatable { } /// Serializes the type as cbor. + @override CborValue toCbor() { return CborList([ input.toCbor(), @@ -317,7 +323,7 @@ final class TransactionUnspentOutput extends Equatable { } /// The transaction metadata as a list of key-value pairs (a map). -final class AuxiliaryData extends Equatable { +final class AuxiliaryData extends Equatable implements CborEncodable { /// The transaction metadata map. final Map map; @@ -331,6 +337,7 @@ final class AuxiliaryData extends Equatable { } /// Serializes the type as cbor. + @override CborValue toCbor() { return CborMap( map, diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/transaction_output.dart b/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/transaction_output.dart new file mode 100644 index 00000000000..3913609c835 --- /dev/null +++ b/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/transaction_output.dart @@ -0,0 +1,230 @@ +import 'dart:typed_data'; +import 'package:catalyst_cardano_serialization/src/address.dart'; +import 'package:catalyst_cardano_serialization/src/datum.dart'; +import 'package:catalyst_cardano_serialization/src/scripts.dart'; +import 'package:catalyst_cardano_serialization/src/types.dart'; +import 'package:cbor/cbor.dart'; +import 'package:equatable/equatable.dart'; +import 'package:pinenacl/api.dart'; + +/// Abstract base class representing a transaction output in Cardano. +/// A [ShelleyMultiAssetTransactionOutput] defines the destination address +/// and the amount of cryptocurrency being sent to that address. +/// +/// There are different implementations of Cardano's +/// TransactionOutputs depending on the era. +/// This class provides common functionality for both types. +/// +/// > Note: It does not support pure Shelley era outputs i.e. output with only +/// pure coin (int) type as amount. +sealed class ShelleyMultiAssetTransactionOutput extends Equatable + implements CborEncodable { + /// The destination address for the output. + final ShelleyAddress address; + + /// The amount of cryptocurrency being sent to the address. + final Balance amount; + + /// Constructor for the [ShelleyMultiAssetTransactionOutput] class, requiring + /// an address and amount. + const ShelleyMultiAssetTransactionOutput({ + required this.address, + required this.amount, + }); + + @override + List get props => [address, amount]; + + /// Abstract method for copying a transaction output with optional new values. + /// Subclasses must implement this method to allow for creating modified + /// copies of existing transaction outputs. + ShelleyMultiAssetTransactionOutput copyWith({ + ShelleyAddress? address, + Balance? amount, + }); +} + +/// Class representing a transaction output from the Pre-Babbage era in Cardano. +/// A [PreBabbageTransactionOutput] contains a datum hash, which refers to data +/// stored off-chain. This type of transaction output is used in Cardano before +/// the Babbage era. +/// +/// This class is primarily used for basic transaction outputs that do not +/// include advanced features like script references or inline data. +final class PreBabbageTransactionOutput + extends ShelleyMultiAssetTransactionOutput { + /// Optional datum hash associated with the transaction output. + /// The datum hash is a cryptographic hash that refers to off-chain data. + final DatumHash? datumHash; + + /// Constructor for the [PreBabbageTransactionOutput] class. + /// Requires an address, amount, and optionally a datum hash. + const PreBabbageTransactionOutput({ + required super.address, + required super.amount, + this.datumHash, + }); + + /// Factory constructor to create a [PreBabbageTransactionOutput] from a CBOR + /// encoded list. + /// The CBOR list should contain the address, amount, and optionally the datum + /// hash. + factory PreBabbageTransactionOutput._fromCborList(CborList list) { + final address = ShelleyAddress.fromCbor(list[0]); + final amount = Balance.fromCbor(list[1]); + final datumHash = list.length > 2 + ? DatumHash(Uint8List.fromList((list[2] as CborBytes).bytes)) + : null; + + return PreBabbageTransactionOutput( + address: address, + amount: amount, + datumHash: datumHash, + ); + } + + /// Converts the [PreBabbageTransactionOutput] to a CBOR-encoded value. + /// The CBOR list contains the address, amount, and optionally the datum hash. + @override + CborValue toCbor() { + final list = CborList([ + address.toCbor(), + amount.toCbor(), + if (datumHash != null) datumHash!.toCbor(), + ]); + return list; + } + + /// Method for creating a copy of the [PreBabbageTransactionOutput] with + /// optional new values. + /// This is useful for modifying existing transaction outputs without creating + /// a new instance from scratch. + @override + PreBabbageTransactionOutput copyWith({ + ShelleyAddress? address, + Balance? amount, + DatumHash? datumHash, + }) { + return PreBabbageTransactionOutput( + address: address ?? this.address, + amount: amount ?? this.amount, + datumHash: datumHash ?? this.datumHash, + ); + } + + /// Override of the [Equatable] properties for value comparison. + /// This allows comparing [PreBabbageTransactionOutput] instances based on + /// their address, amount, and optional datum hash. + @override + List get props => [address, amount, datumHash]; +} + +/// Class representing a transaction output from the Post-Alonzo era in Cardano. +/// A [TransactionOutput] can include additional fields like a datum option +/// and a script reference, supporting advanced features introduced after the +/// Alonzo hard fork. +/// +/// This class is used for creating transaction outputs with inline data or +/// references to +/// on-chain scripts. +/// > **Note**: In default, the latest era's transaction output is the +/// > [TransactionOutput] class. +final class TransactionOutput extends ShelleyMultiAssetTransactionOutput { + /// Optional datum option associated with the transaction output. + /// This can be either a datum hash (referencing off-chain data) or inline + /// data (stored on-chain). + final DatumOption? datumOption; + + /// Optional script reference associated with the transaction output. + /// The script reference allows for including a reference to a script in the + /// transaction output. + final ScriptRef? scriptRef; + + /// Constructor for the [TransactionOutput] class. + /// Requires an address, amount, and optionally a datum option and script + /// reference. + const TransactionOutput({ + required super.address, + required super.amount, + this.datumOption, + this.scriptRef, + }); + + /// Factory constructor to create a [ShelleyMultiAssetTransactionOutput] + /// from a CBOR-encoded value. + /// Depending on the structure of the CBOR value, it returns either a + /// [PreBabbageTransactionOutput] or [TransactionOutput]. + static ShelleyMultiAssetTransactionOutput fromCbor(CborValue value) { + try { + return switch (value) { + CborList _ => PreBabbageTransactionOutput._fromCborList(value), + CborMap _ => TransactionOutput._fromCborMap(value), + _ => throw ArgumentError('Invalid CBOR value for TransactionOutput'), + }; + } catch (e) { + throw ArgumentError( + 'Failed to decode ShelleyMultiAssetTransactionOutput: $e', + ); + } + } + + /// Factory constructor to create a [TransactionOutput] from a CBOR map. + /// The CBOR map should contain the address, amount, and optionally the datum + /// option and script reference. + factory TransactionOutput._fromCborMap(CborMap map) { + final address = ShelleyAddress.fromCbor(map[const CborSmallInt(0)]!); + final amount = Balance.fromCbor(map[const CborSmallInt(1)]!); + final datumOption = map[const CborSmallInt(2)] != null + ? DatumOption.fromCbor(map[const CborSmallInt(2)]!) + : null; + final scriptRef = map[const CborSmallInt(3)] != null + ? ScriptRef.fromCbor(map[const CborSmallInt(3)]!) + : null; + + return TransactionOutput( + address: address, + amount: amount, + datumOption: datumOption, + scriptRef: scriptRef, + ); + } + + /// Converts the [TransactionOutput] to a CBOR-encoded value. + /// The CBOR map contains the address, amount, and optionally the datum option + /// and script reference. + @override + CborValue toCbor() { + final map = CborMap({ + const CborSmallInt(0): address.toCbor(), + const CborSmallInt(1): amount.toCbor(), + }); + + if (datumOption != null) { + map[const CborSmallInt(2)] = datumOption!.toCbor(); + } + + if (scriptRef != null) { + map[const CborSmallInt(3)] = scriptRef!.toCbor(); + } + + return map; + } + + @override + TransactionOutput copyWith({ + ShelleyAddress? address, + Balance? amount, + DatumOption? datumOption, + ScriptRef? scriptRef, + }) { + return TransactionOutput( + address: address ?? this.address, + amount: amount ?? this.amount, + datumOption: datumOption ?? this.datumOption, + scriptRef: scriptRef ?? this.scriptRef, + ); + } + + @override + List get props => [address, amount, datumOption, scriptRef]; +} diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/types.dart b/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/types.dart index 4c5a2bd0d66..970ad2154de 100644 --- a/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/types.dart +++ b/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/types.dart @@ -3,6 +3,17 @@ import 'package:cbor/cbor.dart'; import 'package:convert/convert.dart'; import 'package:equatable/equatable.dart'; +/// An interface for classes that support CBOR serialization. +/// +// ignore: one_member_abstracts +abstract interface class CborEncodable { + /// Creates a new instance of [CborEncodable]. + const CborEncodable(); + + /// Converts this instance to its CBOR representation. + CborValue toCbor(); +} + /// Specifies on which network the code will run. enum NetworkId { /// The production network @@ -29,6 +40,14 @@ enum NetworkId { /// Specifies an amount of ADA in terms of lovelace. extension type const Coin(int value) { + /// The amount of lovelaces in one ADA. + static const int adaInLovelaces = 1000000; + + /// Creates a [Coin] from [amount] specified in ADAs. + factory Coin.fromAda(double amount) { + return Coin((amount * adaInLovelaces).toInt()); + } + /// Deserializes the type from cbor. factory Coin.fromCbor(CborValue value) { return Coin((value as CborSmallInt).value); @@ -37,6 +56,9 @@ extension type const Coin(int value) { /// Serializes the type as cbor. CborValue toCbor() => CborSmallInt(value); + /// Converts lovelaces to ADAs + double get ada => value / adaInLovelaces; + /// Adds [other] value to this value and returns a new [Coin]. Coin operator +(Coin other) => Coin(value + other.value); @@ -75,7 +97,7 @@ extension type const SlotBigNum(int value) { } /// Represents the balance of the wallet in terms of [Coin]. -final class Balance extends Equatable { +final class Balance extends Equatable implements CborEncodable { /// The amount of [Coin] that the wallet holds. final Coin coin; @@ -116,6 +138,7 @@ final class Balance extends Equatable { } /// Serializes the type as cbor. + @override CborValue toCbor() { final multiAsset = this.multiAsset; if (multiAsset == null) { @@ -198,7 +221,7 @@ final class Balance extends Equatable { } /// Holds native assets minted with [PolicyId]. -final class MultiAsset extends Equatable { +final class MultiAsset extends Equatable implements CborEncodable { /// The map of native assets. /// /// The [Coin] is used to describe the amount of native assets @@ -237,6 +260,7 @@ final class MultiAsset extends Equatable { } /// Serializes the type as cbor. + @override CborValue toCbor() { return CborMap({ for (final policy in bundle.entries) @@ -307,7 +331,8 @@ extension type AssetName(String name) { /// Deserializes the type from cbor. factory AssetName.fromCbor(CborValue value) { final bytes = (value as CborBytes).bytes; - return AssetName(CborString.fromUtf8(bytes).toString()); + // FIXME(ilap): Handle non ASCII/UTF-8 characters. + return AssetName(CborString.fromUtf8(bytes).toString(allowMalformed: true)); } /// Serializes the type as cbor. diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/witness.dart b/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/witness.dart index c040c6be802..6fe6aaf4e43 100644 --- a/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/witness.dart +++ b/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/witness.dart @@ -1,37 +1,162 @@ -import 'package:catalyst_cardano_serialization/src/signature.dart'; +import 'package:catalyst_cardano_serialization/catalyst_cardano_serialization.dart'; import 'package:cbor/cbor.dart'; import 'package:equatable/equatable.dart'; +/// Witnesses that allow for various types of transactions on the Cardano +/// blockchain. +enum WitnessType { + /// A witness that contains a verification key. + vkeyWitness(0), + + /// A native script witness. + nativeScript(1), + + /// A bootstrap witness. + bootstrapWitness(2), + + /// A Plutus v1 script witness. + plutusV1Script(3), + + /// A Plutus data witness. + plutusData(4), + + /// A redeemer witness. + redeemers(5), + + /// A Plutus v2 script witness. + plutusV2Script(6), + + /// A Plutus v3 script witness. + plutusV3Script(7); + + /// The integer value associated with the witness type. + final int value; + + /// Constructs a new `WitnessType` with the given integer value. + const WitnessType(this.value); +} + /// A set of witnesses that sign the transaction. -final class TransactionWitnessSet extends Equatable { +final class TransactionWitnessSet extends Equatable implements CborEncodable { /// The witnesses that sign the transaction. final Set vkeyWitnesses; + /// The native scripts. + final Set nativeScripts; + + /// The Plutus V1 scripts. + final Set plutusV1Scripts; + + /// Redeemers for Plutus scripts. + // FIXME(ilap): implement proper redeemers type. + final Set redeemers; + + /// The Plutus V2 scripts. + final Set plutusV2Scripts; + + /// The Plutus V3 scripts. + final Set plutusV3Scripts; + /// The default constructor for [TransactionWitnessSet]. - const TransactionWitnessSet({required this.vkeyWitnesses}); + const TransactionWitnessSet({ + this.vkeyWitnesses = const {}, + this.nativeScripts = const {}, + this.plutusV1Scripts = const {}, + this.redeemers = const {}, + this.plutusV2Scripts = const {}, + this.plutusV3Scripts = const {}, + }); /// Deserializes the type from cbor. factory TransactionWitnessSet.fromCbor(CborValue value) { final map = value as CborMap; return TransactionWitnessSet( - vkeyWitnesses: map.values.map(VkeyWitness.fromCbor).toSet(), + vkeyWitnesses: _getWitnesses( + map, + WitnessType.vkeyWitness, + VkeyWitness.fromCbor, + ), + nativeScripts: _getWitnesses( + map, + WitnessType.nativeScript, + NativeScript.fromCbor, + ), + plutusV1Scripts: _getWitnesses( + map, + WitnessType.plutusV1Script, + PlutusV1Script.fromCbor, + ), + redeemers: _getWitnesses( + map, + WitnessType.redeemers, + Redeemer.fromCbor, + ), + plutusV2Scripts: _getWitnesses( + map, + WitnessType.plutusV2Script, + PlutusV2Script.fromCbor, + ), + plutusV3Scripts: _getWitnesses( + map, + WitnessType.plutusV3Script, + PlutusV3Script.fromCbor, + ), ); } + static Set _getWitnesses( + CborMap map, + WitnessType type, + T Function(CborValue) fromCbor, + ) { + final value = map[CborSmallInt(type.value)] as CborList?; + return value?.map(fromCbor).toSet() ?? {}; + } + /// Serializes the type as cbor. + @override CborValue toCbor() { return CborMap({ - for (final vkey in vkeyWitnesses.indexed) - CborSmallInt(vkey.$1): vkey.$2.toCbor(), + ..._generateCborPair(WitnessType.vkeyWitness, vkeyWitnesses), + ..._generateCborPair(WitnessType.nativeScript, nativeScripts), + ..._generateCborPair(WitnessType.plutusV1Script, plutusV1Scripts), + ..._generateCborPair(WitnessType.redeemers, redeemers), + ..._generateCborPair(WitnessType.plutusV2Script, plutusV2Scripts), + ..._generateCborPair(WitnessType.plutusV3Script, plutusV3Scripts), }); } + Map _generateCborPair( + WitnessType witnessType, + Set witnesses, + ) { + if (witnesses.isNotEmpty) { + return { + CborSmallInt(witnessType.value): CborList( + witnesses + .map( + (value) => value.toCbor(), + ) + .toList(), + ), + }; + } + return {}; + } + @override - List get props => [vkeyWitnesses]; + List get props => [ + vkeyWitnesses, + nativeScripts, + plutusV1Scripts, + redeemers, + plutusV2Scripts, + plutusV3Scripts, + ]; } /// The transaction witness with a [signature] of the transaction. -final class VkeyWitness extends Equatable { +final class VkeyWitness extends Equatable implements CborEncodable { /// The public key of the witness. final Ed25519PublicKey vkey; @@ -56,9 +181,8 @@ final class VkeyWitness extends Equatable { /// Deserializes the type from cbor. factory VkeyWitness.fromCbor(CborValue value) { final list = value as CborList; - final innerList = list[0] as CborList; - final vkey = innerList[0]; - final signature = innerList[1]; + final vkey = list[0]; + final signature = list[1]; return VkeyWitness( vkey: Ed25519PublicKey.fromCbor(vkey), @@ -67,12 +191,11 @@ final class VkeyWitness extends Equatable { } /// Serializes the type as cbor. + @override CborValue toCbor() { return CborList([ - CborList([ - vkey.toCbor(), - signature.toCbor(), - ]), + vkey.toCbor(), + signature.toCbor(), ]); } diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/pubspec.yaml b/catalyst_voices_packages/catalyst_cardano_serialization/pubspec.yaml index 62da9af3c01..5a4c26478e1 100644 --- a/catalyst_voices_packages/catalyst_cardano_serialization/pubspec.yaml +++ b/catalyst_voices_packages/catalyst_cardano_serialization/pubspec.yaml @@ -3,23 +3,24 @@ description: Dart package providing serialization/deserialization for common str repository: https://github.com/input-output-hk/catalyst-voices/tree/main/catalyst_voices_packages/catalyst_cardano_serialization issue_tracker: https://github.com/input-output-hk/catalyst-voices/issues topics: [blockchain, cardano, cryptocurrency, wallet] -version: 0.1.3 +version: 0.4.0 environment: - sdk: ">=3.3.4 <4.0.0" + sdk: ">=3.5.0 <4.0.0" + flutter: ">=3.24.1" dependencies: bech32: ^0.2.2 bip32_ed25519: ^0.6.0 - catalyst_compression: ^0.1.1 - catalyst_compression_web: ^0.1.1 + catalyst_compression: ^0.3.0 + catalyst_compression_web: ^0.3.0 cbor: ^6.2.0 convert: ^3.1.1 cryptography: ^2.7.0 equatable: ^2.0.5 pinenacl: ^0.6.0 + ulid: ^2.0.0 dev_dependencies: - catalyst_analysis: - path: ../catalyst_analysis + catalyst_analysis: ^2.0.0 test: ^1.24.9 diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/test/fees_test.dart b/catalyst_voices_packages/catalyst_cardano_serialization/test/fees_test.dart index da84355c50a..4ed5f2898a6 100644 --- a/catalyst_voices_packages/catalyst_cardano_serialization/test/fees_test.dart +++ b/catalyst_voices_packages/catalyst_cardano_serialization/test/fees_test.dart @@ -13,7 +13,7 @@ void main() { ); final tx = fullSignedTestTransaction(); - expect(linearFee.minNoScriptFee(tx), equals(177953)); + expect(linearFee.minNoScriptFee(tx), equals(177777)); }); test('minFeeNoScript with constant fee only', () { @@ -33,7 +33,7 @@ void main() { ); final tx = fullSignedTestTransaction(); - expect(linearFee.minNoScriptFee(tx), equals(22572)); + expect(linearFee.minNoScriptFee(tx), equals(22396)); }); }); } diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/test/hashes_test.dart b/catalyst_voices_packages/catalyst_cardano_serialization/test/hashes_test.dart index f7574757919..8136b31c12b 100644 --- a/catalyst_voices_packages/catalyst_cardano_serialization/test/hashes_test.dart +++ b/catalyst_voices_packages/catalyst_cardano_serialization/test/hashes_test.dart @@ -70,7 +70,7 @@ void main() { expect( hash, equals( - TransactionInputsHash.fromHex('26497de5e0c8fe2e4b0c85651f96b3c9'), + TransactionInputsHash.fromHex('7336167445e63489f9c477367fd7a529'), ), ); }); diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/test/rbac/auth_token_test.dart b/catalyst_voices_packages/catalyst_cardano_serialization/test/rbac/auth_token_test.dart new file mode 100644 index 00000000000..6e327179b7a --- /dev/null +++ b/catalyst_voices_packages/catalyst_cardano_serialization/test/rbac/auth_token_test.dart @@ -0,0 +1,88 @@ +import 'dart:convert'; + +import 'package:catalyst_cardano_serialization/catalyst_cardano_serialization.dart'; +import 'package:cbor/cbor.dart'; +import 'package:cryptography/cryptography.dart'; +import 'package:test/test.dart'; +import 'package:ulid/ulid.dart'; + +// The certificate provided in the request +final _c509Cert = C509Certificate.fromHex( + ''' +8B004301F50D6B524643207465737420 +43411A63B0CD001A6955B90047010123 +456789AB01582102B1216AB96E5B3B33 +40F5BDF02E693F16213A04525ED44450 +B1019C2DFD3838AB010058406FC90301 +5259A38C0800A3D0B2969CA21977E8ED +6EC344964D4E1C6B37C8FB541274C3BB +81B2F53073C5F101A5AC2A92886583B6 +A2679B6E682D2A26945ED0B2 +''' + .replaceAll('\n', ''), +); + +void main() { + group(AuthToken, () { + final kid = CertificateHash.fromC509Certificate(_c509Cert); + final privateKey = Ed25519PrivateKey.seeded(0); + final timestamp = DateTime.utc(2023); + + test('Generate AuthToken', () async { + final token = await AuthToken.generate( + kid: kid, + privateKey: privateKey, + timestamp: timestamp, + ); + + expect(token, startsWith('${AuthToken.prefix}.')); + + // Decode the base64 token part + final parts = token.split('.'); + expect(parts.length, 2); + + final base64Token = parts[1]; + final decodedToken = base64Decode(base64Token); + + final decodedTokenAsArray = [ + 0x83, + ...decodedToken, + ]; + + // Decode the CBOR token + final decodedCbor = cbor.decode(decodedTokenAsArray) as CborList; + expect(decodedCbor.length, 3); + + final decodedKid = decodedCbor[0] as CborBytes; + final decodedUlid = decodedCbor[1] as CborBytes; + final decodedSignature = decodedCbor[2] as CborBytes; + + expect(decodedKid.bytes, (kid.toCbor() as CborBytes).bytes); + expect( + Ulid.fromBytes(decodedUlid.bytes).toMillis(), + timestamp.millisecondsSinceEpoch, + ); + + // Verify the signature + final toBeSigned = [ + ...cbor.encode(kid.toCbor()), + ...cbor.encode(decodedUlid), + ]; + + final publicKey = await privateKey.derivePublicKey(); + + final isValid = await Ed25519().verify( + toBeSigned, + signature: Signature( + decodedSignature.bytes, + publicKey: SimplePublicKey( + publicKey.bytes, + type: KeyPairType.ed25519, + ), + ), + ); + + expect(isValid, isTrue); + }); + }); +} diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/test/scripts_test.dart b/catalyst_voices_packages/catalyst_cardano_serialization/test/scripts_test.dart new file mode 100644 index 00000000000..ca5a4ccf530 --- /dev/null +++ b/catalyst_voices_packages/catalyst_cardano_serialization/test/scripts_test.dart @@ -0,0 +1,232 @@ +import 'dart:typed_data'; +import 'package:catalyst_cardano_serialization/catalyst_cardano_serialization.dart'; +import 'package:convert/convert.dart'; +import 'package:test/test.dart'; + +/// Native scripts hashes generated by `cardano-cli` using Cardano's Simple +/// scripts [examples](https://developers.cardano.org/docs/get-started/cardano-cli/simple-scripts/) +const nativeScriptsJSON = [ + { + 'json': { + 'type': 'sig', + 'keyHash': 'e09d36c79dec9bd1b3d9e152247701cd0bb860b5ebfd1de8abb6735a', + }, + 'hash': '208bdcaf2d83ae026964e23659c703a377473168a39cbdc2b0241115', + }, + { + 'json': { + 'type': 'all', + 'scripts': [ + { + 'type': 'sig', + 'keyHash': 'e09d36c79dec9bd1b3d9e152247701cd0bb860b5ebfd1de8abb6735a', + }, + { + 'type': 'sig', + 'keyHash': 'a687dcc24e00dd3caafbeb5e68f97ca8ef269cb6fe971345eb951756', + }, + { + 'type': 'sig', + 'keyHash': '0bd1d702b2e6188fe0857a6dc7ffb0675229bab58c86638ffa87ed6d', + } + ], + }, + 'hash': '477e52b3116b62fe8cd34a312615f5fcd678c94e1d6cdb86c1a3964c', + }, + { + 'json': { + 'type': 'all', + 'scripts': [ + {'type': 'after', 'slot': 1000}, + { + 'type': 'sig', + 'keyHash': '966e394a544f242081e41d1965137b1bb412ac230d40ed5407821c37', + } + ], + }, + 'hash': '120125c6dea2049988eb0dc8ddcc4c56dd48628d45206a2d0bc7e55b', + }, + { + 'json': { + 'type': 'atLeast', + 'required': 2, + 'scripts': [ + { + 'type': 'sig', + 'keyHash': '2f3d4cf10d0471a1db9f2d2907de867968c27bca6272f062cd1c2413', + }, + { + 'type': 'sig', + 'keyHash': 'f856c0c5839bab22673747d53f1ae9eed84afafb085f086e8e988614', + }, + { + 'type': 'sig', + 'keyHash': 'b275b08c999097247f7c17e77007c7010cd19f20cc086ad99d398538', + } + ], + }, + 'hash': '1e3e60975af4971f7cc02ed4d90c87abaafd2dd070a42eafa6f5e939', + }, + { + 'json': { + 'type': 'any', + 'scripts': [ + { + 'type': 'sig', + 'keyHash': 'b275b08c999097247f7c17e77007c7010cd19f20cc086ad99d398538', + }, + { + 'type': 'all', + 'scripts': [ + {'type': 'before', 'slot': 3000}, + { + 'type': 'sig', + 'keyHash': + '966e394a544f242081e41d1965137b1bb412ac230d40ed5407821c37', + } + ], + } + ], + }, + 'hash': '6519f942518b8761f4b02e1403365b7d7befae1eb488b7fffcbab33f', + }, + { + 'json': { + 'type': 'any', + 'scripts': [ + { + 'type': 'sig', + 'keyHash': 'd92b712d1882c3b0f75b6f677e0b2cbef4fbc8b8121bb9dde324ff09', + }, + { + 'type': 'sig', + 'keyHash': '4d780ed1bfc88cbd4da3f48de91fe728c3530d662564bf5a284b5321', + }, + { + 'type': 'sig', + 'keyHash': '3a94d6d4e786a3f5d439939cafc0536f6abc324fb8404084d6034bf8', + } + ], + }, + 'hash': '7004b5785308380e2dac2955d234b60aa4b786057dd5a93984439d32', + } +]; + +/// Plutus V2 scripts generated by [aiken](https://github.com/aiken-lang/aiken)'s build. +const plutusV2ScriptsJSON = [ + { + 'compiledCode': '590130010000323232323232323223223225333008533009490145546' + '8652054696d65732030332f4a616e2f32303039204368616e63656c6c' + '6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757' + '420666f722062616e6b730014a02930a99804a4811856616c69646174' + '6f722072657475726e65642066616c73650013656533333300b001153' + '330063370e900018041baa0011533300a300937540022930a99803802' + '0b0a998038020b0a998038020b0a998038020b0a998038020b0a99803' + '8020b29999998048008a99980219b8748000c018dd50008a999804180' + '39baa001149854cc01400c5854cc01400c5854cc01400c5854cc01400' + 'c5854cc01400c5854cc01400c592401085f723a20566f696400490108' + '5f643a20566f6964005734ae7155ceaab9e5573eae91', + 'hash': '2e72caa0f64fc6f9cd53ae781154fbbb135c7625f10fcdf603566bc9', + }, + { + 'compiledCode': '589101000032323232323232232253330054a22930a99803248118566' + '16c696461746f722072657475726e65642066616c7365001365653333' + '33008001153330033370e900018029baa001153330073006375400229' + '30a998020010b0a998020010b0a998020010b0a998020010b0a998020' + '010b0a998020010b2481085f723a20566f6964005734ae7155ceaab9e' + '5573eae91', + 'hash': '12b96851f48bac3b2758f3e221208d584d043ec4e6715a9955d5209d', + }, + { + 'compiledCode': '590132010000323232323232323232322232322533300732325333009' + '3005300b3754601e60200042a666012600a60166ea8c8cc0040040088' + '94ccc03c0045300103d87a800013232533300d3375e602660206ea800' + '802c4cdd2a40006602400497ae01330040040013013002301100114a2' + '294054cc02924121657870656374204d696e74285f706f6c6963795f6' + '96429203d20707572706f736500163758601c60166ea8c038004c028d' + 'd50008a4c2a6601092011856616c696461746f722072657475726e656' + '42066616c73650013656533333300d002153330053001300737540042' + 'a66601260106ea8008526153300600416153300600416153300600416' + '153300600416153300600416153300600416370e9000249085f723a20' + '566f6964005734ae7155ceaab9e5573eae815d0aba257481', + 'hash': 'cb9d6b7cabd2b8b944c857abeb0518c155ba500fcc80c086f859b8ea', + }, +]; + +/// Plutus scripts' hashes generated by `cardano-cli`. +/// > **Note**: The cborHex of a `cardano-cli` outputs are double decoded. +const doubleEncodedPlutusScripts = [ + { + 'plutusScript': { + 'type': 'PlutusScriptV1', + 'description': '', + 'cborHex': '454401020304', + }, + 'hash': '4cf0813cc182d96de6b469751636defeb91421fda73e8916cb951653', + }, + { + 'plutusScript': { + 'type': 'PlutusScriptV2', + 'description': '', + 'cborHex': '454401020304', + }, + 'hash': '9d59620cf11f039336221486bb7e6b10adb59f308f6710f050bbc02d', + }, + { + 'plutusScript': { + 'type': 'PlutusScriptV3', + 'description': '', + 'cborHex': '454401020304', + }, + 'hash': '42ad9d5ffaca5d74e97c3f4ffc8f8fae8876362ed86f24db328529f3', + } +]; + +void main() { + group(NativeScript, () { + test('hashes', () { + for (final script in nativeScriptsJSON) { + final json = script['json']! as Map; + final simpleScript = NativeScript.fromJSON(json); + + expect( + simpleScript.hash, + equals(hex.decode(script['hash']! as String)), + ); + } + }); + }); + + group(PlutusScript, () { + test('hashes (V2 only)', () { + for (final script in plutusV2ScriptsJSON) { + final scriptCborBytes = + Uint8List.fromList(hex.decode(script['compiledCode']!)); + final plutusScript = PlutusV2Script(scriptCborBytes); + + expect(plutusScript.hash, equals(hex.decode(script['hash']!))); + } + }); + }); + + group(PlutusScript, () { + test('hashes (double decoded scripts)', () { + for (final json in doubleEncodedPlutusScripts) { + final script = json['plutusScript']! as Map; + final hash = json['hash']! as String; + final type = script['type'] as String; + final cborHex = script['cborHex'] as String; + final cborBytes = Uint8List.fromList(hex.decode(cborHex)); + + final plutusScript = switch (type) { + 'PlutusScriptV1' => PlutusV1Script(cborBytes), + 'PlutusScriptV2' => PlutusV2Script(cborBytes), + 'PlutusScriptV3' => PlutusV3Script(cborBytes), + _ => throw Exception('Unknown script type: $type'), + }; + + expect(plutusScript.hash, equals(hex.decode(hash))); + } + }); + }); +} diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/test/test_utils/test_data.dart b/catalyst_voices_packages/catalyst_cardano_serialization/test/test_utils/test_data.dart index 764f56ea1b2..829f338872c 100644 --- a/catalyst_voices_packages/catalyst_cardano_serialization/test/test_utils/test_data.dart +++ b/catalyst_voices_packages/catalyst_cardano_serialization/test/test_utils/test_data.dart @@ -2,6 +2,7 @@ import 'package:catalyst_cardano_serialization/src/address.dart'; import 'package:catalyst_cardano_serialization/src/hashes.dart'; import 'package:catalyst_cardano_serialization/src/signature.dart'; import 'package:catalyst_cardano_serialization/src/transaction.dart'; +import 'package:catalyst_cardano_serialization/src/transaction_output.dart'; import 'package:catalyst_cardano_serialization/src/types.dart'; import 'package:catalyst_cardano_serialization/src/witness.dart'; import 'package:cbor/cbor.dart'; @@ -36,7 +37,7 @@ TransactionUnspentOutput testUtxo() { transactionId: testTransactionHash, index: 0, ), - output: TransactionOutput( + output: PreBabbageTransactionOutput( address: ShelleyAddress.fromBech32( 'addr_test1qpu5vlrf4xkxv2qpwngf6cjhtw542ayty80v8dyr49rf5' 'ewvxwdrt70qlcpeeagscasafhffqsxy36t90ldv06wqrk2qum8x5w', @@ -51,11 +52,11 @@ Transaction minimalUnsignedTestTransaction() { body: TransactionBody( inputs: {testUtxo().input}, outputs: [ - TransactionOutput( + PreBabbageTransactionOutput( address: testnetAddr, amount: const Balance(coin: Coin(1000000)), ), - TransactionOutput( + PreBabbageTransactionOutput( address: testnetChangeAddr, amount: const Balance(coin: Coin(9998833003)), ), @@ -63,9 +64,7 @@ Transaction minimalUnsignedTestTransaction() { fee: const Coin(166997), ), isValid: true, - witnessSet: const TransactionWitnessSet( - vkeyWitnesses: {}, - ), + witnessSet: const TransactionWitnessSet(), ); } @@ -75,11 +74,11 @@ Transaction minimalSignedTestTransaction() { body: TransactionBody( inputs: {testUtxo().input}, outputs: [ - TransactionOutput( + PreBabbageTransactionOutput( address: testnetAddr, amount: const Balance(coin: Coin(1000000)), ), - TransactionOutput( + PreBabbageTransactionOutput( address: testnetChangeAddr, amount: const Balance(coin: Coin(9998833003)), ), @@ -116,11 +115,11 @@ Transaction fullUnsignedTestTransaction() { body: TransactionBody( inputs: {testUtxo().input}, outputs: [ - TransactionOutput( + PreBabbageTransactionOutput( address: testnetAddr, amount: const Balance(coin: Coin(1000000)), ), - TransactionOutput( + PreBabbageTransactionOutput( address: testnetChangeAddr, amount: const Balance(coin: Coin(9998832827)), ), @@ -129,19 +128,19 @@ Transaction fullUnsignedTestTransaction() { ttl: const SlotBigNum(41193), auxiliaryDataHash: AuxiliaryDataHash.fromAuxiliaryData(auxiliaryData), requiredSigners: { - Ed25519PublicKey.fromBytes( - hex.decode( - '3311ca404fcf22c91d607ace285d70e2' - '263a1b81745c39673080329bd1a3f56e', + Ed25519PublicKeyHash.fromPublicKey( + Ed25519PublicKey.fromBytes( + hex.decode( + '3311ca404fcf22c91d607ace285d70e2' + '263a1b81745c39673080329bd1a3f56e', + ), ), ), }, networkId: NetworkId.testnet, ), isValid: true, - witnessSet: const TransactionWitnessSet( - vkeyWitnesses: {}, - ), + witnessSet: const TransactionWitnessSet(), auxiliaryData: auxiliaryData, ); } @@ -154,11 +153,11 @@ Transaction fullSignedTestTransaction() { body: TransactionBody( inputs: {testUtxo().input}, outputs: [ - TransactionOutput( + PreBabbageTransactionOutput( address: testnetAddr, amount: const Balance(coin: Coin(1000000)), ), - TransactionOutput( + PreBabbageTransactionOutput( address: testnetChangeAddr, amount: const Balance(coin: Coin(9998832827)), ), @@ -167,10 +166,12 @@ Transaction fullSignedTestTransaction() { ttl: const SlotBigNum(41193), auxiliaryDataHash: AuxiliaryDataHash.fromAuxiliaryData(auxiliaryData), requiredSigners: { - Ed25519PublicKey.fromBytes( - hex.decode( - '3311ca404fcf22c91d607ace285d70e2' - '263a1b81745c39673080329bd1a3f56e', + Ed25519PublicKeyHash.fromPublicKey( + Ed25519PublicKey.fromBytes( + hex.decode( + '3311ca404fcf22c91d607ace285d70e2' + '263a1b81745c39673080329bd1a3f56e', + ), ), ), }, diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/test/transaction_output_test.dart b/catalyst_voices_packages/catalyst_cardano_serialization/test/transaction_output_test.dart new file mode 100644 index 00000000000..a55aa5f6d11 --- /dev/null +++ b/catalyst_voices_packages/catalyst_cardano_serialization/test/transaction_output_test.dart @@ -0,0 +1,158 @@ +import 'dart:typed_data'; + +import 'package:catalyst_cardano_serialization/src/datum.dart'; +import 'package:catalyst_cardano_serialization/src/scripts.dart'; +import 'package:catalyst_cardano_serialization/src/transaction_output.dart'; +import 'package:catalyst_cardano_serialization/src/types.dart'; +import 'package:cbor/cbor.dart'; +import 'package:test/test.dart'; + +import 'test_utils/test_data.dart'; + +void main() { + const value = Balance(coin: Coin(1000000)); + final datumHash = DatumHash(Uint8List.fromList([1, 2, 3, 4, 5, 6])); + final datumData = Data(CborBytes(Uint8List.fromList([1, 2, 3, 4, 5, 6]))); + final datumOptionHash = DatumOption(datumHash); + final datumOptionData = DatumOption(datumData); + final script = PlutusV2Script(Uint8List.fromList([0x43, 0x01, 0x02, 0x03])); + final scriptRef = ScriptRef(script); + + group('TransactionOutput encoding/decoding Tests', () { + test('Shelley-Ma era transaction output', () { + final output = TransactionOutput(address: testnetAddr, amount: value); + final cbor = output.toCbor(); + final decoded = TransactionOutput.fromCbor(cbor); + expect(decoded, equals(output)); + expect(decoded.toCbor(), equals(cbor)); + }); + + test('Alonzo era transaction output with datum hash', () { + final output = PreBabbageTransactionOutput( + address: testnetAddr, + amount: value, + datumHash: datumHash, + ); + final cbor = output.toCbor(); + final decoded = TransactionOutput.fromCbor(cbor); + expect(decoded, equals(output)); + expect(decoded.toCbor(), equals(cbor)); + }); + + test('Babbage era transaction output with datum option hash', () { + final output = TransactionOutput( + address: testnetAddr, + amount: value, + datumOption: datumOptionHash, + ); + final cbor = output.toCbor(); + final decoded = TransactionOutput.fromCbor(cbor); + expect(decoded, equals(output)); + expect(decoded.toCbor(), equals(cbor)); + }); + + test('Babbage era transaction output with datum option data', () { + final output = TransactionOutput( + address: testnetAddr, + amount: value, + datumOption: datumOptionData, + ); + final cbor = output.toCbor(); + final decoded = TransactionOutput.fromCbor(cbor); + expect(decoded, equals(output)); + expect(decoded.toCbor(), equals(cbor)); + }); + + test('Babbage era transaction output with datum option data and script ref', + () { + final output = TransactionOutput( + address: testnetAddr, + amount: value, + datumOption: datumOptionData, + scriptRef: scriptRef, + ); + final cbor = output.toCbor(); + final decoded = TransactionOutput.fromCbor(cbor); + expect(decoded, equals(output)); + expect(decoded.toCbor(), equals(cbor)); + }); + + test('Babbage era transaction output with datum option hash and script ref', + () { + final output = TransactionOutput( + address: testnetAddr, + amount: value, + datumOption: datumOptionHash, + scriptRef: scriptRef, + ); + final cbor = output.toCbor(); + final decoded = TransactionOutput.fromCbor(cbor); + expect(decoded, equals(output)); + expect(decoded.toCbor(), equals(cbor)); + }); + + test('Babbage era transaction output only with script ref', () { + final output = TransactionOutput( + address: testnetAddr, + amount: value, + scriptRef: scriptRef, + ); + final cbor = output.toCbor(); + final decoded = TransactionOutput.fromCbor(cbor); + expect(decoded, equals(output)); + expect(decoded.toCbor(), equals(cbor)); + }); + }); + + group('TransactionOutput minimal validation Tests', () { + test('Invalid CBOR input throws error', () { + final invalidCbor = CborList([const CborSmallInt(1)]); + + expect( + () => TransactionOutput.fromCbor(invalidCbor), + throwsArgumentError, + ); + }); + + test('Invalid datum option type throws error', () { + final invalidCbor = CborList([ + const CborSmallInt(3), // Invalid type + CborBytes([1, 2, 3]), + ]); + + expect( + () => DatumOption.fromCbor(invalidCbor), + throwsA(isA()), + ); + }); + + test('Equality and hashCode', () { + final output1 = + PreBabbageTransactionOutput(address: testnetAddr, amount: value); + final output2 = + PreBabbageTransactionOutput(address: testnetAddr, amount: value); + + expect(output1, equals(output2)); + expect(output1.hashCode, equals(output2.hashCode)); + }); + + test('CopyWith functionality', () { + final original = TransactionOutput( + address: testnetAddr, + amount: value, + datumOption: datumOptionHash, + ); + + final modified = original.copyWith( + datumOption: datumOptionData, + scriptRef: scriptRef, + ); + + expect(modified.datumOption, equals(datumOptionData)); + expect(modified.scriptRef, equals(scriptRef)); + expect(modified.address, equals(testnetAddr)); + expect(modified.amount, equals(value)); + expect(modified.datumOption, isNot(equals(datumOptionHash))); + }); + }); +} diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/test/transaction_test.dart b/catalyst_voices_packages/catalyst_cardano_serialization/test/transaction_test.dart index b6776a96fb4..33638315e7b 100644 --- a/catalyst_voices_packages/catalyst_cardano_serialization/test/transaction_test.dart +++ b/catalyst_voices_packages/catalyst_cardano_serialization/test/transaction_test.dart @@ -15,17 +15,16 @@ void main() { '4b3bfa1bb83b1a000f424082583900c035332d2dcba35744e880729198459e32c50e' 'b3e179b1fa2247348c80b846ad416f120db94c1a401992950b11b9bc7d65dbb3424c' '0f8de41b0000000253fa14bb021a00028d050319a0e907582057b9d4976bc8017e5b' - '95c6996bac1749765e188c990b5c705a65c78f8349227d0e8158203311ca404fcf22' - 'c91d607ace285d70e2263a1b81745c39673080329bd1a3f56e0f00a1008182582033' - '11ca404fcf22c91d607ace285d70e2263a1b81745c39673080329bd1a3f56e5840f5' - 'eb006f048fdfa9b81b0fe3abee1ce1f1a75789dc21088b23ebf95c76b050ad157a49' - '7999e083e1957c2a3d730a07a5b2aef4a755783c9ce778c02c4a08970ff5a4016454' - '6573740246aabbccddeeff031903e50482a50081825820afcf8497561065afe1ca62' - '3823508753cc580eb575ac8f1d6cfaa18c3ceeac010001818258390080f9e2c88e6c' - '817008f3a812ed889b4a4da8e0bd103f86e7335422aa122a946b9ad3d2ddf029d3a8' - '28f0468aece76895f15c9efbd69b42771a00df1560021a0002e63003182f075820bd' - 'c2b27e6869aa9a5fa23a1f1fd3a87025d8703df4fd7b120d058c839dc0415c82a101' - '41aa80', + '95c6996bac1749765e188c990b5c705a65c78f8349227d0e81581cc035332d2dcba3' + '5744e880729198459e32c50eb3e179b1fa2247348c0f00a100818258203311ca404f' + 'cf22c91d607ace285d70e2263a1b81745c39673080329bd1a3f56e5840f5eb006f04' + '8fdfa9b81b0fe3abee1ce1f1a75789dc21088b23ebf95c76b050ad157a497999e083' + 'e1957c2a3d730a07a5b2aef4a755783c9ce778c02c4a08970ff5a401645465737402' + '46aabbccddeeff031903e50482a50081825820afcf8497561065afe1ca6238235087' + '53cc580eb575ac8f1d6cfaa18c3ceeac010001818258390080f9e2c88e6c817008f3' + 'a812ed889b4a4da8e0bd103f86e7335422aa122a946b9ad3d2ddf029d3a828f0468a' + 'ece76895f15c9efbd69b42771a00df1560021a0002e63003182f075820bdc2b27e68' + '69aa9a5fa23a1f1fd3a87025d8703df4fd7b120d058c839dc0415c82a10141aa80', ); }); @@ -36,19 +35,18 @@ void main() { test('full unsigned transaction serialized to cbor', () { _testTransactionSerialization( fullUnsignedTestTransaction(), - '84a700818258204c1fbc5433ec764164945d736a09dc087d59ff30e64d26d462ff85' - '70cd4be9a700018282581d6082e016828989cd9d809b50d6976d9efa9bc5b2c1a78d' - '4b3bfa1bb83b1a000f424082583900c035332d2dcba35744e880729198459e32c50e' - 'b3e179b1fa2247348c80b846ad416f120db94c1a401992950b11b9bc7d65dbb3424c' - '0f8de41b0000000253fa14bb021a00028d050319a0e907582057b9d4976bc8017e5b' - '95c6996bac1749765e188c990b5c705a65c78f8349227d0e8158203311ca404fcf22' - 'c91d607ace285d70e2263a1b81745c39673080329bd1a3f56e0f00a0f5a401645465' - '73740246aabbccddeeff031903e50482a50081825820afcf8497561065afe1ca6238' - '23508753cc580eb575ac8f1d6cfaa18c3ceeac010001818258390080f9e2c88e6c81' - '7008f3a812ed889b4a4da8e0bd103f86e7335422aa122a946b9ad3d2ddf029d3a828' - 'f0468aece76895f15c9efbd69b42771a00df1560021a0002e63003182f075820bdc2' - 'b27e6869aa9a5fa23a1f1fd3a87025d8703df4fd7b120d058c839dc0415c82a10141' - 'aa80', + '84a700818258204c1fbc5433ec764164945d736a09dc087d59ff30e64d26d462ff857' + '0cd4be9a700018282581d6082e016828989cd9d809b50d6976d9efa9bc5b2c1a78d4b' + '3bfa1bb83b1a000f424082583900c035332d2dcba35744e880729198459e32c50eb3e' + '179b1fa2247348c80b846ad416f120db94c1a401992950b11b9bc7d65dbb3424c0f8d' + 'e41b0000000253fa14bb021a00028d050319a0e907582057b9d4976bc8017e5b95c69' + '96bac1749765e188c990b5c705a65c78f8349227d0e81581cc035332d2dcba35744e8' + '80729198459e32c50eb3e179b1fa2247348c0f00a0f5a40164546573740246aabbccd' + 'deeff031903e50482a50081825820afcf8497561065afe1ca623823508753cc580eb5' + '75ac8f1d6cfaa18c3ceeac010001818258390080f9e2c88e6c817008f3a812ed889b4' + 'a4da8e0bd103f86e7335422aa122a946b9ad3d2ddf029d3a828f0468aece76895f15c' + '9efbd69b42771a00df1560021a0002e63003182f075820bdc2b27e6869aa9a5fa23a1' + 'f1fd3a87025d8703df4fd7b120d058c839dc0415c82a10141aa80', ); }); @@ -96,6 +94,47 @@ void main() { minimalUnsignedTestTransaction(), ); }); + + test('on-chain complex transaction serialized from and to cbor', () { + const hex1 = + '84ab0082825820f72db06cf05d94b184209b0a0a868fc249a4f9e6347e4e95a1b9b0' + 'fb207cdd1d0b825820f72db06cf05d94b184209b0a0a868fc249a4f9e6347e4e95a1' + 'b9b0fb207cdd1d181d0184a300581d705d2470cbd75a4e78c7f72a6f1f19a3ae6d1e' + 'd673a8343ffa084119a801821a0cfe6a80a0028201d818582bd8799f581cd5c9e461' + '7211e2ff7c7fd01d0de9ac82e5bb81dc5a5e16b59911866a1b00000190d6052db0ff' + 'a300581d7062bac7f9f130121bd89270a98900f8e4845c788675f6e0d8dd90e02f01' + '821a00151c56a1581c490738c27186a3389e38b96b3e3493f1ce955bff43a1daba4f' + 'f122dba1416901028201d818584fd8799f01d8798044696c61704131582006c56b73' + 'bcea44f3e37aaf3dba42c9add608b1066248e7f69cc76feeb78ead48581c5223613e' + '39d8299c9394781b8a6a2e4055897586f80e21cefd4f56aaff82583900d5c9e46172' + '11e2ff7c7fd01d0de9ac82e5bb81dc5a5e16b59911866a2f58247e06e3a792a3649f' + '9923a6934d56ef150ab5bb9d15db287fe5821a00115cb0a1581c5223613e39d8299c' + '9394781b8a6a2e4055897586f80e21cefd4f56aaa144696c61700182583900d5c9e4' + '617211e2ff7c7fd01d0de9ac82e5bb81dc5a5e16b59911866a2f58247e06e3a792a3' + '649f9923a6934d56ef150ab5bb9d15db287fe51b0000000237fb222c021a0004620a' + '031a0345fbe4081a0345fba809a1581c5223613e39d8299c9394781b8a6a2e405589' + '7586f80e21cefd4f56aaa144696c6170010b5820cc26bf5b2930452161ec613b5ff5' + '386a0146d448778ec6c516260b0add4ebab30d81825820f72db06cf05d94b184209b' + '0a0a868fc249a4f9e6347e4e95a1b9b0fb207cdd1d181d1082583900d5c9e4617211' + 'e2ff7c7fd01d0de9ac82e5bb81dc5a5e16b59911866a2f58247e06e3a792a3649f99' + '23a6934d56ef150ab5bb9d15db287fe5821a000eedc2a0111b000000024500a0fc12' + '82825820f72db06cf05d94b184209b0a0a868fc249a4f9e6347e4e95a1b9b0fb207c' + 'dd1d01825820f72db06cf05d94b184209b0a0a868fc249a4f9e6347e4e95a1b9b0fb' + '207cdd1d00a2008182582002440b1de68453a43b489d2971f49f03e311accfa6efa2' + 'c577d537b18deb5d675840bac28150d717e3a7b077b195cd1cf9620ee01d973a7e6d' + '35af8c2e1ef5b9b5311a1c81b5c700590b0854fa089ed1d93b3d0780decca12559bb' + '3e91b53d4e330d0582840000d87980821a00037c8d1a055940fd840100d8799fd879' + '9fd8799f41304168416affd8799f41304168416affd8799f58203a34a78764bea088' + 'a63800dcd306ad6bee0e8868a7c36948ac3955a3271b2e98d87a9f5820e3b0c44298' + 'fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855ffd87a9f5820e3' + 'b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855ffffff' + 'ff821a000b0a3d1a106ae424f5f6'; + + final serialized = cbor.decode(hex.decode(hex1)); + final tx = Transaction.fromCbor(serialized); + final hex2 = hex.encode(cbor.encode(tx.toCbor())); + expect(hex1, equals(hex2)); + }); }); } diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/test/types_test.dart b/catalyst_voices_packages/catalyst_cardano_serialization/test/types_test.dart index 53ce4ba345b..91fb22b3530 100644 --- a/catalyst_voices_packages/catalyst_cardano_serialization/test/types_test.dart +++ b/catalyst_voices_packages/catalyst_cardano_serialization/test/types_test.dart @@ -5,6 +5,11 @@ import 'package:test/test.dart'; void main() { group(Coin, () { + test('fromAda constructor', () { + final coin = Coin.fromAda(123); + expect(coin.ada, equals(123)); + }); + test('addition', () { expect(const Coin(3) + const Coin(100), equals(const Coin(103))); expect(const Coin(-100) + const Coin(100), equals(const Coin(0))); diff --git a/catalyst_voices_packages/catalyst_compression/catalyst_compression/CHANGELOG.md b/catalyst_voices_packages/catalyst_compression/catalyst_compression/CHANGELOG.md index 95a992cfe86..225e4cd0d41 100644 --- a/catalyst_voices_packages/catalyst_compression/catalyst_compression/CHANGELOG.md +++ b/catalyst_voices_packages/catalyst_compression/catalyst_compression/CHANGELOG.md @@ -1,3 +1,15 @@ +## 0.3.0 + +> Note: This release has breaking changes. + + - **BREAKING** **CHORE**: upgrade flutter to 3.24.1 and dart to 3.5.0 ([#725](https://github.com/input-output-hk/catalyst-voices/issues/725)). ([eb8a516e](https://github.com/input-output-hk/catalyst-voices/commit/eb8a516edbd25386c0fbe41501285870abf82543)) + +## 0.2.0 + +> Note: This release has breaking changes. + + - **BREAKING** **FEAT**: COSE_SIGN1 signatures and verification ([#669](https://github.com/input-output-hk/catalyst-voices/issues/669)). ([f5a910ef](https://github.com/input-output-hk/catalyst-voices/commit/f5a910efe36442171521b9ec429aed4a46e05b83)) + ## 0.1.1 - **FEAT**: catalyst compression - brotli/zstd ([#626](https://github.com/input-output-hk/catalyst-voices/issues/626)). ([2b8e7d72](https://github.com/input-output-hk/catalyst-voices/commit/2b8e7d7239f9982aa7144a676a86d21b97f912fb)) diff --git a/catalyst_voices_packages/catalyst_compression/catalyst_compression/README.md b/catalyst_voices_packages/catalyst_compression/catalyst_compression/README.md index c901137c1b5..68965ff97a8 100644 --- a/catalyst_voices_packages/catalyst_compression/catalyst_compression/README.md +++ b/catalyst_voices_packages/catalyst_compression/catalyst_compression/README.md @@ -19,8 +19,8 @@ This package exposes a JS wrapper around Brotli and zstd compression/decompressi ## Requirements -* Dart: 3.3.4+ -* Flutter: 3.22.1+ +* Dart: 3.5.0+ +* Flutter: 3.24.1+ ## Install diff --git a/catalyst_voices_packages/catalyst_compression/catalyst_compression/analysis_options.yaml b/catalyst_voices_packages/catalyst_compression/catalyst_compression/analysis_options.yaml index 886855b51aa..a2316514a4b 100644 --- a/catalyst_voices_packages/catalyst_compression/catalyst_compression/analysis_options.yaml +++ b/catalyst_voices_packages/catalyst_compression/catalyst_compression/analysis_options.yaml @@ -1,4 +1,4 @@ -include: package:catalyst_analysis/analysis_options.1.0.0.yaml +include: package:catalyst_analysis/analysis_options.yaml analyzer: exclude: [build/**, lib/*.g.dart, lib/generated/**] diff --git a/catalyst_voices_packages/catalyst_compression/catalyst_compression/lib/src/catalyst_compression.dart b/catalyst_voices_packages/catalyst_compression/catalyst_compression/lib/src/catalyst_compression.dart index 3946fff01e0..de08f685d27 100644 --- a/catalyst_voices_packages/catalyst_compression/catalyst_compression/lib/src/catalyst_compression.dart +++ b/catalyst_voices_packages/catalyst_compression/catalyst_compression/lib/src/catalyst_compression.dart @@ -2,7 +2,7 @@ import 'package:catalyst_compression/catalyst_compression.dart'; import 'package:catalyst_compression_platform_interface/catalyst_compression_platform_interface.dart'; /// A Flutter plugin exposing brotli and zstd compression/decompression algorithms. -class CatalystCompression { +final class CatalystCompression { /// The default instance of [CatalystCompression] to use. static final CatalystCompression instance = CatalystCompression._(); diff --git a/catalyst_voices_packages/catalyst_compression/catalyst_compression/pubspec.yaml b/catalyst_voices_packages/catalyst_compression/catalyst_compression/pubspec.yaml index 56a28390289..417f871d755 100644 --- a/catalyst_voices_packages/catalyst_compression/catalyst_compression/pubspec.yaml +++ b/catalyst_voices_packages/catalyst_compression/catalyst_compression/pubspec.yaml @@ -3,11 +3,11 @@ description: A Flutter plugin exposing Brotli and zstd compression algorithms. repository: https://github.com/input-output-hk/catalyst-voices/tree/main/catalyst_voices_packages/catalyst_compression/catalyst_compression issue_tracker: https://github.com/input-output-hk/catalyst-voices/issues topics: [compression, codec] -version: 0.1.1 +version: 0.3.0 environment: - sdk: ">=3.3.4 <4.0.0" - flutter: ">=3.22.1" + sdk: ">=3.5.0 <4.0.0" + flutter: ">=3.24.1" flutter: plugin: @@ -16,15 +16,14 @@ flutter: default_package: catalyst_compression_web dependencies: - catalyst_compression_platform_interface: ^0.1.1 - catalyst_compression_web: ^0.1.1 + catalyst_compression_platform_interface: ^0.2.0 + catalyst_compression_web: ^0.3.0 convert: ^3.1.1 flutter: sdk: flutter dev_dependencies: - catalyst_analysis: - path: ../../catalyst_analysis + catalyst_analysis: ^2.0.0 flutter_test: sdk: flutter plugin_platform_interface: ^2.1.7 diff --git a/catalyst_voices_packages/catalyst_compression/catalyst_compression_platform_interface/CHANGELOG.md b/catalyst_voices_packages/catalyst_compression/catalyst_compression_platform_interface/CHANGELOG.md index 95a992cfe86..2a4d7f870b8 100644 --- a/catalyst_voices_packages/catalyst_compression/catalyst_compression_platform_interface/CHANGELOG.md +++ b/catalyst_voices_packages/catalyst_compression/catalyst_compression_platform_interface/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.2.0 + +> Note: This release has breaking changes. + + - **BREAKING** **CHORE**: upgrade flutter to 3.24.1 and dart to 3.5.0 ([#725](https://github.com/input-output-hk/catalyst-voices/issues/725)). ([eb8a516e](https://github.com/input-output-hk/catalyst-voices/commit/eb8a516edbd25386c0fbe41501285870abf82543)) + ## 0.1.1 - **FEAT**: catalyst compression - brotli/zstd ([#626](https://github.com/input-output-hk/catalyst-voices/issues/626)). ([2b8e7d72](https://github.com/input-output-hk/catalyst-voices/commit/2b8e7d7239f9982aa7144a676a86d21b97f912fb)) diff --git a/catalyst_voices_packages/catalyst_compression/catalyst_compression_platform_interface/analysis_options.yaml b/catalyst_voices_packages/catalyst_compression/catalyst_compression_platform_interface/analysis_options.yaml index 886855b51aa..a2316514a4b 100644 --- a/catalyst_voices_packages/catalyst_compression/catalyst_compression_platform_interface/analysis_options.yaml +++ b/catalyst_voices_packages/catalyst_compression/catalyst_compression_platform_interface/analysis_options.yaml @@ -1,4 +1,4 @@ -include: package:catalyst_analysis/analysis_options.1.0.0.yaml +include: package:catalyst_analysis/analysis_options.yaml analyzer: exclude: [build/**, lib/*.g.dart, lib/generated/**] diff --git a/catalyst_voices_packages/catalyst_compression/catalyst_compression_platform_interface/pubspec.yaml b/catalyst_voices_packages/catalyst_compression/catalyst_compression_platform_interface/pubspec.yaml index 9adb60ed60c..d0d706e7602 100644 --- a/catalyst_voices_packages/catalyst_compression/catalyst_compression_platform_interface/pubspec.yaml +++ b/catalyst_voices_packages/catalyst_compression/catalyst_compression_platform_interface/pubspec.yaml @@ -3,11 +3,11 @@ description: A common platform interface for the catalyst_compression plugin. repository: https://github.com/input-output-hk/catalyst-voices/tree/main/catalyst_voices_packages/catalyst_compression/catalyst_compression_platform_interface issue_tracker: https://github.com/input-output-hk/catalyst-voices/issues topics: [compression, codec] -version: 0.1.1 +version: 0.2.0 environment: - sdk: ">=3.3.4 <4.0.0" - flutter: ">=3.22.1" + sdk: ">=3.5.0 <4.0.0" + flutter: ">=3.24.1" dependencies: equatable: ^2.0.5 @@ -16,7 +16,6 @@ dependencies: plugin_platform_interface: ^2.1.7 dev_dependencies: - catalyst_analysis: - path: ../../catalyst_analysis + catalyst_analysis: ^2.0.0 flutter_test: sdk: flutter diff --git a/catalyst_voices_packages/catalyst_compression/catalyst_compression_web/CHANGELOG.md b/catalyst_voices_packages/catalyst_compression/catalyst_compression_web/CHANGELOG.md index 95a992cfe86..82d7f71a04c 100644 --- a/catalyst_voices_packages/catalyst_compression/catalyst_compression_web/CHANGELOG.md +++ b/catalyst_voices_packages/catalyst_compression/catalyst_compression_web/CHANGELOG.md @@ -1,3 +1,16 @@ +## 0.3.0 + +> Note: This release has breaking changes. + + - **BREAKING** **CHORE**: upgrade flutter to 3.24.1 and dart to 3.5.0 ([#725](https://github.com/input-output-hk/catalyst-voices/issues/725)). ([eb8a516e](https://github.com/input-output-hk/catalyst-voices/commit/eb8a516edbd25386c0fbe41501285870abf82543)) + +## 0.2.0 + +> Note: This release has breaking changes. + + - **FEAT**: cose flutter package structure ([#649](https://github.com/input-output-hk/catalyst-voices/issues/649)). ([1875849c](https://github.com/input-output-hk/catalyst-voices/commit/1875849c530babbd69dce3882423cc7d9ffdbfa4)) + - **BREAKING** **FEAT**: COSE_SIGN1 signatures and verification ([#669](https://github.com/input-output-hk/catalyst-voices/issues/669)). ([f5a910ef](https://github.com/input-output-hk/catalyst-voices/commit/f5a910efe36442171521b9ec429aed4a46e05b83)) + ## 0.1.1 - **FEAT**: catalyst compression - brotli/zstd ([#626](https://github.com/input-output-hk/catalyst-voices/issues/626)). ([2b8e7d72](https://github.com/input-output-hk/catalyst-voices/commit/2b8e7d7239f9982aa7144a676a86d21b97f912fb)) diff --git a/catalyst_voices_packages/catalyst_compression/catalyst_compression_web/analysis_options.yaml b/catalyst_voices_packages/catalyst_compression/catalyst_compression_web/analysis_options.yaml index 886855b51aa..a2316514a4b 100644 --- a/catalyst_voices_packages/catalyst_compression/catalyst_compression_web/analysis_options.yaml +++ b/catalyst_voices_packages/catalyst_compression/catalyst_compression_web/analysis_options.yaml @@ -1,4 +1,4 @@ -include: package:catalyst_analysis/analysis_options.1.0.0.yaml +include: package:catalyst_analysis/analysis_options.yaml analyzer: exclude: [build/**, lib/*.g.dart, lib/generated/**] diff --git a/catalyst_voices_packages/catalyst_compression/catalyst_compression_web/lib/catalyst_compression_web.dart b/catalyst_voices_packages/catalyst_compression/catalyst_compression_web/lib/catalyst_compression_web.dart index 24f03d8c8b0..bcc80afaf62 100644 --- a/catalyst_voices_packages/catalyst_compression/catalyst_compression_web/lib/catalyst_compression_web.dart +++ b/catalyst_voices_packages/catalyst_compression/catalyst_compression_web/lib/catalyst_compression_web.dart @@ -6,7 +6,7 @@ import 'package:flutter_web_plugins/flutter_web_plugins.dart' show Registrar; /// /// This class implements the `package:catalyst_compression` functionality /// for the web. -class CatalystCompressionWeb extends CatalystCompressionPlatform { +final class CatalystCompressionWeb extends CatalystCompressionPlatform { /// A constructor that allows tests to override the window object used by the /// plugin. CatalystCompressionWeb(); diff --git a/catalyst_voices_packages/catalyst_compression/catalyst_compression_web/pubspec.yaml b/catalyst_voices_packages/catalyst_compression/catalyst_compression_web/pubspec.yaml index cd7939f8547..d9dc1ff8150 100644 --- a/catalyst_voices_packages/catalyst_compression/catalyst_compression_web/pubspec.yaml +++ b/catalyst_voices_packages/catalyst_compression/catalyst_compression_web/pubspec.yaml @@ -3,11 +3,11 @@ description: Web platform implementation of catalyst_compression. Exposes a JS w repository: https://github.com/input-output-hk/catalyst-voices/tree/main/catalyst_voices_packages/catalyst_compression/catalyst_compression_web issue_tracker: https://github.com/input-output-hk/catalyst-voices/issues topics: [compression, codec] -version: 0.1.1 +version: 0.3.0 environment: - sdk: ">=3.3.4 <4.0.0" - flutter: ">=3.22.1" + sdk: ">=3.5.0 <4.0.0" + flutter: ">=3.24.1" flutter: plugin: @@ -21,7 +21,7 @@ flutter: - assets/js/ dependencies: - catalyst_compression_platform_interface: ^0.1.1 + catalyst_compression_platform_interface: ^0.2.0 convert: ^3.1.1 flutter: sdk: flutter @@ -30,7 +30,6 @@ dependencies: web: ^0.5.0 dev_dependencies: - catalyst_analysis: - path: ../../catalyst_analysis + catalyst_analysis: ^2.0.0 flutter_test: sdk: flutter diff --git a/catalyst_voices_packages/catalyst_cose/CHANGELOG.md b/catalyst_voices_packages/catalyst_cose/CHANGELOG.md new file mode 100644 index 00000000000..8bcfd7cc21e --- /dev/null +++ b/catalyst_voices_packages/catalyst_cose/CHANGELOG.md @@ -0,0 +1,16 @@ +## 0.3.0 + +> Note: This release has breaking changes. + + - **BREAKING** **CHORE**: upgrade flutter to 3.24.1 and dart to 3.5.0 ([#725](https://github.com/input-output-hk/catalyst-voices/issues/725)). ([eb8a516e](https://github.com/input-output-hk/catalyst-voices/commit/eb8a516edbd25386c0fbe41501285870abf82543)) + +## 0.2.0 + +> Note: This release has breaking changes. + + - **FEAT**: cose flutter package structure ([#649](https://github.com/input-output-hk/catalyst-voices/issues/649)). ([1875849c](https://github.com/input-output-hk/catalyst-voices/commit/1875849c530babbd69dce3882423cc7d9ffdbfa4)) + - **BREAKING** **FEAT**: COSE_SIGN1 signatures and verification ([#669](https://github.com/input-output-hk/catalyst-voices/issues/669)). ([f5a910ef](https://github.com/input-output-hk/catalyst-voices/commit/f5a910efe36442171521b9ec429aed4a46e05b83)) + +## 0.1.0 + +* Initial release. diff --git a/catalyst_voices_packages/catalyst_cose/catalyst_cose/LICENSE b/catalyst_voices_packages/catalyst_cose/LICENSE similarity index 100% rename from catalyst_voices_packages/catalyst_cose/catalyst_cose/LICENSE rename to catalyst_voices_packages/catalyst_cose/LICENSE diff --git a/catalyst_voices_packages/catalyst_cose/catalyst_cose/README.md b/catalyst_voices_packages/catalyst_cose/README.md similarity index 51% rename from catalyst_voices_packages/catalyst_cose/catalyst_cose/README.md rename to catalyst_voices_packages/catalyst_cose/README.md index 7f89f04a2e0..e8658b94f3e 100644 --- a/catalyst_voices_packages/catalyst_cose/catalyst_cose/README.md +++ b/catalyst_voices_packages/catalyst_cose/README.md @@ -4,7 +4,6 @@ * [Features](#features) * [Requirements](#requirements) * [Install](#install) - * [Web setup](#web-setup) * [Example](#example) * [Limitations](#limitations) * [Support](#support) @@ -13,29 +12,18 @@ ## Features This package exposes a CBOR Object Signing and Encryption -[RFC-8152](https://datatracker.ietf.org/doc/html/rfc8152) implementation of cose-js npm module. - -* [cose-js](https://www.npmjs.com/package/cose-js) +[RFC-9052](https://datatracker.ietf.org/doc/html/rfc9052), +[RFC-9053](https://datatracker.ietf.org/doc/html/rfc9053) implementation. ## Requirements -* Dart: 3.3.4+ -* Flutter: 3.22.1+ +* Dart: 3.5.0+ ## Install ```yaml dependencies: catalyst_cose: any # or the latest version on Pub - catalyst_cose_web: any # or the latest version on Pub -``` - -## Web setup - -Add the following line at the end of `` section in web/index.html: - -```html - ``` ## Example @@ -43,22 +31,56 @@ Add the following line at the end of `` section in web/index.html: ```dart // ignore_for_file: avoid_print +import 'dart:convert'; + import 'package:catalyst_cose/catalyst_cose.dart'; +import 'package:cbor/cbor.dart'; import 'package:convert/convert.dart'; +import 'package:cryptography/cryptography.dart'; Future main() async { - final message = hex.decode( - '6c1382765aec5358f117733d281c1c7bdc39884d04a45a1e6c67c858bc206c19', + final algorithm = Ed25519(); + final keyPair = await algorithm.newKeyPairFromSeed(List.filled(32, 0)); + final privateKey = await keyPair.extractPrivateKeyBytes(); + final publicKey = await keyPair.extractPublicKey().then((e) => e.bytes); + + final payload = utf8.encode('This is the content.'); + + final coseSign1 = await CatalystCose.sign1( + privateKey: privateKey, + payload: payload, + kid: CborBytes(publicKey), ); - final coseSignature = await CatalystCose.instance.signMessage(message); - print(coseSignature); + final verified = await CatalystCose.verifyCoseSign1( + coseSign1: coseSign1, + publicKey: publicKey, + ); + + print('COSE_SIGN1:'); + print(hex.encode(cbor.encode(coseSign1))); + print('verified: $verified'); + + assert( + verified, + 'The signature proves that given COSE_SIGN1 structure has been ' + 'signed by the owner of the given public key', + ); } ``` ## Limitations -This package supports only web platform at the moment but it's intended that other platforms are supported in the future. +This package supports only a subset of COSE features and algorithms. +More features and algorithms are supposed to be added in the feature. + +Supported features: + +* COSE_SIGN_1: signature + verification + +Supported algorithms: + +* EdDSA: Ed25519 ## Support diff --git a/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/analysis_options.yaml b/catalyst_voices_packages/catalyst_cose/analysis_options.yaml similarity index 50% rename from catalyst_voices_packages/catalyst_cose/catalyst_cose_web/analysis_options.yaml rename to catalyst_voices_packages/catalyst_cose/analysis_options.yaml index 886855b51aa..a2316514a4b 100644 --- a/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/analysis_options.yaml +++ b/catalyst_voices_packages/catalyst_cose/analysis_options.yaml @@ -1,4 +1,4 @@ -include: package:catalyst_analysis/analysis_options.1.0.0.yaml +include: package:catalyst_analysis/analysis_options.yaml analyzer: exclude: [build/**, lib/*.g.dart, lib/generated/**] diff --git a/catalyst_voices_packages/catalyst_cose/catalyst_cose/CHANGELOG.md b/catalyst_voices_packages/catalyst_cose/catalyst_cose/CHANGELOG.md deleted file mode 100644 index 6073234226b..00000000000 --- a/catalyst_voices_packages/catalyst_cose/catalyst_cose/CHANGELOG.md +++ /dev/null @@ -1,3 +0,0 @@ -## 0.1.0 - -* Initial release. diff --git a/catalyst_voices_packages/catalyst_cose/catalyst_cose/analysis_options.yaml b/catalyst_voices_packages/catalyst_cose/catalyst_cose/analysis_options.yaml deleted file mode 100644 index 886855b51aa..00000000000 --- a/catalyst_voices_packages/catalyst_cose/catalyst_cose/analysis_options.yaml +++ /dev/null @@ -1,4 +0,0 @@ -include: package:catalyst_analysis/analysis_options.1.0.0.yaml - -analyzer: - exclude: [build/**, lib/*.g.dart, lib/generated/**] diff --git a/catalyst_voices_packages/catalyst_cose/catalyst_cose/example/main.dart b/catalyst_voices_packages/catalyst_cose/catalyst_cose/example/main.dart deleted file mode 100644 index 746b238ad8e..00000000000 --- a/catalyst_voices_packages/catalyst_cose/catalyst_cose/example/main.dart +++ /dev/null @@ -1,13 +0,0 @@ -// ignore_for_file: avoid_print - -import 'package:catalyst_cose/catalyst_cose.dart'; -import 'package:convert/convert.dart'; - -Future main() async { - final message = hex.decode( - '6c1382765aec5358f117733d281c1c7bdc39884d04a45a1e6c67c858bc206c19', - ); - - final coseSignature = await CatalystCose.instance.signMessage(message); - print(coseSignature); -} diff --git a/catalyst_voices_packages/catalyst_cose/catalyst_cose/lib/catalyst_cose.dart b/catalyst_voices_packages/catalyst_cose/catalyst_cose/lib/catalyst_cose.dart deleted file mode 100644 index 87e996e8e18..00000000000 --- a/catalyst_voices_packages/catalyst_cose/catalyst_cose/lib/catalyst_cose.dart +++ /dev/null @@ -1,3 +0,0 @@ -export 'package:catalyst_cose_platform_interface/catalyst_cose_platform_interface.dart'; - -export 'src/catalyst_cose.dart'; diff --git a/catalyst_voices_packages/catalyst_cose/catalyst_cose/lib/src/catalyst_cose.dart b/catalyst_voices_packages/catalyst_cose/catalyst_cose/lib/src/catalyst_cose.dart deleted file mode 100644 index c865ef3dfeb..00000000000 --- a/catalyst_voices_packages/catalyst_cose/catalyst_cose/lib/src/catalyst_cose.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:catalyst_cose_platform_interface/catalyst_cose_platform_interface.dart'; -import 'package:cbor/cbor.dart'; - -/// A Flutter plugin implementing CBOR Object Signing and Encryption (RFC 8152). -class CatalystCose { - /// The default instance of [CatalystCose] to use. - static final CatalystCose instance = CatalystCose._(); - - CatalystCose._(); - - /// Signs the [message] and returns a [CborValue] representing - /// a COSE signature. - Future signMessage(List message) { - return CatalystCosePlatform.instance.signMessage(message); - } -} diff --git a/catalyst_voices_packages/catalyst_cose/catalyst_cose/pubspec.yaml b/catalyst_voices_packages/catalyst_cose/catalyst_cose/pubspec.yaml deleted file mode 100644 index 4d927a72d89..00000000000 --- a/catalyst_voices_packages/catalyst_cose/catalyst_cose/pubspec.yaml +++ /dev/null @@ -1,31 +0,0 @@ -name: catalyst_cose -description: A Flutter plugin implementing CBOR Object Signing and Encryption (RFC 8152). -repository: https://github.com/input-output-hk/catalyst-voices/tree/main/catalyst_voices_packages/catalyst_cose/catalyst_cose -issue_tracker: https://github.com/input-output-hk/catalyst-voices/issues -topics: [cryptography, encryption, codec] -version: 0.1.0 - -environment: - sdk: ">=3.3.4 <4.0.0" - flutter: ">=3.22.1" - -flutter: - plugin: - platforms: - web: - default_package: catalyst_cose_web - -dependencies: - catalyst_cose_platform_interface: ^0.1.1 - catalyst_cose_web: ^0.1.1 - cbor: ^6.2.0 - convert: ^3.1.1 - flutter: - sdk: flutter - -dev_dependencies: - catalyst_analysis: - path: ../../catalyst_analysis - flutter_test: - sdk: flutter - plugin_platform_interface: ^2.1.7 diff --git a/catalyst_voices_packages/catalyst_cose/catalyst_cose_platform_interface/CHANGELOG.md b/catalyst_voices_packages/catalyst_cose/catalyst_cose_platform_interface/CHANGELOG.md deleted file mode 100644 index 6073234226b..00000000000 --- a/catalyst_voices_packages/catalyst_cose/catalyst_cose_platform_interface/CHANGELOG.md +++ /dev/null @@ -1,3 +0,0 @@ -## 0.1.0 - -* Initial release. diff --git a/catalyst_voices_packages/catalyst_cose/catalyst_cose_platform_interface/LICENSE b/catalyst_voices_packages/catalyst_cose/catalyst_cose_platform_interface/LICENSE deleted file mode 100644 index a55761ab50c..00000000000 --- a/catalyst_voices_packages/catalyst_cose/catalyst_cose_platform_interface/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright (c) 2023 Input Output (IOG). - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. \ No newline at end of file diff --git a/catalyst_voices_packages/catalyst_cose/catalyst_cose_platform_interface/README.md b/catalyst_voices_packages/catalyst_cose/catalyst_cose_platform_interface/README.md deleted file mode 100644 index 8b77ccc61f3..00000000000 --- a/catalyst_voices_packages/catalyst_cose/catalyst_cose_platform_interface/README.md +++ /dev/null @@ -1 +0,0 @@ -# catalyst_cose_platform_interface diff --git a/catalyst_voices_packages/catalyst_cose/catalyst_cose_platform_interface/analysis_options.yaml b/catalyst_voices_packages/catalyst_cose/catalyst_cose_platform_interface/analysis_options.yaml deleted file mode 100644 index 886855b51aa..00000000000 --- a/catalyst_voices_packages/catalyst_cose/catalyst_cose_platform_interface/analysis_options.yaml +++ /dev/null @@ -1,4 +0,0 @@ -include: package:catalyst_analysis/analysis_options.1.0.0.yaml - -analyzer: - exclude: [build/**, lib/*.g.dart, lib/generated/**] diff --git a/catalyst_voices_packages/catalyst_cose/catalyst_cose_platform_interface/example/main.dart b/catalyst_voices_packages/catalyst_cose/catalyst_cose_platform_interface/example/main.dart deleted file mode 100644 index ab33a448715..00000000000 --- a/catalyst_voices_packages/catalyst_cose/catalyst_cose_platform_interface/example/main.dart +++ /dev/null @@ -1,4 +0,0 @@ -void main() { - // ignore: avoid_print - print('Example is located in catalyst_cose/example directory.'); -} diff --git a/catalyst_voices_packages/catalyst_cose/catalyst_cose_platform_interface/lib/catalyst_cose_platform_interface.dart b/catalyst_voices_packages/catalyst_cose/catalyst_cose_platform_interface/lib/catalyst_cose_platform_interface.dart deleted file mode 100644 index 444ad1c194a..00000000000 --- a/catalyst_voices_packages/catalyst_cose/catalyst_cose_platform_interface/lib/catalyst_cose_platform_interface.dart +++ /dev/null @@ -1 +0,0 @@ -export 'src/catalyst_cose_platform.dart'; diff --git a/catalyst_voices_packages/catalyst_cose/catalyst_cose_platform_interface/lib/src/catalyst_cose_platform.dart b/catalyst_voices_packages/catalyst_cose/catalyst_cose_platform_interface/lib/src/catalyst_cose_platform.dart deleted file mode 100644 index b57e66281f8..00000000000 --- a/catalyst_voices_packages/catalyst_cose/catalyst_cose_platform_interface/lib/src/catalyst_cose_platform.dart +++ /dev/null @@ -1,46 +0,0 @@ -import 'package:cbor/cbor.dart'; -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; - -/// The interface that implementations of catalyst_cose must -/// implement. -/// -/// Platform implementations should extend this class rather than implement -/// it as `catalyst_cose` does not consider newly added methods to -/// be breaking changes. Extending this class (using `extends`) ensures that the -/// subclass will get the default implementation, while platform implementations -/// that `implements` this interface will be broken by newly added -/// [CatalystCosePlatform] methods. -abstract class CatalystCosePlatform extends PlatformInterface { - static final Object _token = Object(); - - static CatalystCosePlatform? _instance; - - /// The default instance of [CatalystCosePlatform] to use. - /// - /// Must be set with [instance] setter before it can be used. - static CatalystCosePlatform get instance { - assert( - _instance != null, - 'Make sure to register the instance via ' - 'instance setter before it is used.', - ); - return _instance!; - } - - /// Platform-specific plugins should set this with their own platform-specific - /// class that extends [CatalystCosePlatform] when they register - /// themselves. - static set instance(CatalystCosePlatform instance) { - PlatformInterface.verify(instance, _token); - _instance = instance; - } - - /// Constructs a [CatalystCosePlatform]. - CatalystCosePlatform() : super(token: _token); - - /// Signs the [message] and returns a [CborValue] representing - /// a COSE signature. - Future signMessage(List message) { - throw UnimplementedError('signMessage has not been implemented.'); - } -} diff --git a/catalyst_voices_packages/catalyst_cose/catalyst_cose_platform_interface/pubspec.yaml b/catalyst_voices_packages/catalyst_cose/catalyst_cose_platform_interface/pubspec.yaml deleted file mode 100644 index 299591e0505..00000000000 --- a/catalyst_voices_packages/catalyst_cose/catalyst_cose_platform_interface/pubspec.yaml +++ /dev/null @@ -1,23 +0,0 @@ -name: catalyst_cose_platform_interface -description: A common platform interface for the catalyst_cose plugin. -repository: https://github.com/input-output-hk/catalyst-voices/tree/main/catalyst_voices_packages/catalyst_cose/catalyst_cose_platform_interface -issue_tracker: https://github.com/input-output-hk/catalyst-voices/issues -topics: [cryptography, encryption, codec] -version: 0.1.0 - -environment: - sdk: ">=3.3.4 <4.0.0" - flutter: ">=3.22.1" - -dependencies: - cbor: ^6.2.0 - equatable: ^2.0.5 - flutter: - sdk: flutter - plugin_platform_interface: ^2.1.7 - -dev_dependencies: - catalyst_analysis: - path: ../../catalyst_analysis - flutter_test: - sdk: flutter diff --git a/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/CHANGELOG.md b/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/CHANGELOG.md deleted file mode 100644 index 6073234226b..00000000000 --- a/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/CHANGELOG.md +++ /dev/null @@ -1,3 +0,0 @@ -## 0.1.0 - -* Initial release. diff --git a/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/LICENSE b/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/LICENSE deleted file mode 100644 index a55761ab50c..00000000000 --- a/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright (c) 2023 Input Output (IOG). - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. \ No newline at end of file diff --git a/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/README.md b/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/README.md deleted file mode 100644 index 165f2690057..00000000000 --- a/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/README.md +++ /dev/null @@ -1 +0,0 @@ -# catalyst_cose_web diff --git a/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/assets/js/.gitignore b/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/assets/js/.gitignore deleted file mode 100644 index 074ab526491..00000000000 --- a/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/assets/js/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -package-lock.json -node_modules \ No newline at end of file diff --git a/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/assets/js/README.md b/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/assets/js/README.md deleted file mode 100644 index 9a550a399eb..00000000000 --- a/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/assets/js/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# catalyst_cose_js - -## How to make changes in JS layer? - -catalyst_cose depends on the [cose-js](https://www.npmjs.com/package/cose-js) -node module which doesn't work out of the box on the browser without the node environment. - -To make it compatible with the browser we employ the [browserify](https://browserify.org/) -which transforms the JS code to be browser runnable. - -To make changes in JS layer take the following steps: - -1. run `cd assets/js` # enter the directory. -2. run `npm install` to init the node environment. -3. Apply your changes in `main.js`. -This is the source file that will get transformed into a browser runnable version later. -4. Run `npx browserify main.js -o catalyst_cose.js` to make the module runnable in a browser. diff --git a/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/assets/js/catalyst_cose.js b/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/assets/js/catalyst_cose.js deleted file mode 100644 index 3cdf5165212..00000000000 --- a/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/assets/js/catalyst_cose.js +++ /dev/null @@ -1,42119 +0,0 @@ -(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i= 0.12 - * 3. Auto detected promise based on first sucessful require of - * known promise libraries. Note this is a last resort, as the - * loaded library is non-deterministic. node.js >= 0.12 will - * always use global.Promise over this priority list. - * 4. Throws error. - * - * For Browser: - * - * 1. Previous registration - * 2. window.Promise - * 3. Throws error. - * - * Options: - * - * Promise: Desired Promise constructor - * global: Boolean - Should the registration be cached in a global variable to - * allow cross dependency/bundle registration? (default true) - */ -module.exports = function(root, loadImplementation){ - return function register(implementation, opts){ - implementation = implementation || null - opts = opts || {} - // global registration unless explicitly {global: false} in options (default true) - var registerGlobal = opts.global !== false; - - // load any previous global registration - if(registered === null && registerGlobal){ - registered = root[REGISTRATION_KEY] || null - } - - if(registered !== null - && implementation !== null - && registered.implementation !== implementation){ - // Throw error if attempting to redefine implementation - throw new Error('any-promise already defined as "'+registered.implementation+ - '". You can only register an implementation before the first '+ - ' call to require("any-promise") and an implementation cannot be changed') - } - - if(registered === null){ - // use provided implementation - if(implementation !== null && typeof opts.Promise !== 'undefined'){ - registered = { - Promise: opts.Promise, - implementation: implementation - } - } else { - // require implementation if implementation is specified but not provided - registered = loadImplementation(implementation) - } - - if(registerGlobal){ - // register preference globally in case multiple installations - root[REGISTRATION_KEY] = registered - } - } - - return registered - } -} - -},{}],5:[function(require,module,exports){ -"use strict"; -module.exports = require('./loader')(window, loadImplementation) - -/** - * Browser specific loadImplementation. Always uses `window.Promise` - * - * To register a custom implementation, must register with `Promise` option. - */ -function loadImplementation(){ - if(typeof window.Promise === 'undefined'){ - throw new Error("any-promise browser requires a polyfill or explicit registration"+ - " e.g: require('any-promise/register/bluebird')") - } - return { - Promise: window.Promise, - implementation: 'window.Promise' - } -} - -},{"./loader":4}],6:[function(require,module,exports){ -var asn1 = exports; - -asn1.bignum = require('bn.js'); - -asn1.define = require('./asn1/api').define; -asn1.base = require('./asn1/base'); -asn1.constants = require('./asn1/constants'); -asn1.decoders = require('./asn1/decoders'); -asn1.encoders = require('./asn1/encoders'); - -},{"./asn1/api":7,"./asn1/base":9,"./asn1/constants":13,"./asn1/decoders":15,"./asn1/encoders":18,"bn.js":20}],7:[function(require,module,exports){ -var asn1 = require('../asn1'); -var inherits = require('inherits'); - -var api = exports; - -api.define = function define(name, body) { - return new Entity(name, body); -}; - -function Entity(name, body) { - this.name = name; - this.body = body; - - this.decoders = {}; - this.encoders = {}; -}; - -Entity.prototype._createNamed = function createNamed(base) { - var named; - try { - named = require('vm').runInThisContext( - '(function ' + this.name + '(entity) {\n' + - ' this._initNamed(entity);\n' + - '})' - ); - } catch (e) { - named = function (entity) { - this._initNamed(entity); - }; - } - inherits(named, base); - named.prototype._initNamed = function initnamed(entity) { - base.call(this, entity); - }; - - return new named(this); -}; - -Entity.prototype._getDecoder = function _getDecoder(enc) { - enc = enc || 'der'; - // Lazily create decoder - if (!this.decoders.hasOwnProperty(enc)) - this.decoders[enc] = this._createNamed(asn1.decoders[enc]); - return this.decoders[enc]; -}; - -Entity.prototype.decode = function decode(data, enc, options) { - return this._getDecoder(enc).decode(data, options); -}; - -Entity.prototype._getEncoder = function _getEncoder(enc) { - enc = enc || 'der'; - // Lazily create encoder - if (!this.encoders.hasOwnProperty(enc)) - this.encoders[enc] = this._createNamed(asn1.encoders[enc]); - return this.encoders[enc]; -}; - -Entity.prototype.encode = function encode(data, enc, /* internal */ reporter) { - return this._getEncoder(enc).encode(data, reporter); -}; - -},{"../asn1":6,"inherits":155,"vm":250}],8:[function(require,module,exports){ -var inherits = require('inherits'); -var Reporter = require('../base').Reporter; -var Buffer = require('buffer').Buffer; - -function DecoderBuffer(base, options) { - Reporter.call(this, options); - if (!Buffer.isBuffer(base)) { - this.error('Input not Buffer'); - return; - } - - this.base = base; - this.offset = 0; - this.length = base.length; -} -inherits(DecoderBuffer, Reporter); -exports.DecoderBuffer = DecoderBuffer; - -DecoderBuffer.prototype.save = function save() { - return { offset: this.offset, reporter: Reporter.prototype.save.call(this) }; -}; - -DecoderBuffer.prototype.restore = function restore(save) { - // Return skipped data - var res = new DecoderBuffer(this.base); - res.offset = save.offset; - res.length = this.offset; - - this.offset = save.offset; - Reporter.prototype.restore.call(this, save.reporter); - - return res; -}; - -DecoderBuffer.prototype.isEmpty = function isEmpty() { - return this.offset === this.length; -}; - -DecoderBuffer.prototype.readUInt8 = function readUInt8(fail) { - if (this.offset + 1 <= this.length) - return this.base.readUInt8(this.offset++, true); - else - return this.error(fail || 'DecoderBuffer overrun'); -} - -DecoderBuffer.prototype.skip = function skip(bytes, fail) { - if (!(this.offset + bytes <= this.length)) - return this.error(fail || 'DecoderBuffer overrun'); - - var res = new DecoderBuffer(this.base); - - // Share reporter state - res._reporterState = this._reporterState; - - res.offset = this.offset; - res.length = this.offset + bytes; - this.offset += bytes; - return res; -} - -DecoderBuffer.prototype.raw = function raw(save) { - return this.base.slice(save ? save.offset : this.offset, this.length); -} - -function EncoderBuffer(value, reporter) { - if (Array.isArray(value)) { - this.length = 0; - this.value = value.map(function(item) { - if (!(item instanceof EncoderBuffer)) - item = new EncoderBuffer(item, reporter); - this.length += item.length; - return item; - }, this); - } else if (typeof value === 'number') { - if (!(0 <= value && value <= 0xff)) - return reporter.error('non-byte EncoderBuffer value'); - this.value = value; - this.length = 1; - } else if (typeof value === 'string') { - this.value = value; - this.length = Buffer.byteLength(value); - } else if (Buffer.isBuffer(value)) { - this.value = value; - this.length = value.length; - } else { - return reporter.error('Unsupported type: ' + typeof value); - } -} -exports.EncoderBuffer = EncoderBuffer; - -EncoderBuffer.prototype.join = function join(out, offset) { - if (!out) - out = new Buffer(this.length); - if (!offset) - offset = 0; - - if (this.length === 0) - return out; - - if (Array.isArray(this.value)) { - this.value.forEach(function(item) { - item.join(out, offset); - offset += item.length; - }); - } else { - if (typeof this.value === 'number') - out[offset] = this.value; - else if (typeof this.value === 'string') - out.write(this.value, offset); - else if (Buffer.isBuffer(this.value)) - this.value.copy(out, offset); - offset += this.length; - } - - return out; -}; - -},{"../base":9,"buffer":63,"inherits":155}],9:[function(require,module,exports){ -var base = exports; - -base.Reporter = require('./reporter').Reporter; -base.DecoderBuffer = require('./buffer').DecoderBuffer; -base.EncoderBuffer = require('./buffer').EncoderBuffer; -base.Node = require('./node'); - -},{"./buffer":8,"./node":10,"./reporter":11}],10:[function(require,module,exports){ -var Reporter = require('../base').Reporter; -var EncoderBuffer = require('../base').EncoderBuffer; -var DecoderBuffer = require('../base').DecoderBuffer; -var assert = require('minimalistic-assert'); - -// Supported tags -var tags = [ - 'seq', 'seqof', 'set', 'setof', 'objid', 'bool', - 'gentime', 'utctime', 'null_', 'enum', 'int', 'objDesc', - 'bitstr', 'bmpstr', 'charstr', 'genstr', 'graphstr', 'ia5str', 'iso646str', - 'numstr', 'octstr', 'printstr', 't61str', 'unistr', 'utf8str', 'videostr' -]; - -// Public methods list -var methods = [ - 'key', 'obj', 'use', 'optional', 'explicit', 'implicit', 'def', 'choice', - 'any', 'contains' -].concat(tags); - -// Overrided methods list -var overrided = [ - '_peekTag', '_decodeTag', '_use', - '_decodeStr', '_decodeObjid', '_decodeTime', - '_decodeNull', '_decodeInt', '_decodeBool', '_decodeList', - - '_encodeComposite', '_encodeStr', '_encodeObjid', '_encodeTime', - '_encodeNull', '_encodeInt', '_encodeBool' -]; - -function Node(enc, parent) { - var state = {}; - this._baseState = state; - - state.enc = enc; - - state.parent = parent || null; - state.children = null; - - // State - state.tag = null; - state.args = null; - state.reverseArgs = null; - state.choice = null; - state.optional = false; - state.any = false; - state.obj = false; - state.use = null; - state.useDecoder = null; - state.key = null; - state['default'] = null; - state.explicit = null; - state.implicit = null; - state.contains = null; - - // Should create new instance on each method - if (!state.parent) { - state.children = []; - this._wrap(); - } -} -module.exports = Node; - -var stateProps = [ - 'enc', 'parent', 'children', 'tag', 'args', 'reverseArgs', 'choice', - 'optional', 'any', 'obj', 'use', 'alteredUse', 'key', 'default', 'explicit', - 'implicit', 'contains' -]; - -Node.prototype.clone = function clone() { - var state = this._baseState; - var cstate = {}; - stateProps.forEach(function(prop) { - cstate[prop] = state[prop]; - }); - var res = new this.constructor(cstate.parent); - res._baseState = cstate; - return res; -}; - -Node.prototype._wrap = function wrap() { - var state = this._baseState; - methods.forEach(function(method) { - this[method] = function _wrappedMethod() { - var clone = new this.constructor(this); - state.children.push(clone); - return clone[method].apply(clone, arguments); - }; - }, this); -}; - -Node.prototype._init = function init(body) { - var state = this._baseState; - - assert(state.parent === null); - body.call(this); - - // Filter children - state.children = state.children.filter(function(child) { - return child._baseState.parent === this; - }, this); - assert.equal(state.children.length, 1, 'Root node can have only one child'); -}; - -Node.prototype._useArgs = function useArgs(args) { - var state = this._baseState; - - // Filter children and args - var children = args.filter(function(arg) { - return arg instanceof this.constructor; - }, this); - args = args.filter(function(arg) { - return !(arg instanceof this.constructor); - }, this); - - if (children.length !== 0) { - assert(state.children === null); - state.children = children; - - // Replace parent to maintain backward link - children.forEach(function(child) { - child._baseState.parent = this; - }, this); - } - if (args.length !== 0) { - assert(state.args === null); - state.args = args; - state.reverseArgs = args.map(function(arg) { - if (typeof arg !== 'object' || arg.constructor !== Object) - return arg; - - var res = {}; - Object.keys(arg).forEach(function(key) { - if (key == (key | 0)) - key |= 0; - var value = arg[key]; - res[value] = key; - }); - return res; - }); - } -}; - -// -// Overrided methods -// - -overrided.forEach(function(method) { - Node.prototype[method] = function _overrided() { - var state = this._baseState; - throw new Error(method + ' not implemented for encoding: ' + state.enc); - }; -}); - -// -// Public methods -// - -tags.forEach(function(tag) { - Node.prototype[tag] = function _tagMethod() { - var state = this._baseState; - var args = Array.prototype.slice.call(arguments); - - assert(state.tag === null); - state.tag = tag; - - this._useArgs(args); - - return this; - }; -}); - -Node.prototype.use = function use(item) { - assert(item); - var state = this._baseState; - - assert(state.use === null); - state.use = item; - - return this; -}; - -Node.prototype.optional = function optional() { - var state = this._baseState; - - state.optional = true; - - return this; -}; - -Node.prototype.def = function def(val) { - var state = this._baseState; - - assert(state['default'] === null); - state['default'] = val; - state.optional = true; - - return this; -}; - -Node.prototype.explicit = function explicit(num) { - var state = this._baseState; - - assert(state.explicit === null && state.implicit === null); - state.explicit = num; - - return this; -}; - -Node.prototype.implicit = function implicit(num) { - var state = this._baseState; - - assert(state.explicit === null && state.implicit === null); - state.implicit = num; - - return this; -}; - -Node.prototype.obj = function obj() { - var state = this._baseState; - var args = Array.prototype.slice.call(arguments); - - state.obj = true; - - if (args.length !== 0) - this._useArgs(args); - - return this; -}; - -Node.prototype.key = function key(newKey) { - var state = this._baseState; - - assert(state.key === null); - state.key = newKey; - - return this; -}; - -Node.prototype.any = function any() { - var state = this._baseState; - - state.any = true; - - return this; -}; - -Node.prototype.choice = function choice(obj) { - var state = this._baseState; - - assert(state.choice === null); - state.choice = obj; - this._useArgs(Object.keys(obj).map(function(key) { - return obj[key]; - })); - - return this; -}; - -Node.prototype.contains = function contains(item) { - var state = this._baseState; - - assert(state.use === null); - state.contains = item; - - return this; -}; - -// -// Decoding -// - -Node.prototype._decode = function decode(input, options) { - var state = this._baseState; - - // Decode root node - if (state.parent === null) - return input.wrapResult(state.children[0]._decode(input, options)); - - var result = state['default']; - var present = true; - - var prevKey = null; - if (state.key !== null) - prevKey = input.enterKey(state.key); - - // Check if tag is there - if (state.optional) { - var tag = null; - if (state.explicit !== null) - tag = state.explicit; - else if (state.implicit !== null) - tag = state.implicit; - else if (state.tag !== null) - tag = state.tag; - - if (tag === null && !state.any) { - // Trial and Error - var save = input.save(); - try { - if (state.choice === null) - this._decodeGeneric(state.tag, input, options); - else - this._decodeChoice(input, options); - present = true; - } catch (e) { - present = false; - } - input.restore(save); - } else { - present = this._peekTag(input, tag, state.any); - - if (input.isError(present)) - return present; - } - } - - // Push object on stack - var prevObj; - if (state.obj && present) - prevObj = input.enterObject(); - - if (present) { - // Unwrap explicit values - if (state.explicit !== null) { - var explicit = this._decodeTag(input, state.explicit); - if (input.isError(explicit)) - return explicit; - input = explicit; - } - - var start = input.offset; - - // Unwrap implicit and normal values - if (state.use === null && state.choice === null) { - if (state.any) - var save = input.save(); - var body = this._decodeTag( - input, - state.implicit !== null ? state.implicit : state.tag, - state.any - ); - if (input.isError(body)) - return body; - - if (state.any) - result = input.raw(save); - else - input = body; - } - - if (options && options.track && state.tag !== null) - options.track(input.path(), start, input.length, 'tagged'); - - if (options && options.track && state.tag !== null) - options.track(input.path(), input.offset, input.length, 'content'); - - // Select proper method for tag - if (state.any) - result = result; - else if (state.choice === null) - result = this._decodeGeneric(state.tag, input, options); - else - result = this._decodeChoice(input, options); - - if (input.isError(result)) - return result; - - // Decode children - if (!state.any && state.choice === null && state.children !== null) { - state.children.forEach(function decodeChildren(child) { - // NOTE: We are ignoring errors here, to let parser continue with other - // parts of encoded data - child._decode(input, options); - }); - } - - // Decode contained/encoded by schema, only in bit or octet strings - if (state.contains && (state.tag === 'octstr' || state.tag === 'bitstr')) { - var data = new DecoderBuffer(result); - result = this._getUse(state.contains, input._reporterState.obj) - ._decode(data, options); - } - } - - // Pop object - if (state.obj && present) - result = input.leaveObject(prevObj); - - // Set key - if (state.key !== null && (result !== null || present === true)) - input.leaveKey(prevKey, state.key, result); - else if (prevKey !== null) - input.exitKey(prevKey); - - return result; -}; - -Node.prototype._decodeGeneric = function decodeGeneric(tag, input, options) { - var state = this._baseState; - - if (tag === 'seq' || tag === 'set') - return null; - if (tag === 'seqof' || tag === 'setof') - return this._decodeList(input, tag, state.args[0], options); - else if (/str$/.test(tag)) - return this._decodeStr(input, tag, options); - else if (tag === 'objid' && state.args) - return this._decodeObjid(input, state.args[0], state.args[1], options); - else if (tag === 'objid') - return this._decodeObjid(input, null, null, options); - else if (tag === 'gentime' || tag === 'utctime') - return this._decodeTime(input, tag, options); - else if (tag === 'null_') - return this._decodeNull(input, options); - else if (tag === 'bool') - return this._decodeBool(input, options); - else if (tag === 'objDesc') - return this._decodeStr(input, tag, options); - else if (tag === 'int' || tag === 'enum') - return this._decodeInt(input, state.args && state.args[0], options); - - if (state.use !== null) { - return this._getUse(state.use, input._reporterState.obj) - ._decode(input, options); - } else { - return input.error('unknown tag: ' + tag); - } -}; - -Node.prototype._getUse = function _getUse(entity, obj) { - - var state = this._baseState; - // Create altered use decoder if implicit is set - state.useDecoder = this._use(entity, obj); - assert(state.useDecoder._baseState.parent === null); - state.useDecoder = state.useDecoder._baseState.children[0]; - if (state.implicit !== state.useDecoder._baseState.implicit) { - state.useDecoder = state.useDecoder.clone(); - state.useDecoder._baseState.implicit = state.implicit; - } - return state.useDecoder; -}; - -Node.prototype._decodeChoice = function decodeChoice(input, options) { - var state = this._baseState; - var result = null; - var match = false; - - Object.keys(state.choice).some(function(key) { - var save = input.save(); - var node = state.choice[key]; - try { - var value = node._decode(input, options); - if (input.isError(value)) - return false; - - result = { type: key, value: value }; - match = true; - } catch (e) { - input.restore(save); - return false; - } - return true; - }, this); - - if (!match) - return input.error('Choice not matched'); - - return result; -}; - -// -// Encoding -// - -Node.prototype._createEncoderBuffer = function createEncoderBuffer(data) { - return new EncoderBuffer(data, this.reporter); -}; - -Node.prototype._encode = function encode(data, reporter, parent) { - var state = this._baseState; - if (state['default'] !== null && state['default'] === data) - return; - - var result = this._encodeValue(data, reporter, parent); - if (result === undefined) - return; - - if (this._skipDefault(result, reporter, parent)) - return; - - return result; -}; - -Node.prototype._encodeValue = function encode(data, reporter, parent) { - var state = this._baseState; - - // Decode root node - if (state.parent === null) - return state.children[0]._encode(data, reporter || new Reporter()); - - var result = null; - - // Set reporter to share it with a child class - this.reporter = reporter; - - // Check if data is there - if (state.optional && data === undefined) { - if (state['default'] !== null) - data = state['default'] - else - return; - } - - // Encode children first - var content = null; - var primitive = false; - if (state.any) { - // Anything that was given is translated to buffer - result = this._createEncoderBuffer(data); - } else if (state.choice) { - result = this._encodeChoice(data, reporter); - } else if (state.contains) { - content = this._getUse(state.contains, parent)._encode(data, reporter); - primitive = true; - } else if (state.children) { - content = state.children.map(function(child) { - if (child._baseState.tag === 'null_') - return child._encode(null, reporter, data); - - if (child._baseState.key === null) - return reporter.error('Child should have a key'); - var prevKey = reporter.enterKey(child._baseState.key); - - if (typeof data !== 'object') - return reporter.error('Child expected, but input is not object'); - - var res = child._encode(data[child._baseState.key], reporter, data); - reporter.leaveKey(prevKey); - - return res; - }, this).filter(function(child) { - return child; - }); - content = this._createEncoderBuffer(content); - } else { - if (state.tag === 'seqof' || state.tag === 'setof') { - // TODO(indutny): this should be thrown on DSL level - if (!(state.args && state.args.length === 1)) - return reporter.error('Too many args for : ' + state.tag); - - if (!Array.isArray(data)) - return reporter.error('seqof/setof, but data is not Array'); - - var child = this.clone(); - child._baseState.implicit = null; - content = this._createEncoderBuffer(data.map(function(item) { - var state = this._baseState; - - return this._getUse(state.args[0], data)._encode(item, reporter); - }, child)); - } else if (state.use !== null) { - result = this._getUse(state.use, parent)._encode(data, reporter); - } else { - content = this._encodePrimitive(state.tag, data); - primitive = true; - } - } - - // Encode data itself - var result; - if (!state.any && state.choice === null) { - var tag = state.implicit !== null ? state.implicit : state.tag; - var cls = state.implicit === null ? 'universal' : 'context'; - - if (tag === null) { - if (state.use === null) - reporter.error('Tag could be omitted only for .use()'); - } else { - if (state.use === null) - result = this._encodeComposite(tag, primitive, cls, content); - } - } - - // Wrap in explicit - if (state.explicit !== null) - result = this._encodeComposite(state.explicit, false, 'context', result); - - return result; -}; - -Node.prototype._encodeChoice = function encodeChoice(data, reporter) { - var state = this._baseState; - - var node = state.choice[data.type]; - if (!node) { - assert( - false, - data.type + ' not found in ' + - JSON.stringify(Object.keys(state.choice))); - } - return node._encode(data.value, reporter); -}; - -Node.prototype._encodePrimitive = function encodePrimitive(tag, data) { - var state = this._baseState; - - if (/str$/.test(tag)) - return this._encodeStr(data, tag); - else if (tag === 'objid' && state.args) - return this._encodeObjid(data, state.reverseArgs[0], state.args[1]); - else if (tag === 'objid') - return this._encodeObjid(data, null, null); - else if (tag === 'gentime' || tag === 'utctime') - return this._encodeTime(data, tag); - else if (tag === 'null_') - return this._encodeNull(); - else if (tag === 'int' || tag === 'enum') - return this._encodeInt(data, state.args && state.reverseArgs[0]); - else if (tag === 'bool') - return this._encodeBool(data); - else if (tag === 'objDesc') - return this._encodeStr(data, tag); - else - throw new Error('Unsupported tag: ' + tag); -}; - -Node.prototype._isNumstr = function isNumstr(str) { - return /^[0-9 ]*$/.test(str); -}; - -Node.prototype._isPrintstr = function isPrintstr(str) { - return /^[A-Za-z0-9 '\(\)\+,\-\.\/:=\?]*$/.test(str); -}; - -},{"../base":9,"minimalistic-assert":160}],11:[function(require,module,exports){ -var inherits = require('inherits'); - -function Reporter(options) { - this._reporterState = { - obj: null, - path: [], - options: options || {}, - errors: [] - }; -} -exports.Reporter = Reporter; - -Reporter.prototype.isError = function isError(obj) { - return obj instanceof ReporterError; -}; - -Reporter.prototype.save = function save() { - var state = this._reporterState; - - return { obj: state.obj, pathLen: state.path.length }; -}; - -Reporter.prototype.restore = function restore(data) { - var state = this._reporterState; - - state.obj = data.obj; - state.path = state.path.slice(0, data.pathLen); -}; - -Reporter.prototype.enterKey = function enterKey(key) { - return this._reporterState.path.push(key); -}; - -Reporter.prototype.exitKey = function exitKey(index) { - var state = this._reporterState; - - state.path = state.path.slice(0, index - 1); -}; - -Reporter.prototype.leaveKey = function leaveKey(index, key, value) { - var state = this._reporterState; - - this.exitKey(index); - if (state.obj !== null) - state.obj[key] = value; -}; - -Reporter.prototype.path = function path() { - return this._reporterState.path.join('/'); -}; - -Reporter.prototype.enterObject = function enterObject() { - var state = this._reporterState; - - var prev = state.obj; - state.obj = {}; - return prev; -}; - -Reporter.prototype.leaveObject = function leaveObject(prev) { - var state = this._reporterState; - - var now = state.obj; - state.obj = prev; - return now; -}; - -Reporter.prototype.error = function error(msg) { - var err; - var state = this._reporterState; - - var inherited = msg instanceof ReporterError; - if (inherited) { - err = msg; - } else { - err = new ReporterError(state.path.map(function(elem) { - return '[' + JSON.stringify(elem) + ']'; - }).join(''), msg.message || msg, msg.stack); - } - - if (!state.options.partial) - throw err; - - if (!inherited) - state.errors.push(err); - - return err; -}; - -Reporter.prototype.wrapResult = function wrapResult(result) { - var state = this._reporterState; - if (!state.options.partial) - return result; - - return { - result: this.isError(result) ? null : result, - errors: state.errors - }; -}; - -function ReporterError(path, msg) { - this.path = path; - this.rethrow(msg); -}; -inherits(ReporterError, Error); - -ReporterError.prototype.rethrow = function rethrow(msg) { - this.message = msg + ' at: ' + (this.path || '(shallow)'); - if (Error.captureStackTrace) - Error.captureStackTrace(this, ReporterError); - - if (!this.stack) { - try { - // IE only adds stack when thrown - throw new Error(this.message); - } catch (e) { - this.stack = e.stack; - } - } - return this; -}; - -},{"inherits":155}],12:[function(require,module,exports){ -var constants = require('../constants'); - -exports.tagClass = { - 0: 'universal', - 1: 'application', - 2: 'context', - 3: 'private' -}; -exports.tagClassByName = constants._reverse(exports.tagClass); - -exports.tag = { - 0x00: 'end', - 0x01: 'bool', - 0x02: 'int', - 0x03: 'bitstr', - 0x04: 'octstr', - 0x05: 'null_', - 0x06: 'objid', - 0x07: 'objDesc', - 0x08: 'external', - 0x09: 'real', - 0x0a: 'enum', - 0x0b: 'embed', - 0x0c: 'utf8str', - 0x0d: 'relativeOid', - 0x10: 'seq', - 0x11: 'set', - 0x12: 'numstr', - 0x13: 'printstr', - 0x14: 't61str', - 0x15: 'videostr', - 0x16: 'ia5str', - 0x17: 'utctime', - 0x18: 'gentime', - 0x19: 'graphstr', - 0x1a: 'iso646str', - 0x1b: 'genstr', - 0x1c: 'unistr', - 0x1d: 'charstr', - 0x1e: 'bmpstr' -}; -exports.tagByName = constants._reverse(exports.tag); - -},{"../constants":13}],13:[function(require,module,exports){ -var constants = exports; - -// Helper -constants._reverse = function reverse(map) { - var res = {}; - - Object.keys(map).forEach(function(key) { - // Convert key to integer if it is stringified - if ((key | 0) == key) - key = key | 0; - - var value = map[key]; - res[value] = key; - }); - - return res; -}; - -constants.der = require('./der'); - -},{"./der":12}],14:[function(require,module,exports){ -var inherits = require('inherits'); - -var asn1 = require('../../asn1'); -var base = asn1.base; -var bignum = asn1.bignum; - -// Import DER constants -var der = asn1.constants.der; - -function DERDecoder(entity) { - this.enc = 'der'; - this.name = entity.name; - this.entity = entity; - - // Construct base tree - this.tree = new DERNode(); - this.tree._init(entity.body); -}; -module.exports = DERDecoder; - -DERDecoder.prototype.decode = function decode(data, options) { - if (!(data instanceof base.DecoderBuffer)) - data = new base.DecoderBuffer(data, options); - - return this.tree._decode(data, options); -}; - -// Tree methods - -function DERNode(parent) { - base.Node.call(this, 'der', parent); -} -inherits(DERNode, base.Node); - -DERNode.prototype._peekTag = function peekTag(buffer, tag, any) { - if (buffer.isEmpty()) - return false; - - var state = buffer.save(); - var decodedTag = derDecodeTag(buffer, 'Failed to peek tag: "' + tag + '"'); - if (buffer.isError(decodedTag)) - return decodedTag; - - buffer.restore(state); - - return decodedTag.tag === tag || decodedTag.tagStr === tag || - (decodedTag.tagStr + 'of') === tag || any; -}; - -DERNode.prototype._decodeTag = function decodeTag(buffer, tag, any) { - var decodedTag = derDecodeTag(buffer, - 'Failed to decode tag of "' + tag + '"'); - if (buffer.isError(decodedTag)) - return decodedTag; - - var len = derDecodeLen(buffer, - decodedTag.primitive, - 'Failed to get length of "' + tag + '"'); - - // Failure - if (buffer.isError(len)) - return len; - - if (!any && - decodedTag.tag !== tag && - decodedTag.tagStr !== tag && - decodedTag.tagStr + 'of' !== tag) { - return buffer.error('Failed to match tag: "' + tag + '"'); - } - - if (decodedTag.primitive || len !== null) - return buffer.skip(len, 'Failed to match body of: "' + tag + '"'); - - // Indefinite length... find END tag - var state = buffer.save(); - var res = this._skipUntilEnd( - buffer, - 'Failed to skip indefinite length body: "' + this.tag + '"'); - if (buffer.isError(res)) - return res; - - len = buffer.offset - state.offset; - buffer.restore(state); - return buffer.skip(len, 'Failed to match body of: "' + tag + '"'); -}; - -DERNode.prototype._skipUntilEnd = function skipUntilEnd(buffer, fail) { - while (true) { - var tag = derDecodeTag(buffer, fail); - if (buffer.isError(tag)) - return tag; - var len = derDecodeLen(buffer, tag.primitive, fail); - if (buffer.isError(len)) - return len; - - var res; - if (tag.primitive || len !== null) - res = buffer.skip(len) - else - res = this._skipUntilEnd(buffer, fail); - - // Failure - if (buffer.isError(res)) - return res; - - if (tag.tagStr === 'end') - break; - } -}; - -DERNode.prototype._decodeList = function decodeList(buffer, tag, decoder, - options) { - var result = []; - while (!buffer.isEmpty()) { - var possibleEnd = this._peekTag(buffer, 'end'); - if (buffer.isError(possibleEnd)) - return possibleEnd; - - var res = decoder.decode(buffer, 'der', options); - if (buffer.isError(res) && possibleEnd) - break; - result.push(res); - } - return result; -}; - -DERNode.prototype._decodeStr = function decodeStr(buffer, tag) { - if (tag === 'bitstr') { - var unused = buffer.readUInt8(); - if (buffer.isError(unused)) - return unused; - return { unused: unused, data: buffer.raw() }; - } else if (tag === 'bmpstr') { - var raw = buffer.raw(); - if (raw.length % 2 === 1) - return buffer.error('Decoding of string type: bmpstr length mismatch'); - - var str = ''; - for (var i = 0; i < raw.length / 2; i++) { - str += String.fromCharCode(raw.readUInt16BE(i * 2)); - } - return str; - } else if (tag === 'numstr') { - var numstr = buffer.raw().toString('ascii'); - if (!this._isNumstr(numstr)) { - return buffer.error('Decoding of string type: ' + - 'numstr unsupported characters'); - } - return numstr; - } else if (tag === 'octstr') { - return buffer.raw(); - } else if (tag === 'objDesc') { - return buffer.raw(); - } else if (tag === 'printstr') { - var printstr = buffer.raw().toString('ascii'); - if (!this._isPrintstr(printstr)) { - return buffer.error('Decoding of string type: ' + - 'printstr unsupported characters'); - } - return printstr; - } else if (/str$/.test(tag)) { - return buffer.raw().toString(); - } else { - return buffer.error('Decoding of string type: ' + tag + ' unsupported'); - } -}; - -DERNode.prototype._decodeObjid = function decodeObjid(buffer, values, relative) { - var result; - var identifiers = []; - var ident = 0; - while (!buffer.isEmpty()) { - var subident = buffer.readUInt8(); - ident <<= 7; - ident |= subident & 0x7f; - if ((subident & 0x80) === 0) { - identifiers.push(ident); - ident = 0; - } - } - if (subident & 0x80) - identifiers.push(ident); - - var first = (identifiers[0] / 40) | 0; - var second = identifiers[0] % 40; - - if (relative) - result = identifiers; - else - result = [first, second].concat(identifiers.slice(1)); - - if (values) { - var tmp = values[result.join(' ')]; - if (tmp === undefined) - tmp = values[result.join('.')]; - if (tmp !== undefined) - result = tmp; - } - - return result; -}; - -DERNode.prototype._decodeTime = function decodeTime(buffer, tag) { - var str = buffer.raw().toString(); - if (tag === 'gentime') { - var year = str.slice(0, 4) | 0; - var mon = str.slice(4, 6) | 0; - var day = str.slice(6, 8) | 0; - var hour = str.slice(8, 10) | 0; - var min = str.slice(10, 12) | 0; - var sec = str.slice(12, 14) | 0; - } else if (tag === 'utctime') { - var year = str.slice(0, 2) | 0; - var mon = str.slice(2, 4) | 0; - var day = str.slice(4, 6) | 0; - var hour = str.slice(6, 8) | 0; - var min = str.slice(8, 10) | 0; - var sec = str.slice(10, 12) | 0; - if (year < 70) - year = 2000 + year; - else - year = 1900 + year; - } else { - return buffer.error('Decoding ' + tag + ' time is not supported yet'); - } - - return Date.UTC(year, mon - 1, day, hour, min, sec, 0); -}; - -DERNode.prototype._decodeNull = function decodeNull(buffer) { - return null; -}; - -DERNode.prototype._decodeBool = function decodeBool(buffer) { - var res = buffer.readUInt8(); - if (buffer.isError(res)) - return res; - else - return res !== 0; -}; - -DERNode.prototype._decodeInt = function decodeInt(buffer, values) { - // Bigint, return as it is (assume big endian) - var raw = buffer.raw(); - var res = new bignum(raw); - - if (values) - res = values[res.toString(10)] || res; - - return res; -}; - -DERNode.prototype._use = function use(entity, obj) { - if (typeof entity === 'function') - entity = entity(obj); - return entity._getDecoder('der').tree; -}; - -// Utility methods - -function derDecodeTag(buf, fail) { - var tag = buf.readUInt8(fail); - if (buf.isError(tag)) - return tag; - - var cls = der.tagClass[tag >> 6]; - var primitive = (tag & 0x20) === 0; - - // Multi-octet tag - load - if ((tag & 0x1f) === 0x1f) { - var oct = tag; - tag = 0; - while ((oct & 0x80) === 0x80) { - oct = buf.readUInt8(fail); - if (buf.isError(oct)) - return oct; - - tag <<= 7; - tag |= oct & 0x7f; - } - } else { - tag &= 0x1f; - } - var tagStr = der.tag[tag]; - - return { - cls: cls, - primitive: primitive, - tag: tag, - tagStr: tagStr - }; -} - -function derDecodeLen(buf, primitive, fail) { - var len = buf.readUInt8(fail); - if (buf.isError(len)) - return len; - - // Indefinite form - if (!primitive && len === 0x80) - return null; - - // Definite form - if ((len & 0x80) === 0) { - // Short form - return len; - } - - // Long form - var num = len & 0x7f; - if (num > 4) - return buf.error('length octect is too long'); - - len = 0; - for (var i = 0; i < num; i++) { - len <<= 8; - var j = buf.readUInt8(fail); - if (buf.isError(j)) - return j; - len |= j; - } - - return len; -} - -},{"../../asn1":6,"inherits":155}],15:[function(require,module,exports){ -var decoders = exports; - -decoders.der = require('./der'); -decoders.pem = require('./pem'); - -},{"./der":14,"./pem":16}],16:[function(require,module,exports){ -var inherits = require('inherits'); -var Buffer = require('buffer').Buffer; - -var DERDecoder = require('./der'); - -function PEMDecoder(entity) { - DERDecoder.call(this, entity); - this.enc = 'pem'; -}; -inherits(PEMDecoder, DERDecoder); -module.exports = PEMDecoder; - -PEMDecoder.prototype.decode = function decode(data, options) { - var lines = data.toString().split(/[\r\n]+/g); - - var label = options.label.toUpperCase(); - - var re = /^-----(BEGIN|END) ([^-]+)-----$/; - var start = -1; - var end = -1; - for (var i = 0; i < lines.length; i++) { - var match = lines[i].match(re); - if (match === null) - continue; - - if (match[2] !== label) - continue; - - if (start === -1) { - if (match[1] !== 'BEGIN') - break; - start = i; - } else { - if (match[1] !== 'END') - break; - end = i; - break; - } - } - if (start === -1 || end === -1) - throw new Error('PEM section not found for: ' + label); - - var base64 = lines.slice(start + 1, end).join(''); - // Remove excessive symbols - base64.replace(/[^a-z0-9\+\/=]+/gi, ''); - - var input = new Buffer(base64, 'base64'); - return DERDecoder.prototype.decode.call(this, input, options); -}; - -},{"./der":14,"buffer":63,"inherits":155}],17:[function(require,module,exports){ -var inherits = require('inherits'); -var Buffer = require('buffer').Buffer; - -var asn1 = require('../../asn1'); -var base = asn1.base; - -// Import DER constants -var der = asn1.constants.der; - -function DEREncoder(entity) { - this.enc = 'der'; - this.name = entity.name; - this.entity = entity; - - // Construct base tree - this.tree = new DERNode(); - this.tree._init(entity.body); -}; -module.exports = DEREncoder; - -DEREncoder.prototype.encode = function encode(data, reporter) { - return this.tree._encode(data, reporter).join(); -}; - -// Tree methods - -function DERNode(parent) { - base.Node.call(this, 'der', parent); -} -inherits(DERNode, base.Node); - -DERNode.prototype._encodeComposite = function encodeComposite(tag, - primitive, - cls, - content) { - var encodedTag = encodeTag(tag, primitive, cls, this.reporter); - - // Short form - if (content.length < 0x80) { - var header = new Buffer(2); - header[0] = encodedTag; - header[1] = content.length; - return this._createEncoderBuffer([ header, content ]); - } - - // Long form - // Count octets required to store length - var lenOctets = 1; - for (var i = content.length; i >= 0x100; i >>= 8) - lenOctets++; - - var header = new Buffer(1 + 1 + lenOctets); - header[0] = encodedTag; - header[1] = 0x80 | lenOctets; - - for (var i = 1 + lenOctets, j = content.length; j > 0; i--, j >>= 8) - header[i] = j & 0xff; - - return this._createEncoderBuffer([ header, content ]); -}; - -DERNode.prototype._encodeStr = function encodeStr(str, tag) { - if (tag === 'bitstr') { - return this._createEncoderBuffer([ str.unused | 0, str.data ]); - } else if (tag === 'bmpstr') { - var buf = new Buffer(str.length * 2); - for (var i = 0; i < str.length; i++) { - buf.writeUInt16BE(str.charCodeAt(i), i * 2); - } - return this._createEncoderBuffer(buf); - } else if (tag === 'numstr') { - if (!this._isNumstr(str)) { - return this.reporter.error('Encoding of string type: numstr supports ' + - 'only digits and space'); - } - return this._createEncoderBuffer(str); - } else if (tag === 'printstr') { - if (!this._isPrintstr(str)) { - return this.reporter.error('Encoding of string type: printstr supports ' + - 'only latin upper and lower case letters, ' + - 'digits, space, apostrophe, left and rigth ' + - 'parenthesis, plus sign, comma, hyphen, ' + - 'dot, slash, colon, equal sign, ' + - 'question mark'); - } - return this._createEncoderBuffer(str); - } else if (/str$/.test(tag)) { - return this._createEncoderBuffer(str); - } else if (tag === 'objDesc') { - return this._createEncoderBuffer(str); - } else { - return this.reporter.error('Encoding of string type: ' + tag + - ' unsupported'); - } -}; - -DERNode.prototype._encodeObjid = function encodeObjid(id, values, relative) { - if (typeof id === 'string') { - if (!values) - return this.reporter.error('string objid given, but no values map found'); - if (!values.hasOwnProperty(id)) - return this.reporter.error('objid not found in values map'); - id = values[id].split(/[\s\.]+/g); - for (var i = 0; i < id.length; i++) - id[i] |= 0; - } else if (Array.isArray(id)) { - id = id.slice(); - for (var i = 0; i < id.length; i++) - id[i] |= 0; - } - - if (!Array.isArray(id)) { - return this.reporter.error('objid() should be either array or string, ' + - 'got: ' + JSON.stringify(id)); - } - - if (!relative) { - if (id[1] >= 40) - return this.reporter.error('Second objid identifier OOB'); - id.splice(0, 2, id[0] * 40 + id[1]); - } - - // Count number of octets - var size = 0; - for (var i = 0; i < id.length; i++) { - var ident = id[i]; - for (size++; ident >= 0x80; ident >>= 7) - size++; - } - - var objid = new Buffer(size); - var offset = objid.length - 1; - for (var i = id.length - 1; i >= 0; i--) { - var ident = id[i]; - objid[offset--] = ident & 0x7f; - while ((ident >>= 7) > 0) - objid[offset--] = 0x80 | (ident & 0x7f); - } - - return this._createEncoderBuffer(objid); -}; - -function two(num) { - if (num < 10) - return '0' + num; - else - return num; -} - -DERNode.prototype._encodeTime = function encodeTime(time, tag) { - var str; - var date = new Date(time); - - if (tag === 'gentime') { - str = [ - two(date.getFullYear()), - two(date.getUTCMonth() + 1), - two(date.getUTCDate()), - two(date.getUTCHours()), - two(date.getUTCMinutes()), - two(date.getUTCSeconds()), - 'Z' - ].join(''); - } else if (tag === 'utctime') { - str = [ - two(date.getFullYear() % 100), - two(date.getUTCMonth() + 1), - two(date.getUTCDate()), - two(date.getUTCHours()), - two(date.getUTCMinutes()), - two(date.getUTCSeconds()), - 'Z' - ].join(''); - } else { - this.reporter.error('Encoding ' + tag + ' time is not supported yet'); - } - - return this._encodeStr(str, 'octstr'); -}; - -DERNode.prototype._encodeNull = function encodeNull() { - return this._createEncoderBuffer(''); -}; - -DERNode.prototype._encodeInt = function encodeInt(num, values) { - if (typeof num === 'string') { - if (!values) - return this.reporter.error('String int or enum given, but no values map'); - if (!values.hasOwnProperty(num)) { - return this.reporter.error('Values map doesn\'t contain: ' + - JSON.stringify(num)); - } - num = values[num]; - } - - // Bignum, assume big endian - if (typeof num !== 'number' && !Buffer.isBuffer(num)) { - var numArray = num.toArray(); - if (!num.sign && numArray[0] & 0x80) { - numArray.unshift(0); - } - num = new Buffer(numArray); - } - - if (Buffer.isBuffer(num)) { - var size = num.length; - if (num.length === 0) - size++; - - var out = new Buffer(size); - num.copy(out); - if (num.length === 0) - out[0] = 0 - return this._createEncoderBuffer(out); - } - - if (num < 0x80) - return this._createEncoderBuffer(num); - - if (num < 0x100) - return this._createEncoderBuffer([0, num]); - - var size = 1; - for (var i = num; i >= 0x100; i >>= 8) - size++; - - var out = new Array(size); - for (var i = out.length - 1; i >= 0; i--) { - out[i] = num & 0xff; - num >>= 8; - } - if(out[0] & 0x80) { - out.unshift(0); - } - - return this._createEncoderBuffer(new Buffer(out)); -}; - -DERNode.prototype._encodeBool = function encodeBool(value) { - return this._createEncoderBuffer(value ? 0xff : 0); -}; - -DERNode.prototype._use = function use(entity, obj) { - if (typeof entity === 'function') - entity = entity(obj); - return entity._getEncoder('der').tree; -}; - -DERNode.prototype._skipDefault = function skipDefault(dataBuffer, reporter, parent) { - var state = this._baseState; - var i; - if (state['default'] === null) - return false; - - var data = dataBuffer.join(); - if (state.defaultBuffer === undefined) - state.defaultBuffer = this._encodeValue(state['default'], reporter, parent).join(); - - if (data.length !== state.defaultBuffer.length) - return false; - - for (i=0; i < data.length; i++) - if (data[i] !== state.defaultBuffer[i]) - return false; - - return true; -}; - -// Utility methods - -function encodeTag(tag, primitive, cls, reporter) { - var res; - - if (tag === 'seqof') - tag = 'seq'; - else if (tag === 'setof') - tag = 'set'; - - if (der.tagByName.hasOwnProperty(tag)) - res = der.tagByName[tag]; - else if (typeof tag === 'number' && (tag | 0) === tag) - res = tag; - else - return reporter.error('Unknown tag: ' + tag); - - if (res >= 0x1f) - return reporter.error('Multi-octet tag encoding unsupported'); - - if (!primitive) - res |= 0x20; - - res |= (der.tagClassByName[cls || 'universal'] << 6); - - return res; -} - -},{"../../asn1":6,"buffer":63,"inherits":155}],18:[function(require,module,exports){ -var encoders = exports; - -encoders.der = require('./der'); -encoders.pem = require('./pem'); - -},{"./der":17,"./pem":19}],19:[function(require,module,exports){ -var inherits = require('inherits'); - -var DEREncoder = require('./der'); - -function PEMEncoder(entity) { - DEREncoder.call(this, entity); - this.enc = 'pem'; -}; -inherits(PEMEncoder, DEREncoder); -module.exports = PEMEncoder; - -PEMEncoder.prototype.encode = function encode(data, options) { - var buf = DEREncoder.prototype.encode.call(this, data); - - var p = buf.toString('base64'); - var out = [ '-----BEGIN ' + options.label + '-----' ]; - for (var i = 0; i < p.length; i += 64) - out.push(p.slice(i, i + 64)); - out.push('-----END ' + options.label + '-----'); - return out.join('\n'); -}; - -},{"./der":17,"inherits":155}],20:[function(require,module,exports){ -(function (module, exports) { - 'use strict'; - - // Utils - function assert (val, msg) { - if (!val) throw new Error(msg || 'Assertion failed'); - } - - // Could use `inherits` module, but don't want to move from single file - // architecture yet. - function inherits (ctor, superCtor) { - ctor.super_ = superCtor; - var TempCtor = function () {}; - TempCtor.prototype = superCtor.prototype; - ctor.prototype = new TempCtor(); - ctor.prototype.constructor = ctor; - } - - // BN - - function BN (number, base, endian) { - if (BN.isBN(number)) { - return number; - } - - this.negative = 0; - this.words = null; - this.length = 0; - - // Reduction context - this.red = null; - - if (number !== null) { - if (base === 'le' || base === 'be') { - endian = base; - base = 10; - } - - this._init(number || 0, base || 10, endian || 'be'); - } - } - if (typeof module === 'object') { - module.exports = BN; - } else { - exports.BN = BN; - } - - BN.BN = BN; - BN.wordSize = 26; - - var Buffer; - try { - if (typeof window !== 'undefined' && typeof window.Buffer !== 'undefined') { - Buffer = window.Buffer; - } else { - Buffer = require('buffer').Buffer; - } - } catch (e) { - } - - BN.isBN = function isBN (num) { - if (num instanceof BN) { - return true; - } - - return num !== null && typeof num === 'object' && - num.constructor.wordSize === BN.wordSize && Array.isArray(num.words); - }; - - BN.max = function max (left, right) { - if (left.cmp(right) > 0) return left; - return right; - }; - - BN.min = function min (left, right) { - if (left.cmp(right) < 0) return left; - return right; - }; - - BN.prototype._init = function init (number, base, endian) { - if (typeof number === 'number') { - return this._initNumber(number, base, endian); - } - - if (typeof number === 'object') { - return this._initArray(number, base, endian); - } - - if (base === 'hex') { - base = 16; - } - assert(base === (base | 0) && base >= 2 && base <= 36); - - number = number.toString().replace(/\s+/g, ''); - var start = 0; - if (number[0] === '-') { - start++; - this.negative = 1; - } - - if (start < number.length) { - if (base === 16) { - this._parseHex(number, start, endian); - } else { - this._parseBase(number, base, start); - if (endian === 'le') { - this._initArray(this.toArray(), base, endian); - } - } - } - }; - - BN.prototype._initNumber = function _initNumber (number, base, endian) { - if (number < 0) { - this.negative = 1; - number = -number; - } - if (number < 0x4000000) { - this.words = [ number & 0x3ffffff ]; - this.length = 1; - } else if (number < 0x10000000000000) { - this.words = [ - number & 0x3ffffff, - (number / 0x4000000) & 0x3ffffff - ]; - this.length = 2; - } else { - assert(number < 0x20000000000000); // 2 ^ 53 (unsafe) - this.words = [ - number & 0x3ffffff, - (number / 0x4000000) & 0x3ffffff, - 1 - ]; - this.length = 3; - } - - if (endian !== 'le') return; - - // Reverse the bytes - this._initArray(this.toArray(), base, endian); - }; - - BN.prototype._initArray = function _initArray (number, base, endian) { - // Perhaps a Uint8Array - assert(typeof number.length === 'number'); - if (number.length <= 0) { - this.words = [ 0 ]; - this.length = 1; - return this; - } - - this.length = Math.ceil(number.length / 3); - this.words = new Array(this.length); - for (var i = 0; i < this.length; i++) { - this.words[i] = 0; - } - - var j, w; - var off = 0; - if (endian === 'be') { - for (i = number.length - 1, j = 0; i >= 0; i -= 3) { - w = number[i] | (number[i - 1] << 8) | (number[i - 2] << 16); - this.words[j] |= (w << off) & 0x3ffffff; - this.words[j + 1] = (w >>> (26 - off)) & 0x3ffffff; - off += 24; - if (off >= 26) { - off -= 26; - j++; - } - } - } else if (endian === 'le') { - for (i = 0, j = 0; i < number.length; i += 3) { - w = number[i] | (number[i + 1] << 8) | (number[i + 2] << 16); - this.words[j] |= (w << off) & 0x3ffffff; - this.words[j + 1] = (w >>> (26 - off)) & 0x3ffffff; - off += 24; - if (off >= 26) { - off -= 26; - j++; - } - } - } - return this.strip(); - }; - - function parseHex4Bits (string, index) { - var c = string.charCodeAt(index); - // 'A' - 'F' - if (c >= 65 && c <= 70) { - return c - 55; - // 'a' - 'f' - } else if (c >= 97 && c <= 102) { - return c - 87; - // '0' - '9' - } else { - return (c - 48) & 0xf; - } - } - - function parseHexByte (string, lowerBound, index) { - var r = parseHex4Bits(string, index); - if (index - 1 >= lowerBound) { - r |= parseHex4Bits(string, index - 1) << 4; - } - return r; - } - - BN.prototype._parseHex = function _parseHex (number, start, endian) { - // Create possibly bigger array to ensure that it fits the number - this.length = Math.ceil((number.length - start) / 6); - this.words = new Array(this.length); - for (var i = 0; i < this.length; i++) { - this.words[i] = 0; - } - - // 24-bits chunks - var off = 0; - var j = 0; - - var w; - if (endian === 'be') { - for (i = number.length - 1; i >= start; i -= 2) { - w = parseHexByte(number, start, i) << off; - this.words[j] |= w & 0x3ffffff; - if (off >= 18) { - off -= 18; - j += 1; - this.words[j] |= w >>> 26; - } else { - off += 8; - } - } - } else { - var parseLength = number.length - start; - for (i = parseLength % 2 === 0 ? start + 1 : start; i < number.length; i += 2) { - w = parseHexByte(number, start, i) << off; - this.words[j] |= w & 0x3ffffff; - if (off >= 18) { - off -= 18; - j += 1; - this.words[j] |= w >>> 26; - } else { - off += 8; - } - } - } - - this.strip(); - }; - - function parseBase (str, start, end, mul) { - var r = 0; - var len = Math.min(str.length, end); - for (var i = start; i < len; i++) { - var c = str.charCodeAt(i) - 48; - - r *= mul; - - // 'a' - if (c >= 49) { - r += c - 49 + 0xa; - - // 'A' - } else if (c >= 17) { - r += c - 17 + 0xa; - - // '0' - '9' - } else { - r += c; - } - } - return r; - } - - BN.prototype._parseBase = function _parseBase (number, base, start) { - // Initialize as zero - this.words = [ 0 ]; - this.length = 1; - - // Find length of limb in base - for (var limbLen = 0, limbPow = 1; limbPow <= 0x3ffffff; limbPow *= base) { - limbLen++; - } - limbLen--; - limbPow = (limbPow / base) | 0; - - var total = number.length - start; - var mod = total % limbLen; - var end = Math.min(total, total - mod) + start; - - var word = 0; - for (var i = start; i < end; i += limbLen) { - word = parseBase(number, i, i + limbLen, base); - - this.imuln(limbPow); - if (this.words[0] + word < 0x4000000) { - this.words[0] += word; - } else { - this._iaddn(word); - } - } - - if (mod !== 0) { - var pow = 1; - word = parseBase(number, i, number.length, base); - - for (i = 0; i < mod; i++) { - pow *= base; - } - - this.imuln(pow); - if (this.words[0] + word < 0x4000000) { - this.words[0] += word; - } else { - this._iaddn(word); - } - } - - this.strip(); - }; - - BN.prototype.copy = function copy (dest) { - dest.words = new Array(this.length); - for (var i = 0; i < this.length; i++) { - dest.words[i] = this.words[i]; - } - dest.length = this.length; - dest.negative = this.negative; - dest.red = this.red; - }; - - BN.prototype.clone = function clone () { - var r = new BN(null); - this.copy(r); - return r; - }; - - BN.prototype._expand = function _expand (size) { - while (this.length < size) { - this.words[this.length++] = 0; - } - return this; - }; - - // Remove leading `0` from `this` - BN.prototype.strip = function strip () { - while (this.length > 1 && this.words[this.length - 1] === 0) { - this.length--; - } - return this._normSign(); - }; - - BN.prototype._normSign = function _normSign () { - // -0 = 0 - if (this.length === 1 && this.words[0] === 0) { - this.negative = 0; - } - return this; - }; - - BN.prototype.inspect = function inspect () { - return (this.red ? ''; - }; - - /* - - var zeros = []; - var groupSizes = []; - var groupBases = []; - - var s = ''; - var i = -1; - while (++i < BN.wordSize) { - zeros[i] = s; - s += '0'; - } - groupSizes[0] = 0; - groupSizes[1] = 0; - groupBases[0] = 0; - groupBases[1] = 0; - var base = 2 - 1; - while (++base < 36 + 1) { - var groupSize = 0; - var groupBase = 1; - while (groupBase < (1 << BN.wordSize) / base) { - groupBase *= base; - groupSize += 1; - } - groupSizes[base] = groupSize; - groupBases[base] = groupBase; - } - - */ - - var zeros = [ - '', - '0', - '00', - '000', - '0000', - '00000', - '000000', - '0000000', - '00000000', - '000000000', - '0000000000', - '00000000000', - '000000000000', - '0000000000000', - '00000000000000', - '000000000000000', - '0000000000000000', - '00000000000000000', - '000000000000000000', - '0000000000000000000', - '00000000000000000000', - '000000000000000000000', - '0000000000000000000000', - '00000000000000000000000', - '000000000000000000000000', - '0000000000000000000000000' - ]; - - var groupSizes = [ - 0, 0, - 25, 16, 12, 11, 10, 9, 8, - 8, 7, 7, 7, 7, 6, 6, - 6, 6, 6, 6, 6, 5, 5, - 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5 - ]; - - var groupBases = [ - 0, 0, - 33554432, 43046721, 16777216, 48828125, 60466176, 40353607, 16777216, - 43046721, 10000000, 19487171, 35831808, 62748517, 7529536, 11390625, - 16777216, 24137569, 34012224, 47045881, 64000000, 4084101, 5153632, - 6436343, 7962624, 9765625, 11881376, 14348907, 17210368, 20511149, - 24300000, 28629151, 33554432, 39135393, 45435424, 52521875, 60466176 - ]; - - BN.prototype.toString = function toString (base, padding) { - base = base || 10; - padding = padding | 0 || 1; - - var out; - if (base === 16 || base === 'hex') { - out = ''; - var off = 0; - var carry = 0; - for (var i = 0; i < this.length; i++) { - var w = this.words[i]; - var word = (((w << off) | carry) & 0xffffff).toString(16); - carry = (w >>> (24 - off)) & 0xffffff; - if (carry !== 0 || i !== this.length - 1) { - out = zeros[6 - word.length] + word + out; - } else { - out = word + out; - } - off += 2; - if (off >= 26) { - off -= 26; - i--; - } - } - if (carry !== 0) { - out = carry.toString(16) + out; - } - while (out.length % padding !== 0) { - out = '0' + out; - } - if (this.negative !== 0) { - out = '-' + out; - } - return out; - } - - if (base === (base | 0) && base >= 2 && base <= 36) { - // var groupSize = Math.floor(BN.wordSize * Math.LN2 / Math.log(base)); - var groupSize = groupSizes[base]; - // var groupBase = Math.pow(base, groupSize); - var groupBase = groupBases[base]; - out = ''; - var c = this.clone(); - c.negative = 0; - while (!c.isZero()) { - var r = c.modn(groupBase).toString(base); - c = c.idivn(groupBase); - - if (!c.isZero()) { - out = zeros[groupSize - r.length] + r + out; - } else { - out = r + out; - } - } - if (this.isZero()) { - out = '0' + out; - } - while (out.length % padding !== 0) { - out = '0' + out; - } - if (this.negative !== 0) { - out = '-' + out; - } - return out; - } - - assert(false, 'Base should be between 2 and 36'); - }; - - BN.prototype.toNumber = function toNumber () { - var ret = this.words[0]; - if (this.length === 2) { - ret += this.words[1] * 0x4000000; - } else if (this.length === 3 && this.words[2] === 0x01) { - // NOTE: at this stage it is known that the top bit is set - ret += 0x10000000000000 + (this.words[1] * 0x4000000); - } else if (this.length > 2) { - assert(false, 'Number can only safely store up to 53 bits'); - } - return (this.negative !== 0) ? -ret : ret; - }; - - BN.prototype.toJSON = function toJSON () { - return this.toString(16); - }; - - BN.prototype.toBuffer = function toBuffer (endian, length) { - assert(typeof Buffer !== 'undefined'); - return this.toArrayLike(Buffer, endian, length); - }; - - BN.prototype.toArray = function toArray (endian, length) { - return this.toArrayLike(Array, endian, length); - }; - - BN.prototype.toArrayLike = function toArrayLike (ArrayType, endian, length) { - var byteLength = this.byteLength(); - var reqLength = length || Math.max(1, byteLength); - assert(byteLength <= reqLength, 'byte array longer than desired length'); - assert(reqLength > 0, 'Requested array length <= 0'); - - this.strip(); - var littleEndian = endian === 'le'; - var res = new ArrayType(reqLength); - - var b, i; - var q = this.clone(); - if (!littleEndian) { - // Assume big-endian - for (i = 0; i < reqLength - byteLength; i++) { - res[i] = 0; - } - - for (i = 0; !q.isZero(); i++) { - b = q.andln(0xff); - q.iushrn(8); - - res[reqLength - i - 1] = b; - } - } else { - for (i = 0; !q.isZero(); i++) { - b = q.andln(0xff); - q.iushrn(8); - - res[i] = b; - } - - for (; i < reqLength; i++) { - res[i] = 0; - } - } - - return res; - }; - - if (Math.clz32) { - BN.prototype._countBits = function _countBits (w) { - return 32 - Math.clz32(w); - }; - } else { - BN.prototype._countBits = function _countBits (w) { - var t = w; - var r = 0; - if (t >= 0x1000) { - r += 13; - t >>>= 13; - } - if (t >= 0x40) { - r += 7; - t >>>= 7; - } - if (t >= 0x8) { - r += 4; - t >>>= 4; - } - if (t >= 0x02) { - r += 2; - t >>>= 2; - } - return r + t; - }; - } - - BN.prototype._zeroBits = function _zeroBits (w) { - // Short-cut - if (w === 0) return 26; - - var t = w; - var r = 0; - if ((t & 0x1fff) === 0) { - r += 13; - t >>>= 13; - } - if ((t & 0x7f) === 0) { - r += 7; - t >>>= 7; - } - if ((t & 0xf) === 0) { - r += 4; - t >>>= 4; - } - if ((t & 0x3) === 0) { - r += 2; - t >>>= 2; - } - if ((t & 0x1) === 0) { - r++; - } - return r; - }; - - // Return number of used bits in a BN - BN.prototype.bitLength = function bitLength () { - var w = this.words[this.length - 1]; - var hi = this._countBits(w); - return (this.length - 1) * 26 + hi; - }; - - function toBitArray (num) { - var w = new Array(num.bitLength()); - - for (var bit = 0; bit < w.length; bit++) { - var off = (bit / 26) | 0; - var wbit = bit % 26; - - w[bit] = (num.words[off] & (1 << wbit)) >>> wbit; - } - - return w; - } - - // Number of trailing zero bits - BN.prototype.zeroBits = function zeroBits () { - if (this.isZero()) return 0; - - var r = 0; - for (var i = 0; i < this.length; i++) { - var b = this._zeroBits(this.words[i]); - r += b; - if (b !== 26) break; - } - return r; - }; - - BN.prototype.byteLength = function byteLength () { - return Math.ceil(this.bitLength() / 8); - }; - - BN.prototype.toTwos = function toTwos (width) { - if (this.negative !== 0) { - return this.abs().inotn(width).iaddn(1); - } - return this.clone(); - }; - - BN.prototype.fromTwos = function fromTwos (width) { - if (this.testn(width - 1)) { - return this.notn(width).iaddn(1).ineg(); - } - return this.clone(); - }; - - BN.prototype.isNeg = function isNeg () { - return this.negative !== 0; - }; - - // Return negative clone of `this` - BN.prototype.neg = function neg () { - return this.clone().ineg(); - }; - - BN.prototype.ineg = function ineg () { - if (!this.isZero()) { - this.negative ^= 1; - } - - return this; - }; - - // Or `num` with `this` in-place - BN.prototype.iuor = function iuor (num) { - while (this.length < num.length) { - this.words[this.length++] = 0; - } - - for (var i = 0; i < num.length; i++) { - this.words[i] = this.words[i] | num.words[i]; - } - - return this.strip(); - }; - - BN.prototype.ior = function ior (num) { - assert((this.negative | num.negative) === 0); - return this.iuor(num); - }; - - // Or `num` with `this` - BN.prototype.or = function or (num) { - if (this.length > num.length) return this.clone().ior(num); - return num.clone().ior(this); - }; - - BN.prototype.uor = function uor (num) { - if (this.length > num.length) return this.clone().iuor(num); - return num.clone().iuor(this); - }; - - // And `num` with `this` in-place - BN.prototype.iuand = function iuand (num) { - // b = min-length(num, this) - var b; - if (this.length > num.length) { - b = num; - } else { - b = this; - } - - for (var i = 0; i < b.length; i++) { - this.words[i] = this.words[i] & num.words[i]; - } - - this.length = b.length; - - return this.strip(); - }; - - BN.prototype.iand = function iand (num) { - assert((this.negative | num.negative) === 0); - return this.iuand(num); - }; - - // And `num` with `this` - BN.prototype.and = function and (num) { - if (this.length > num.length) return this.clone().iand(num); - return num.clone().iand(this); - }; - - BN.prototype.uand = function uand (num) { - if (this.length > num.length) return this.clone().iuand(num); - return num.clone().iuand(this); - }; - - // Xor `num` with `this` in-place - BN.prototype.iuxor = function iuxor (num) { - // a.length > b.length - var a; - var b; - if (this.length > num.length) { - a = this; - b = num; - } else { - a = num; - b = this; - } - - for (var i = 0; i < b.length; i++) { - this.words[i] = a.words[i] ^ b.words[i]; - } - - if (this !== a) { - for (; i < a.length; i++) { - this.words[i] = a.words[i]; - } - } - - this.length = a.length; - - return this.strip(); - }; - - BN.prototype.ixor = function ixor (num) { - assert((this.negative | num.negative) === 0); - return this.iuxor(num); - }; - - // Xor `num` with `this` - BN.prototype.xor = function xor (num) { - if (this.length > num.length) return this.clone().ixor(num); - return num.clone().ixor(this); - }; - - BN.prototype.uxor = function uxor (num) { - if (this.length > num.length) return this.clone().iuxor(num); - return num.clone().iuxor(this); - }; - - // Not ``this`` with ``width`` bitwidth - BN.prototype.inotn = function inotn (width) { - assert(typeof width === 'number' && width >= 0); - - var bytesNeeded = Math.ceil(width / 26) | 0; - var bitsLeft = width % 26; - - // Extend the buffer with leading zeroes - this._expand(bytesNeeded); - - if (bitsLeft > 0) { - bytesNeeded--; - } - - // Handle complete words - for (var i = 0; i < bytesNeeded; i++) { - this.words[i] = ~this.words[i] & 0x3ffffff; - } - - // Handle the residue - if (bitsLeft > 0) { - this.words[i] = ~this.words[i] & (0x3ffffff >> (26 - bitsLeft)); - } - - // And remove leading zeroes - return this.strip(); - }; - - BN.prototype.notn = function notn (width) { - return this.clone().inotn(width); - }; - - // Set `bit` of `this` - BN.prototype.setn = function setn (bit, val) { - assert(typeof bit === 'number' && bit >= 0); - - var off = (bit / 26) | 0; - var wbit = bit % 26; - - this._expand(off + 1); - - if (val) { - this.words[off] = this.words[off] | (1 << wbit); - } else { - this.words[off] = this.words[off] & ~(1 << wbit); - } - - return this.strip(); - }; - - // Add `num` to `this` in-place - BN.prototype.iadd = function iadd (num) { - var r; - - // negative + positive - if (this.negative !== 0 && num.negative === 0) { - this.negative = 0; - r = this.isub(num); - this.negative ^= 1; - return this._normSign(); - - // positive + negative - } else if (this.negative === 0 && num.negative !== 0) { - num.negative = 0; - r = this.isub(num); - num.negative = 1; - return r._normSign(); - } - - // a.length > b.length - var a, b; - if (this.length > num.length) { - a = this; - b = num; - } else { - a = num; - b = this; - } - - var carry = 0; - for (var i = 0; i < b.length; i++) { - r = (a.words[i] | 0) + (b.words[i] | 0) + carry; - this.words[i] = r & 0x3ffffff; - carry = r >>> 26; - } - for (; carry !== 0 && i < a.length; i++) { - r = (a.words[i] | 0) + carry; - this.words[i] = r & 0x3ffffff; - carry = r >>> 26; - } - - this.length = a.length; - if (carry !== 0) { - this.words[this.length] = carry; - this.length++; - // Copy the rest of the words - } else if (a !== this) { - for (; i < a.length; i++) { - this.words[i] = a.words[i]; - } - } - - return this; - }; - - // Add `num` to `this` - BN.prototype.add = function add (num) { - var res; - if (num.negative !== 0 && this.negative === 0) { - num.negative = 0; - res = this.sub(num); - num.negative ^= 1; - return res; - } else if (num.negative === 0 && this.negative !== 0) { - this.negative = 0; - res = num.sub(this); - this.negative = 1; - return res; - } - - if (this.length > num.length) return this.clone().iadd(num); - - return num.clone().iadd(this); - }; - - // Subtract `num` from `this` in-place - BN.prototype.isub = function isub (num) { - // this - (-num) = this + num - if (num.negative !== 0) { - num.negative = 0; - var r = this.iadd(num); - num.negative = 1; - return r._normSign(); - - // -this - num = -(this + num) - } else if (this.negative !== 0) { - this.negative = 0; - this.iadd(num); - this.negative = 1; - return this._normSign(); - } - - // At this point both numbers are positive - var cmp = this.cmp(num); - - // Optimization - zeroify - if (cmp === 0) { - this.negative = 0; - this.length = 1; - this.words[0] = 0; - return this; - } - - // a > b - var a, b; - if (cmp > 0) { - a = this; - b = num; - } else { - a = num; - b = this; - } - - var carry = 0; - for (var i = 0; i < b.length; i++) { - r = (a.words[i] | 0) - (b.words[i] | 0) + carry; - carry = r >> 26; - this.words[i] = r & 0x3ffffff; - } - for (; carry !== 0 && i < a.length; i++) { - r = (a.words[i] | 0) + carry; - carry = r >> 26; - this.words[i] = r & 0x3ffffff; - } - - // Copy rest of the words - if (carry === 0 && i < a.length && a !== this) { - for (; i < a.length; i++) { - this.words[i] = a.words[i]; - } - } - - this.length = Math.max(this.length, i); - - if (a !== this) { - this.negative = 1; - } - - return this.strip(); - }; - - // Subtract `num` from `this` - BN.prototype.sub = function sub (num) { - return this.clone().isub(num); - }; - - function smallMulTo (self, num, out) { - out.negative = num.negative ^ self.negative; - var len = (self.length + num.length) | 0; - out.length = len; - len = (len - 1) | 0; - - // Peel one iteration (compiler can't do it, because of code complexity) - var a = self.words[0] | 0; - var b = num.words[0] | 0; - var r = a * b; - - var lo = r & 0x3ffffff; - var carry = (r / 0x4000000) | 0; - out.words[0] = lo; - - for (var k = 1; k < len; k++) { - // Sum all words with the same `i + j = k` and accumulate `ncarry`, - // note that ncarry could be >= 0x3ffffff - var ncarry = carry >>> 26; - var rword = carry & 0x3ffffff; - var maxJ = Math.min(k, num.length - 1); - for (var j = Math.max(0, k - self.length + 1); j <= maxJ; j++) { - var i = (k - j) | 0; - a = self.words[i] | 0; - b = num.words[j] | 0; - r = a * b + rword; - ncarry += (r / 0x4000000) | 0; - rword = r & 0x3ffffff; - } - out.words[k] = rword | 0; - carry = ncarry | 0; - } - if (carry !== 0) { - out.words[k] = carry | 0; - } else { - out.length--; - } - - return out.strip(); - } - - // TODO(indutny): it may be reasonable to omit it for users who don't need - // to work with 256-bit numbers, otherwise it gives 20% improvement for 256-bit - // multiplication (like elliptic secp256k1). - var comb10MulTo = function comb10MulTo (self, num, out) { - var a = self.words; - var b = num.words; - var o = out.words; - var c = 0; - var lo; - var mid; - var hi; - var a0 = a[0] | 0; - var al0 = a0 & 0x1fff; - var ah0 = a0 >>> 13; - var a1 = a[1] | 0; - var al1 = a1 & 0x1fff; - var ah1 = a1 >>> 13; - var a2 = a[2] | 0; - var al2 = a2 & 0x1fff; - var ah2 = a2 >>> 13; - var a3 = a[3] | 0; - var al3 = a3 & 0x1fff; - var ah3 = a3 >>> 13; - var a4 = a[4] | 0; - var al4 = a4 & 0x1fff; - var ah4 = a4 >>> 13; - var a5 = a[5] | 0; - var al5 = a5 & 0x1fff; - var ah5 = a5 >>> 13; - var a6 = a[6] | 0; - var al6 = a6 & 0x1fff; - var ah6 = a6 >>> 13; - var a7 = a[7] | 0; - var al7 = a7 & 0x1fff; - var ah7 = a7 >>> 13; - var a8 = a[8] | 0; - var al8 = a8 & 0x1fff; - var ah8 = a8 >>> 13; - var a9 = a[9] | 0; - var al9 = a9 & 0x1fff; - var ah9 = a9 >>> 13; - var b0 = b[0] | 0; - var bl0 = b0 & 0x1fff; - var bh0 = b0 >>> 13; - var b1 = b[1] | 0; - var bl1 = b1 & 0x1fff; - var bh1 = b1 >>> 13; - var b2 = b[2] | 0; - var bl2 = b2 & 0x1fff; - var bh2 = b2 >>> 13; - var b3 = b[3] | 0; - var bl3 = b3 & 0x1fff; - var bh3 = b3 >>> 13; - var b4 = b[4] | 0; - var bl4 = b4 & 0x1fff; - var bh4 = b4 >>> 13; - var b5 = b[5] | 0; - var bl5 = b5 & 0x1fff; - var bh5 = b5 >>> 13; - var b6 = b[6] | 0; - var bl6 = b6 & 0x1fff; - var bh6 = b6 >>> 13; - var b7 = b[7] | 0; - var bl7 = b7 & 0x1fff; - var bh7 = b7 >>> 13; - var b8 = b[8] | 0; - var bl8 = b8 & 0x1fff; - var bh8 = b8 >>> 13; - var b9 = b[9] | 0; - var bl9 = b9 & 0x1fff; - var bh9 = b9 >>> 13; - - out.negative = self.negative ^ num.negative; - out.length = 19; - /* k = 0 */ - lo = Math.imul(al0, bl0); - mid = Math.imul(al0, bh0); - mid = (mid + Math.imul(ah0, bl0)) | 0; - hi = Math.imul(ah0, bh0); - var w0 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w0 >>> 26)) | 0; - w0 &= 0x3ffffff; - /* k = 1 */ - lo = Math.imul(al1, bl0); - mid = Math.imul(al1, bh0); - mid = (mid + Math.imul(ah1, bl0)) | 0; - hi = Math.imul(ah1, bh0); - lo = (lo + Math.imul(al0, bl1)) | 0; - mid = (mid + Math.imul(al0, bh1)) | 0; - mid = (mid + Math.imul(ah0, bl1)) | 0; - hi = (hi + Math.imul(ah0, bh1)) | 0; - var w1 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w1 >>> 26)) | 0; - w1 &= 0x3ffffff; - /* k = 2 */ - lo = Math.imul(al2, bl0); - mid = Math.imul(al2, bh0); - mid = (mid + Math.imul(ah2, bl0)) | 0; - hi = Math.imul(ah2, bh0); - lo = (lo + Math.imul(al1, bl1)) | 0; - mid = (mid + Math.imul(al1, bh1)) | 0; - mid = (mid + Math.imul(ah1, bl1)) | 0; - hi = (hi + Math.imul(ah1, bh1)) | 0; - lo = (lo + Math.imul(al0, bl2)) | 0; - mid = (mid + Math.imul(al0, bh2)) | 0; - mid = (mid + Math.imul(ah0, bl2)) | 0; - hi = (hi + Math.imul(ah0, bh2)) | 0; - var w2 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w2 >>> 26)) | 0; - w2 &= 0x3ffffff; - /* k = 3 */ - lo = Math.imul(al3, bl0); - mid = Math.imul(al3, bh0); - mid = (mid + Math.imul(ah3, bl0)) | 0; - hi = Math.imul(ah3, bh0); - lo = (lo + Math.imul(al2, bl1)) | 0; - mid = (mid + Math.imul(al2, bh1)) | 0; - mid = (mid + Math.imul(ah2, bl1)) | 0; - hi = (hi + Math.imul(ah2, bh1)) | 0; - lo = (lo + Math.imul(al1, bl2)) | 0; - mid = (mid + Math.imul(al1, bh2)) | 0; - mid = (mid + Math.imul(ah1, bl2)) | 0; - hi = (hi + Math.imul(ah1, bh2)) | 0; - lo = (lo + Math.imul(al0, bl3)) | 0; - mid = (mid + Math.imul(al0, bh3)) | 0; - mid = (mid + Math.imul(ah0, bl3)) | 0; - hi = (hi + Math.imul(ah0, bh3)) | 0; - var w3 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w3 >>> 26)) | 0; - w3 &= 0x3ffffff; - /* k = 4 */ - lo = Math.imul(al4, bl0); - mid = Math.imul(al4, bh0); - mid = (mid + Math.imul(ah4, bl0)) | 0; - hi = Math.imul(ah4, bh0); - lo = (lo + Math.imul(al3, bl1)) | 0; - mid = (mid + Math.imul(al3, bh1)) | 0; - mid = (mid + Math.imul(ah3, bl1)) | 0; - hi = (hi + Math.imul(ah3, bh1)) | 0; - lo = (lo + Math.imul(al2, bl2)) | 0; - mid = (mid + Math.imul(al2, bh2)) | 0; - mid = (mid + Math.imul(ah2, bl2)) | 0; - hi = (hi + Math.imul(ah2, bh2)) | 0; - lo = (lo + Math.imul(al1, bl3)) | 0; - mid = (mid + Math.imul(al1, bh3)) | 0; - mid = (mid + Math.imul(ah1, bl3)) | 0; - hi = (hi + Math.imul(ah1, bh3)) | 0; - lo = (lo + Math.imul(al0, bl4)) | 0; - mid = (mid + Math.imul(al0, bh4)) | 0; - mid = (mid + Math.imul(ah0, bl4)) | 0; - hi = (hi + Math.imul(ah0, bh4)) | 0; - var w4 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w4 >>> 26)) | 0; - w4 &= 0x3ffffff; - /* k = 5 */ - lo = Math.imul(al5, bl0); - mid = Math.imul(al5, bh0); - mid = (mid + Math.imul(ah5, bl0)) | 0; - hi = Math.imul(ah5, bh0); - lo = (lo + Math.imul(al4, bl1)) | 0; - mid = (mid + Math.imul(al4, bh1)) | 0; - mid = (mid + Math.imul(ah4, bl1)) | 0; - hi = (hi + Math.imul(ah4, bh1)) | 0; - lo = (lo + Math.imul(al3, bl2)) | 0; - mid = (mid + Math.imul(al3, bh2)) | 0; - mid = (mid + Math.imul(ah3, bl2)) | 0; - hi = (hi + Math.imul(ah3, bh2)) | 0; - lo = (lo + Math.imul(al2, bl3)) | 0; - mid = (mid + Math.imul(al2, bh3)) | 0; - mid = (mid + Math.imul(ah2, bl3)) | 0; - hi = (hi + Math.imul(ah2, bh3)) | 0; - lo = (lo + Math.imul(al1, bl4)) | 0; - mid = (mid + Math.imul(al1, bh4)) | 0; - mid = (mid + Math.imul(ah1, bl4)) | 0; - hi = (hi + Math.imul(ah1, bh4)) | 0; - lo = (lo + Math.imul(al0, bl5)) | 0; - mid = (mid + Math.imul(al0, bh5)) | 0; - mid = (mid + Math.imul(ah0, bl5)) | 0; - hi = (hi + Math.imul(ah0, bh5)) | 0; - var w5 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w5 >>> 26)) | 0; - w5 &= 0x3ffffff; - /* k = 6 */ - lo = Math.imul(al6, bl0); - mid = Math.imul(al6, bh0); - mid = (mid + Math.imul(ah6, bl0)) | 0; - hi = Math.imul(ah6, bh0); - lo = (lo + Math.imul(al5, bl1)) | 0; - mid = (mid + Math.imul(al5, bh1)) | 0; - mid = (mid + Math.imul(ah5, bl1)) | 0; - hi = (hi + Math.imul(ah5, bh1)) | 0; - lo = (lo + Math.imul(al4, bl2)) | 0; - mid = (mid + Math.imul(al4, bh2)) | 0; - mid = (mid + Math.imul(ah4, bl2)) | 0; - hi = (hi + Math.imul(ah4, bh2)) | 0; - lo = (lo + Math.imul(al3, bl3)) | 0; - mid = (mid + Math.imul(al3, bh3)) | 0; - mid = (mid + Math.imul(ah3, bl3)) | 0; - hi = (hi + Math.imul(ah3, bh3)) | 0; - lo = (lo + Math.imul(al2, bl4)) | 0; - mid = (mid + Math.imul(al2, bh4)) | 0; - mid = (mid + Math.imul(ah2, bl4)) | 0; - hi = (hi + Math.imul(ah2, bh4)) | 0; - lo = (lo + Math.imul(al1, bl5)) | 0; - mid = (mid + Math.imul(al1, bh5)) | 0; - mid = (mid + Math.imul(ah1, bl5)) | 0; - hi = (hi + Math.imul(ah1, bh5)) | 0; - lo = (lo + Math.imul(al0, bl6)) | 0; - mid = (mid + Math.imul(al0, bh6)) | 0; - mid = (mid + Math.imul(ah0, bl6)) | 0; - hi = (hi + Math.imul(ah0, bh6)) | 0; - var w6 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w6 >>> 26)) | 0; - w6 &= 0x3ffffff; - /* k = 7 */ - lo = Math.imul(al7, bl0); - mid = Math.imul(al7, bh0); - mid = (mid + Math.imul(ah7, bl0)) | 0; - hi = Math.imul(ah7, bh0); - lo = (lo + Math.imul(al6, bl1)) | 0; - mid = (mid + Math.imul(al6, bh1)) | 0; - mid = (mid + Math.imul(ah6, bl1)) | 0; - hi = (hi + Math.imul(ah6, bh1)) | 0; - lo = (lo + Math.imul(al5, bl2)) | 0; - mid = (mid + Math.imul(al5, bh2)) | 0; - mid = (mid + Math.imul(ah5, bl2)) | 0; - hi = (hi + Math.imul(ah5, bh2)) | 0; - lo = (lo + Math.imul(al4, bl3)) | 0; - mid = (mid + Math.imul(al4, bh3)) | 0; - mid = (mid + Math.imul(ah4, bl3)) | 0; - hi = (hi + Math.imul(ah4, bh3)) | 0; - lo = (lo + Math.imul(al3, bl4)) | 0; - mid = (mid + Math.imul(al3, bh4)) | 0; - mid = (mid + Math.imul(ah3, bl4)) | 0; - hi = (hi + Math.imul(ah3, bh4)) | 0; - lo = (lo + Math.imul(al2, bl5)) | 0; - mid = (mid + Math.imul(al2, bh5)) | 0; - mid = (mid + Math.imul(ah2, bl5)) | 0; - hi = (hi + Math.imul(ah2, bh5)) | 0; - lo = (lo + Math.imul(al1, bl6)) | 0; - mid = (mid + Math.imul(al1, bh6)) | 0; - mid = (mid + Math.imul(ah1, bl6)) | 0; - hi = (hi + Math.imul(ah1, bh6)) | 0; - lo = (lo + Math.imul(al0, bl7)) | 0; - mid = (mid + Math.imul(al0, bh7)) | 0; - mid = (mid + Math.imul(ah0, bl7)) | 0; - hi = (hi + Math.imul(ah0, bh7)) | 0; - var w7 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w7 >>> 26)) | 0; - w7 &= 0x3ffffff; - /* k = 8 */ - lo = Math.imul(al8, bl0); - mid = Math.imul(al8, bh0); - mid = (mid + Math.imul(ah8, bl0)) | 0; - hi = Math.imul(ah8, bh0); - lo = (lo + Math.imul(al7, bl1)) | 0; - mid = (mid + Math.imul(al7, bh1)) | 0; - mid = (mid + Math.imul(ah7, bl1)) | 0; - hi = (hi + Math.imul(ah7, bh1)) | 0; - lo = (lo + Math.imul(al6, bl2)) | 0; - mid = (mid + Math.imul(al6, bh2)) | 0; - mid = (mid + Math.imul(ah6, bl2)) | 0; - hi = (hi + Math.imul(ah6, bh2)) | 0; - lo = (lo + Math.imul(al5, bl3)) | 0; - mid = (mid + Math.imul(al5, bh3)) | 0; - mid = (mid + Math.imul(ah5, bl3)) | 0; - hi = (hi + Math.imul(ah5, bh3)) | 0; - lo = (lo + Math.imul(al4, bl4)) | 0; - mid = (mid + Math.imul(al4, bh4)) | 0; - mid = (mid + Math.imul(ah4, bl4)) | 0; - hi = (hi + Math.imul(ah4, bh4)) | 0; - lo = (lo + Math.imul(al3, bl5)) | 0; - mid = (mid + Math.imul(al3, bh5)) | 0; - mid = (mid + Math.imul(ah3, bl5)) | 0; - hi = (hi + Math.imul(ah3, bh5)) | 0; - lo = (lo + Math.imul(al2, bl6)) | 0; - mid = (mid + Math.imul(al2, bh6)) | 0; - mid = (mid + Math.imul(ah2, bl6)) | 0; - hi = (hi + Math.imul(ah2, bh6)) | 0; - lo = (lo + Math.imul(al1, bl7)) | 0; - mid = (mid + Math.imul(al1, bh7)) | 0; - mid = (mid + Math.imul(ah1, bl7)) | 0; - hi = (hi + Math.imul(ah1, bh7)) | 0; - lo = (lo + Math.imul(al0, bl8)) | 0; - mid = (mid + Math.imul(al0, bh8)) | 0; - mid = (mid + Math.imul(ah0, bl8)) | 0; - hi = (hi + Math.imul(ah0, bh8)) | 0; - var w8 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w8 >>> 26)) | 0; - w8 &= 0x3ffffff; - /* k = 9 */ - lo = Math.imul(al9, bl0); - mid = Math.imul(al9, bh0); - mid = (mid + Math.imul(ah9, bl0)) | 0; - hi = Math.imul(ah9, bh0); - lo = (lo + Math.imul(al8, bl1)) | 0; - mid = (mid + Math.imul(al8, bh1)) | 0; - mid = (mid + Math.imul(ah8, bl1)) | 0; - hi = (hi + Math.imul(ah8, bh1)) | 0; - lo = (lo + Math.imul(al7, bl2)) | 0; - mid = (mid + Math.imul(al7, bh2)) | 0; - mid = (mid + Math.imul(ah7, bl2)) | 0; - hi = (hi + Math.imul(ah7, bh2)) | 0; - lo = (lo + Math.imul(al6, bl3)) | 0; - mid = (mid + Math.imul(al6, bh3)) | 0; - mid = (mid + Math.imul(ah6, bl3)) | 0; - hi = (hi + Math.imul(ah6, bh3)) | 0; - lo = (lo + Math.imul(al5, bl4)) | 0; - mid = (mid + Math.imul(al5, bh4)) | 0; - mid = (mid + Math.imul(ah5, bl4)) | 0; - hi = (hi + Math.imul(ah5, bh4)) | 0; - lo = (lo + Math.imul(al4, bl5)) | 0; - mid = (mid + Math.imul(al4, bh5)) | 0; - mid = (mid + Math.imul(ah4, bl5)) | 0; - hi = (hi + Math.imul(ah4, bh5)) | 0; - lo = (lo + Math.imul(al3, bl6)) | 0; - mid = (mid + Math.imul(al3, bh6)) | 0; - mid = (mid + Math.imul(ah3, bl6)) | 0; - hi = (hi + Math.imul(ah3, bh6)) | 0; - lo = (lo + Math.imul(al2, bl7)) | 0; - mid = (mid + Math.imul(al2, bh7)) | 0; - mid = (mid + Math.imul(ah2, bl7)) | 0; - hi = (hi + Math.imul(ah2, bh7)) | 0; - lo = (lo + Math.imul(al1, bl8)) | 0; - mid = (mid + Math.imul(al1, bh8)) | 0; - mid = (mid + Math.imul(ah1, bl8)) | 0; - hi = (hi + Math.imul(ah1, bh8)) | 0; - lo = (lo + Math.imul(al0, bl9)) | 0; - mid = (mid + Math.imul(al0, bh9)) | 0; - mid = (mid + Math.imul(ah0, bl9)) | 0; - hi = (hi + Math.imul(ah0, bh9)) | 0; - var w9 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w9 >>> 26)) | 0; - w9 &= 0x3ffffff; - /* k = 10 */ - lo = Math.imul(al9, bl1); - mid = Math.imul(al9, bh1); - mid = (mid + Math.imul(ah9, bl1)) | 0; - hi = Math.imul(ah9, bh1); - lo = (lo + Math.imul(al8, bl2)) | 0; - mid = (mid + Math.imul(al8, bh2)) | 0; - mid = (mid + Math.imul(ah8, bl2)) | 0; - hi = (hi + Math.imul(ah8, bh2)) | 0; - lo = (lo + Math.imul(al7, bl3)) | 0; - mid = (mid + Math.imul(al7, bh3)) | 0; - mid = (mid + Math.imul(ah7, bl3)) | 0; - hi = (hi + Math.imul(ah7, bh3)) | 0; - lo = (lo + Math.imul(al6, bl4)) | 0; - mid = (mid + Math.imul(al6, bh4)) | 0; - mid = (mid + Math.imul(ah6, bl4)) | 0; - hi = (hi + Math.imul(ah6, bh4)) | 0; - lo = (lo + Math.imul(al5, bl5)) | 0; - mid = (mid + Math.imul(al5, bh5)) | 0; - mid = (mid + Math.imul(ah5, bl5)) | 0; - hi = (hi + Math.imul(ah5, bh5)) | 0; - lo = (lo + Math.imul(al4, bl6)) | 0; - mid = (mid + Math.imul(al4, bh6)) | 0; - mid = (mid + Math.imul(ah4, bl6)) | 0; - hi = (hi + Math.imul(ah4, bh6)) | 0; - lo = (lo + Math.imul(al3, bl7)) | 0; - mid = (mid + Math.imul(al3, bh7)) | 0; - mid = (mid + Math.imul(ah3, bl7)) | 0; - hi = (hi + Math.imul(ah3, bh7)) | 0; - lo = (lo + Math.imul(al2, bl8)) | 0; - mid = (mid + Math.imul(al2, bh8)) | 0; - mid = (mid + Math.imul(ah2, bl8)) | 0; - hi = (hi + Math.imul(ah2, bh8)) | 0; - lo = (lo + Math.imul(al1, bl9)) | 0; - mid = (mid + Math.imul(al1, bh9)) | 0; - mid = (mid + Math.imul(ah1, bl9)) | 0; - hi = (hi + Math.imul(ah1, bh9)) | 0; - var w10 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w10 >>> 26)) | 0; - w10 &= 0x3ffffff; - /* k = 11 */ - lo = Math.imul(al9, bl2); - mid = Math.imul(al9, bh2); - mid = (mid + Math.imul(ah9, bl2)) | 0; - hi = Math.imul(ah9, bh2); - lo = (lo + Math.imul(al8, bl3)) | 0; - mid = (mid + Math.imul(al8, bh3)) | 0; - mid = (mid + Math.imul(ah8, bl3)) | 0; - hi = (hi + Math.imul(ah8, bh3)) | 0; - lo = (lo + Math.imul(al7, bl4)) | 0; - mid = (mid + Math.imul(al7, bh4)) | 0; - mid = (mid + Math.imul(ah7, bl4)) | 0; - hi = (hi + Math.imul(ah7, bh4)) | 0; - lo = (lo + Math.imul(al6, bl5)) | 0; - mid = (mid + Math.imul(al6, bh5)) | 0; - mid = (mid + Math.imul(ah6, bl5)) | 0; - hi = (hi + Math.imul(ah6, bh5)) | 0; - lo = (lo + Math.imul(al5, bl6)) | 0; - mid = (mid + Math.imul(al5, bh6)) | 0; - mid = (mid + Math.imul(ah5, bl6)) | 0; - hi = (hi + Math.imul(ah5, bh6)) | 0; - lo = (lo + Math.imul(al4, bl7)) | 0; - mid = (mid + Math.imul(al4, bh7)) | 0; - mid = (mid + Math.imul(ah4, bl7)) | 0; - hi = (hi + Math.imul(ah4, bh7)) | 0; - lo = (lo + Math.imul(al3, bl8)) | 0; - mid = (mid + Math.imul(al3, bh8)) | 0; - mid = (mid + Math.imul(ah3, bl8)) | 0; - hi = (hi + Math.imul(ah3, bh8)) | 0; - lo = (lo + Math.imul(al2, bl9)) | 0; - mid = (mid + Math.imul(al2, bh9)) | 0; - mid = (mid + Math.imul(ah2, bl9)) | 0; - hi = (hi + Math.imul(ah2, bh9)) | 0; - var w11 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w11 >>> 26)) | 0; - w11 &= 0x3ffffff; - /* k = 12 */ - lo = Math.imul(al9, bl3); - mid = Math.imul(al9, bh3); - mid = (mid + Math.imul(ah9, bl3)) | 0; - hi = Math.imul(ah9, bh3); - lo = (lo + Math.imul(al8, bl4)) | 0; - mid = (mid + Math.imul(al8, bh4)) | 0; - mid = (mid + Math.imul(ah8, bl4)) | 0; - hi = (hi + Math.imul(ah8, bh4)) | 0; - lo = (lo + Math.imul(al7, bl5)) | 0; - mid = (mid + Math.imul(al7, bh5)) | 0; - mid = (mid + Math.imul(ah7, bl5)) | 0; - hi = (hi + Math.imul(ah7, bh5)) | 0; - lo = (lo + Math.imul(al6, bl6)) | 0; - mid = (mid + Math.imul(al6, bh6)) | 0; - mid = (mid + Math.imul(ah6, bl6)) | 0; - hi = (hi + Math.imul(ah6, bh6)) | 0; - lo = (lo + Math.imul(al5, bl7)) | 0; - mid = (mid + Math.imul(al5, bh7)) | 0; - mid = (mid + Math.imul(ah5, bl7)) | 0; - hi = (hi + Math.imul(ah5, bh7)) | 0; - lo = (lo + Math.imul(al4, bl8)) | 0; - mid = (mid + Math.imul(al4, bh8)) | 0; - mid = (mid + Math.imul(ah4, bl8)) | 0; - hi = (hi + Math.imul(ah4, bh8)) | 0; - lo = (lo + Math.imul(al3, bl9)) | 0; - mid = (mid + Math.imul(al3, bh9)) | 0; - mid = (mid + Math.imul(ah3, bl9)) | 0; - hi = (hi + Math.imul(ah3, bh9)) | 0; - var w12 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w12 >>> 26)) | 0; - w12 &= 0x3ffffff; - /* k = 13 */ - lo = Math.imul(al9, bl4); - mid = Math.imul(al9, bh4); - mid = (mid + Math.imul(ah9, bl4)) | 0; - hi = Math.imul(ah9, bh4); - lo = (lo + Math.imul(al8, bl5)) | 0; - mid = (mid + Math.imul(al8, bh5)) | 0; - mid = (mid + Math.imul(ah8, bl5)) | 0; - hi = (hi + Math.imul(ah8, bh5)) | 0; - lo = (lo + Math.imul(al7, bl6)) | 0; - mid = (mid + Math.imul(al7, bh6)) | 0; - mid = (mid + Math.imul(ah7, bl6)) | 0; - hi = (hi + Math.imul(ah7, bh6)) | 0; - lo = (lo + Math.imul(al6, bl7)) | 0; - mid = (mid + Math.imul(al6, bh7)) | 0; - mid = (mid + Math.imul(ah6, bl7)) | 0; - hi = (hi + Math.imul(ah6, bh7)) | 0; - lo = (lo + Math.imul(al5, bl8)) | 0; - mid = (mid + Math.imul(al5, bh8)) | 0; - mid = (mid + Math.imul(ah5, bl8)) | 0; - hi = (hi + Math.imul(ah5, bh8)) | 0; - lo = (lo + Math.imul(al4, bl9)) | 0; - mid = (mid + Math.imul(al4, bh9)) | 0; - mid = (mid + Math.imul(ah4, bl9)) | 0; - hi = (hi + Math.imul(ah4, bh9)) | 0; - var w13 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w13 >>> 26)) | 0; - w13 &= 0x3ffffff; - /* k = 14 */ - lo = Math.imul(al9, bl5); - mid = Math.imul(al9, bh5); - mid = (mid + Math.imul(ah9, bl5)) | 0; - hi = Math.imul(ah9, bh5); - lo = (lo + Math.imul(al8, bl6)) | 0; - mid = (mid + Math.imul(al8, bh6)) | 0; - mid = (mid + Math.imul(ah8, bl6)) | 0; - hi = (hi + Math.imul(ah8, bh6)) | 0; - lo = (lo + Math.imul(al7, bl7)) | 0; - mid = (mid + Math.imul(al7, bh7)) | 0; - mid = (mid + Math.imul(ah7, bl7)) | 0; - hi = (hi + Math.imul(ah7, bh7)) | 0; - lo = (lo + Math.imul(al6, bl8)) | 0; - mid = (mid + Math.imul(al6, bh8)) | 0; - mid = (mid + Math.imul(ah6, bl8)) | 0; - hi = (hi + Math.imul(ah6, bh8)) | 0; - lo = (lo + Math.imul(al5, bl9)) | 0; - mid = (mid + Math.imul(al5, bh9)) | 0; - mid = (mid + Math.imul(ah5, bl9)) | 0; - hi = (hi + Math.imul(ah5, bh9)) | 0; - var w14 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w14 >>> 26)) | 0; - w14 &= 0x3ffffff; - /* k = 15 */ - lo = Math.imul(al9, bl6); - mid = Math.imul(al9, bh6); - mid = (mid + Math.imul(ah9, bl6)) | 0; - hi = Math.imul(ah9, bh6); - lo = (lo + Math.imul(al8, bl7)) | 0; - mid = (mid + Math.imul(al8, bh7)) | 0; - mid = (mid + Math.imul(ah8, bl7)) | 0; - hi = (hi + Math.imul(ah8, bh7)) | 0; - lo = (lo + Math.imul(al7, bl8)) | 0; - mid = (mid + Math.imul(al7, bh8)) | 0; - mid = (mid + Math.imul(ah7, bl8)) | 0; - hi = (hi + Math.imul(ah7, bh8)) | 0; - lo = (lo + Math.imul(al6, bl9)) | 0; - mid = (mid + Math.imul(al6, bh9)) | 0; - mid = (mid + Math.imul(ah6, bl9)) | 0; - hi = (hi + Math.imul(ah6, bh9)) | 0; - var w15 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w15 >>> 26)) | 0; - w15 &= 0x3ffffff; - /* k = 16 */ - lo = Math.imul(al9, bl7); - mid = Math.imul(al9, bh7); - mid = (mid + Math.imul(ah9, bl7)) | 0; - hi = Math.imul(ah9, bh7); - lo = (lo + Math.imul(al8, bl8)) | 0; - mid = (mid + Math.imul(al8, bh8)) | 0; - mid = (mid + Math.imul(ah8, bl8)) | 0; - hi = (hi + Math.imul(ah8, bh8)) | 0; - lo = (lo + Math.imul(al7, bl9)) | 0; - mid = (mid + Math.imul(al7, bh9)) | 0; - mid = (mid + Math.imul(ah7, bl9)) | 0; - hi = (hi + Math.imul(ah7, bh9)) | 0; - var w16 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w16 >>> 26)) | 0; - w16 &= 0x3ffffff; - /* k = 17 */ - lo = Math.imul(al9, bl8); - mid = Math.imul(al9, bh8); - mid = (mid + Math.imul(ah9, bl8)) | 0; - hi = Math.imul(ah9, bh8); - lo = (lo + Math.imul(al8, bl9)) | 0; - mid = (mid + Math.imul(al8, bh9)) | 0; - mid = (mid + Math.imul(ah8, bl9)) | 0; - hi = (hi + Math.imul(ah8, bh9)) | 0; - var w17 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w17 >>> 26)) | 0; - w17 &= 0x3ffffff; - /* k = 18 */ - lo = Math.imul(al9, bl9); - mid = Math.imul(al9, bh9); - mid = (mid + Math.imul(ah9, bl9)) | 0; - hi = Math.imul(ah9, bh9); - var w18 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w18 >>> 26)) | 0; - w18 &= 0x3ffffff; - o[0] = w0; - o[1] = w1; - o[2] = w2; - o[3] = w3; - o[4] = w4; - o[5] = w5; - o[6] = w6; - o[7] = w7; - o[8] = w8; - o[9] = w9; - o[10] = w10; - o[11] = w11; - o[12] = w12; - o[13] = w13; - o[14] = w14; - o[15] = w15; - o[16] = w16; - o[17] = w17; - o[18] = w18; - if (c !== 0) { - o[19] = c; - out.length++; - } - return out; - }; - - // Polyfill comb - if (!Math.imul) { - comb10MulTo = smallMulTo; - } - - function bigMulTo (self, num, out) { - out.negative = num.negative ^ self.negative; - out.length = self.length + num.length; - - var carry = 0; - var hncarry = 0; - for (var k = 0; k < out.length - 1; k++) { - // Sum all words with the same `i + j = k` and accumulate `ncarry`, - // note that ncarry could be >= 0x3ffffff - var ncarry = hncarry; - hncarry = 0; - var rword = carry & 0x3ffffff; - var maxJ = Math.min(k, num.length - 1); - for (var j = Math.max(0, k - self.length + 1); j <= maxJ; j++) { - var i = k - j; - var a = self.words[i] | 0; - var b = num.words[j] | 0; - var r = a * b; - - var lo = r & 0x3ffffff; - ncarry = (ncarry + ((r / 0x4000000) | 0)) | 0; - lo = (lo + rword) | 0; - rword = lo & 0x3ffffff; - ncarry = (ncarry + (lo >>> 26)) | 0; - - hncarry += ncarry >>> 26; - ncarry &= 0x3ffffff; - } - out.words[k] = rword; - carry = ncarry; - ncarry = hncarry; - } - if (carry !== 0) { - out.words[k] = carry; - } else { - out.length--; - } - - return out.strip(); - } - - function jumboMulTo (self, num, out) { - var fftm = new FFTM(); - return fftm.mulp(self, num, out); - } - - BN.prototype.mulTo = function mulTo (num, out) { - var res; - var len = this.length + num.length; - if (this.length === 10 && num.length === 10) { - res = comb10MulTo(this, num, out); - } else if (len < 63) { - res = smallMulTo(this, num, out); - } else if (len < 1024) { - res = bigMulTo(this, num, out); - } else { - res = jumboMulTo(this, num, out); - } - - return res; - }; - - // Cooley-Tukey algorithm for FFT - // slightly revisited to rely on looping instead of recursion - - function FFTM (x, y) { - this.x = x; - this.y = y; - } - - FFTM.prototype.makeRBT = function makeRBT (N) { - var t = new Array(N); - var l = BN.prototype._countBits(N) - 1; - for (var i = 0; i < N; i++) { - t[i] = this.revBin(i, l, N); - } - - return t; - }; - - // Returns binary-reversed representation of `x` - FFTM.prototype.revBin = function revBin (x, l, N) { - if (x === 0 || x === N - 1) return x; - - var rb = 0; - for (var i = 0; i < l; i++) { - rb |= (x & 1) << (l - i - 1); - x >>= 1; - } - - return rb; - }; - - // Performs "tweedling" phase, therefore 'emulating' - // behaviour of the recursive algorithm - FFTM.prototype.permute = function permute (rbt, rws, iws, rtws, itws, N) { - for (var i = 0; i < N; i++) { - rtws[i] = rws[rbt[i]]; - itws[i] = iws[rbt[i]]; - } - }; - - FFTM.prototype.transform = function transform (rws, iws, rtws, itws, N, rbt) { - this.permute(rbt, rws, iws, rtws, itws, N); - - for (var s = 1; s < N; s <<= 1) { - var l = s << 1; - - var rtwdf = Math.cos(2 * Math.PI / l); - var itwdf = Math.sin(2 * Math.PI / l); - - for (var p = 0; p < N; p += l) { - var rtwdf_ = rtwdf; - var itwdf_ = itwdf; - - for (var j = 0; j < s; j++) { - var re = rtws[p + j]; - var ie = itws[p + j]; - - var ro = rtws[p + j + s]; - var io = itws[p + j + s]; - - var rx = rtwdf_ * ro - itwdf_ * io; - - io = rtwdf_ * io + itwdf_ * ro; - ro = rx; - - rtws[p + j] = re + ro; - itws[p + j] = ie + io; - - rtws[p + j + s] = re - ro; - itws[p + j + s] = ie - io; - - /* jshint maxdepth : false */ - if (j !== l) { - rx = rtwdf * rtwdf_ - itwdf * itwdf_; - - itwdf_ = rtwdf * itwdf_ + itwdf * rtwdf_; - rtwdf_ = rx; - } - } - } - } - }; - - FFTM.prototype.guessLen13b = function guessLen13b (n, m) { - var N = Math.max(m, n) | 1; - var odd = N & 1; - var i = 0; - for (N = N / 2 | 0; N; N = N >>> 1) { - i++; - } - - return 1 << i + 1 + odd; - }; - - FFTM.prototype.conjugate = function conjugate (rws, iws, N) { - if (N <= 1) return; - - for (var i = 0; i < N / 2; i++) { - var t = rws[i]; - - rws[i] = rws[N - i - 1]; - rws[N - i - 1] = t; - - t = iws[i]; - - iws[i] = -iws[N - i - 1]; - iws[N - i - 1] = -t; - } - }; - - FFTM.prototype.normalize13b = function normalize13b (ws, N) { - var carry = 0; - for (var i = 0; i < N / 2; i++) { - var w = Math.round(ws[2 * i + 1] / N) * 0x2000 + - Math.round(ws[2 * i] / N) + - carry; - - ws[i] = w & 0x3ffffff; - - if (w < 0x4000000) { - carry = 0; - } else { - carry = w / 0x4000000 | 0; - } - } - - return ws; - }; - - FFTM.prototype.convert13b = function convert13b (ws, len, rws, N) { - var carry = 0; - for (var i = 0; i < len; i++) { - carry = carry + (ws[i] | 0); - - rws[2 * i] = carry & 0x1fff; carry = carry >>> 13; - rws[2 * i + 1] = carry & 0x1fff; carry = carry >>> 13; - } - - // Pad with zeroes - for (i = 2 * len; i < N; ++i) { - rws[i] = 0; - } - - assert(carry === 0); - assert((carry & ~0x1fff) === 0); - }; - - FFTM.prototype.stub = function stub (N) { - var ph = new Array(N); - for (var i = 0; i < N; i++) { - ph[i] = 0; - } - - return ph; - }; - - FFTM.prototype.mulp = function mulp (x, y, out) { - var N = 2 * this.guessLen13b(x.length, y.length); - - var rbt = this.makeRBT(N); - - var _ = this.stub(N); - - var rws = new Array(N); - var rwst = new Array(N); - var iwst = new Array(N); - - var nrws = new Array(N); - var nrwst = new Array(N); - var niwst = new Array(N); - - var rmws = out.words; - rmws.length = N; - - this.convert13b(x.words, x.length, rws, N); - this.convert13b(y.words, y.length, nrws, N); - - this.transform(rws, _, rwst, iwst, N, rbt); - this.transform(nrws, _, nrwst, niwst, N, rbt); - - for (var i = 0; i < N; i++) { - var rx = rwst[i] * nrwst[i] - iwst[i] * niwst[i]; - iwst[i] = rwst[i] * niwst[i] + iwst[i] * nrwst[i]; - rwst[i] = rx; - } - - this.conjugate(rwst, iwst, N); - this.transform(rwst, iwst, rmws, _, N, rbt); - this.conjugate(rmws, _, N); - this.normalize13b(rmws, N); - - out.negative = x.negative ^ y.negative; - out.length = x.length + y.length; - return out.strip(); - }; - - // Multiply `this` by `num` - BN.prototype.mul = function mul (num) { - var out = new BN(null); - out.words = new Array(this.length + num.length); - return this.mulTo(num, out); - }; - - // Multiply employing FFT - BN.prototype.mulf = function mulf (num) { - var out = new BN(null); - out.words = new Array(this.length + num.length); - return jumboMulTo(this, num, out); - }; - - // In-place Multiplication - BN.prototype.imul = function imul (num) { - return this.clone().mulTo(num, this); - }; - - BN.prototype.imuln = function imuln (num) { - assert(typeof num === 'number'); - assert(num < 0x4000000); - - // Carry - var carry = 0; - for (var i = 0; i < this.length; i++) { - var w = (this.words[i] | 0) * num; - var lo = (w & 0x3ffffff) + (carry & 0x3ffffff); - carry >>= 26; - carry += (w / 0x4000000) | 0; - // NOTE: lo is 27bit maximum - carry += lo >>> 26; - this.words[i] = lo & 0x3ffffff; - } - - if (carry !== 0) { - this.words[i] = carry; - this.length++; - } - - return this; - }; - - BN.prototype.muln = function muln (num) { - return this.clone().imuln(num); - }; - - // `this` * `this` - BN.prototype.sqr = function sqr () { - return this.mul(this); - }; - - // `this` * `this` in-place - BN.prototype.isqr = function isqr () { - return this.imul(this.clone()); - }; - - // Math.pow(`this`, `num`) - BN.prototype.pow = function pow (num) { - var w = toBitArray(num); - if (w.length === 0) return new BN(1); - - // Skip leading zeroes - var res = this; - for (var i = 0; i < w.length; i++, res = res.sqr()) { - if (w[i] !== 0) break; - } - - if (++i < w.length) { - for (var q = res.sqr(); i < w.length; i++, q = q.sqr()) { - if (w[i] === 0) continue; - - res = res.mul(q); - } - } - - return res; - }; - - // Shift-left in-place - BN.prototype.iushln = function iushln (bits) { - assert(typeof bits === 'number' && bits >= 0); - var r = bits % 26; - var s = (bits - r) / 26; - var carryMask = (0x3ffffff >>> (26 - r)) << (26 - r); - var i; - - if (r !== 0) { - var carry = 0; - - for (i = 0; i < this.length; i++) { - var newCarry = this.words[i] & carryMask; - var c = ((this.words[i] | 0) - newCarry) << r; - this.words[i] = c | carry; - carry = newCarry >>> (26 - r); - } - - if (carry) { - this.words[i] = carry; - this.length++; - } - } - - if (s !== 0) { - for (i = this.length - 1; i >= 0; i--) { - this.words[i + s] = this.words[i]; - } - - for (i = 0; i < s; i++) { - this.words[i] = 0; - } - - this.length += s; - } - - return this.strip(); - }; - - BN.prototype.ishln = function ishln (bits) { - // TODO(indutny): implement me - assert(this.negative === 0); - return this.iushln(bits); - }; - - // Shift-right in-place - // NOTE: `hint` is a lowest bit before trailing zeroes - // NOTE: if `extended` is present - it will be filled with destroyed bits - BN.prototype.iushrn = function iushrn (bits, hint, extended) { - assert(typeof bits === 'number' && bits >= 0); - var h; - if (hint) { - h = (hint - (hint % 26)) / 26; - } else { - h = 0; - } - - var r = bits % 26; - var s = Math.min((bits - r) / 26, this.length); - var mask = 0x3ffffff ^ ((0x3ffffff >>> r) << r); - var maskedWords = extended; - - h -= s; - h = Math.max(0, h); - - // Extended mode, copy masked part - if (maskedWords) { - for (var i = 0; i < s; i++) { - maskedWords.words[i] = this.words[i]; - } - maskedWords.length = s; - } - - if (s === 0) { - // No-op, we should not move anything at all - } else if (this.length > s) { - this.length -= s; - for (i = 0; i < this.length; i++) { - this.words[i] = this.words[i + s]; - } - } else { - this.words[0] = 0; - this.length = 1; - } - - var carry = 0; - for (i = this.length - 1; i >= 0 && (carry !== 0 || i >= h); i--) { - var word = this.words[i] | 0; - this.words[i] = (carry << (26 - r)) | (word >>> r); - carry = word & mask; - } - - // Push carried bits as a mask - if (maskedWords && carry !== 0) { - maskedWords.words[maskedWords.length++] = carry; - } - - if (this.length === 0) { - this.words[0] = 0; - this.length = 1; - } - - return this.strip(); - }; - - BN.prototype.ishrn = function ishrn (bits, hint, extended) { - // TODO(indutny): implement me - assert(this.negative === 0); - return this.iushrn(bits, hint, extended); - }; - - // Shift-left - BN.prototype.shln = function shln (bits) { - return this.clone().ishln(bits); - }; - - BN.prototype.ushln = function ushln (bits) { - return this.clone().iushln(bits); - }; - - // Shift-right - BN.prototype.shrn = function shrn (bits) { - return this.clone().ishrn(bits); - }; - - BN.prototype.ushrn = function ushrn (bits) { - return this.clone().iushrn(bits); - }; - - // Test if n bit is set - BN.prototype.testn = function testn (bit) { - assert(typeof bit === 'number' && bit >= 0); - var r = bit % 26; - var s = (bit - r) / 26; - var q = 1 << r; - - // Fast case: bit is much higher than all existing words - if (this.length <= s) return false; - - // Check bit and return - var w = this.words[s]; - - return !!(w & q); - }; - - // Return only lowers bits of number (in-place) - BN.prototype.imaskn = function imaskn (bits) { - assert(typeof bits === 'number' && bits >= 0); - var r = bits % 26; - var s = (bits - r) / 26; - - assert(this.negative === 0, 'imaskn works only with positive numbers'); - - if (this.length <= s) { - return this; - } - - if (r !== 0) { - s++; - } - this.length = Math.min(s, this.length); - - if (r !== 0) { - var mask = 0x3ffffff ^ ((0x3ffffff >>> r) << r); - this.words[this.length - 1] &= mask; - } - - return this.strip(); - }; - - // Return only lowers bits of number - BN.prototype.maskn = function maskn (bits) { - return this.clone().imaskn(bits); - }; - - // Add plain number `num` to `this` - BN.prototype.iaddn = function iaddn (num) { - assert(typeof num === 'number'); - assert(num < 0x4000000); - if (num < 0) return this.isubn(-num); - - // Possible sign change - if (this.negative !== 0) { - if (this.length === 1 && (this.words[0] | 0) < num) { - this.words[0] = num - (this.words[0] | 0); - this.negative = 0; - return this; - } - - this.negative = 0; - this.isubn(num); - this.negative = 1; - return this; - } - - // Add without checks - return this._iaddn(num); - }; - - BN.prototype._iaddn = function _iaddn (num) { - this.words[0] += num; - - // Carry - for (var i = 0; i < this.length && this.words[i] >= 0x4000000; i++) { - this.words[i] -= 0x4000000; - if (i === this.length - 1) { - this.words[i + 1] = 1; - } else { - this.words[i + 1]++; - } - } - this.length = Math.max(this.length, i + 1); - - return this; - }; - - // Subtract plain number `num` from `this` - BN.prototype.isubn = function isubn (num) { - assert(typeof num === 'number'); - assert(num < 0x4000000); - if (num < 0) return this.iaddn(-num); - - if (this.negative !== 0) { - this.negative = 0; - this.iaddn(num); - this.negative = 1; - return this; - } - - this.words[0] -= num; - - if (this.length === 1 && this.words[0] < 0) { - this.words[0] = -this.words[0]; - this.negative = 1; - } else { - // Carry - for (var i = 0; i < this.length && this.words[i] < 0; i++) { - this.words[i] += 0x4000000; - this.words[i + 1] -= 1; - } - } - - return this.strip(); - }; - - BN.prototype.addn = function addn (num) { - return this.clone().iaddn(num); - }; - - BN.prototype.subn = function subn (num) { - return this.clone().isubn(num); - }; - - BN.prototype.iabs = function iabs () { - this.negative = 0; - - return this; - }; - - BN.prototype.abs = function abs () { - return this.clone().iabs(); - }; - - BN.prototype._ishlnsubmul = function _ishlnsubmul (num, mul, shift) { - var len = num.length + shift; - var i; - - this._expand(len); - - var w; - var carry = 0; - for (i = 0; i < num.length; i++) { - w = (this.words[i + shift] | 0) + carry; - var right = (num.words[i] | 0) * mul; - w -= right & 0x3ffffff; - carry = (w >> 26) - ((right / 0x4000000) | 0); - this.words[i + shift] = w & 0x3ffffff; - } - for (; i < this.length - shift; i++) { - w = (this.words[i + shift] | 0) + carry; - carry = w >> 26; - this.words[i + shift] = w & 0x3ffffff; - } - - if (carry === 0) return this.strip(); - - // Subtraction overflow - assert(carry === -1); - carry = 0; - for (i = 0; i < this.length; i++) { - w = -(this.words[i] | 0) + carry; - carry = w >> 26; - this.words[i] = w & 0x3ffffff; - } - this.negative = 1; - - return this.strip(); - }; - - BN.prototype._wordDiv = function _wordDiv (num, mode) { - var shift = this.length - num.length; - - var a = this.clone(); - var b = num; - - // Normalize - var bhi = b.words[b.length - 1] | 0; - var bhiBits = this._countBits(bhi); - shift = 26 - bhiBits; - if (shift !== 0) { - b = b.ushln(shift); - a.iushln(shift); - bhi = b.words[b.length - 1] | 0; - } - - // Initialize quotient - var m = a.length - b.length; - var q; - - if (mode !== 'mod') { - q = new BN(null); - q.length = m + 1; - q.words = new Array(q.length); - for (var i = 0; i < q.length; i++) { - q.words[i] = 0; - } - } - - var diff = a.clone()._ishlnsubmul(b, 1, m); - if (diff.negative === 0) { - a = diff; - if (q) { - q.words[m] = 1; - } - } - - for (var j = m - 1; j >= 0; j--) { - var qj = (a.words[b.length + j] | 0) * 0x4000000 + - (a.words[b.length + j - 1] | 0); - - // NOTE: (qj / bhi) is (0x3ffffff * 0x4000000 + 0x3ffffff) / 0x2000000 max - // (0x7ffffff) - qj = Math.min((qj / bhi) | 0, 0x3ffffff); - - a._ishlnsubmul(b, qj, j); - while (a.negative !== 0) { - qj--; - a.negative = 0; - a._ishlnsubmul(b, 1, j); - if (!a.isZero()) { - a.negative ^= 1; - } - } - if (q) { - q.words[j] = qj; - } - } - if (q) { - q.strip(); - } - a.strip(); - - // Denormalize - if (mode !== 'div' && shift !== 0) { - a.iushrn(shift); - } - - return { - div: q || null, - mod: a - }; - }; - - // NOTE: 1) `mode` can be set to `mod` to request mod only, - // to `div` to request div only, or be absent to - // request both div & mod - // 2) `positive` is true if unsigned mod is requested - BN.prototype.divmod = function divmod (num, mode, positive) { - assert(!num.isZero()); - - if (this.isZero()) { - return { - div: new BN(0), - mod: new BN(0) - }; - } - - var div, mod, res; - if (this.negative !== 0 && num.negative === 0) { - res = this.neg().divmod(num, mode); - - if (mode !== 'mod') { - div = res.div.neg(); - } - - if (mode !== 'div') { - mod = res.mod.neg(); - if (positive && mod.negative !== 0) { - mod.iadd(num); - } - } - - return { - div: div, - mod: mod - }; - } - - if (this.negative === 0 && num.negative !== 0) { - res = this.divmod(num.neg(), mode); - - if (mode !== 'mod') { - div = res.div.neg(); - } - - return { - div: div, - mod: res.mod - }; - } - - if ((this.negative & num.negative) !== 0) { - res = this.neg().divmod(num.neg(), mode); - - if (mode !== 'div') { - mod = res.mod.neg(); - if (positive && mod.negative !== 0) { - mod.isub(num); - } - } - - return { - div: res.div, - mod: mod - }; - } - - // Both numbers are positive at this point - - // Strip both numbers to approximate shift value - if (num.length > this.length || this.cmp(num) < 0) { - return { - div: new BN(0), - mod: this - }; - } - - // Very short reduction - if (num.length === 1) { - if (mode === 'div') { - return { - div: this.divn(num.words[0]), - mod: null - }; - } - - if (mode === 'mod') { - return { - div: null, - mod: new BN(this.modn(num.words[0])) - }; - } - - return { - div: this.divn(num.words[0]), - mod: new BN(this.modn(num.words[0])) - }; - } - - return this._wordDiv(num, mode); - }; - - // Find `this` / `num` - BN.prototype.div = function div (num) { - return this.divmod(num, 'div', false).div; - }; - - // Find `this` % `num` - BN.prototype.mod = function mod (num) { - return this.divmod(num, 'mod', false).mod; - }; - - BN.prototype.umod = function umod (num) { - return this.divmod(num, 'mod', true).mod; - }; - - // Find Round(`this` / `num`) - BN.prototype.divRound = function divRound (num) { - var dm = this.divmod(num); - - // Fast case - exact division - if (dm.mod.isZero()) return dm.div; - - var mod = dm.div.negative !== 0 ? dm.mod.isub(num) : dm.mod; - - var half = num.ushrn(1); - var r2 = num.andln(1); - var cmp = mod.cmp(half); - - // Round down - if (cmp < 0 || r2 === 1 && cmp === 0) return dm.div; - - // Round up - return dm.div.negative !== 0 ? dm.div.isubn(1) : dm.div.iaddn(1); - }; - - BN.prototype.modn = function modn (num) { - assert(num <= 0x3ffffff); - var p = (1 << 26) % num; - - var acc = 0; - for (var i = this.length - 1; i >= 0; i--) { - acc = (p * acc + (this.words[i] | 0)) % num; - } - - return acc; - }; - - // In-place division by number - BN.prototype.idivn = function idivn (num) { - assert(num <= 0x3ffffff); - - var carry = 0; - for (var i = this.length - 1; i >= 0; i--) { - var w = (this.words[i] | 0) + carry * 0x4000000; - this.words[i] = (w / num) | 0; - carry = w % num; - } - - return this.strip(); - }; - - BN.prototype.divn = function divn (num) { - return this.clone().idivn(num); - }; - - BN.prototype.egcd = function egcd (p) { - assert(p.negative === 0); - assert(!p.isZero()); - - var x = this; - var y = p.clone(); - - if (x.negative !== 0) { - x = x.umod(p); - } else { - x = x.clone(); - } - - // A * x + B * y = x - var A = new BN(1); - var B = new BN(0); - - // C * x + D * y = y - var C = new BN(0); - var D = new BN(1); - - var g = 0; - - while (x.isEven() && y.isEven()) { - x.iushrn(1); - y.iushrn(1); - ++g; - } - - var yp = y.clone(); - var xp = x.clone(); - - while (!x.isZero()) { - for (var i = 0, im = 1; (x.words[0] & im) === 0 && i < 26; ++i, im <<= 1); - if (i > 0) { - x.iushrn(i); - while (i-- > 0) { - if (A.isOdd() || B.isOdd()) { - A.iadd(yp); - B.isub(xp); - } - - A.iushrn(1); - B.iushrn(1); - } - } - - for (var j = 0, jm = 1; (y.words[0] & jm) === 0 && j < 26; ++j, jm <<= 1); - if (j > 0) { - y.iushrn(j); - while (j-- > 0) { - if (C.isOdd() || D.isOdd()) { - C.iadd(yp); - D.isub(xp); - } - - C.iushrn(1); - D.iushrn(1); - } - } - - if (x.cmp(y) >= 0) { - x.isub(y); - A.isub(C); - B.isub(D); - } else { - y.isub(x); - C.isub(A); - D.isub(B); - } - } - - return { - a: C, - b: D, - gcd: y.iushln(g) - }; - }; - - // This is reduced incarnation of the binary EEA - // above, designated to invert members of the - // _prime_ fields F(p) at a maximal speed - BN.prototype._invmp = function _invmp (p) { - assert(p.negative === 0); - assert(!p.isZero()); - - var a = this; - var b = p.clone(); - - if (a.negative !== 0) { - a = a.umod(p); - } else { - a = a.clone(); - } - - var x1 = new BN(1); - var x2 = new BN(0); - - var delta = b.clone(); - - while (a.cmpn(1) > 0 && b.cmpn(1) > 0) { - for (var i = 0, im = 1; (a.words[0] & im) === 0 && i < 26; ++i, im <<= 1); - if (i > 0) { - a.iushrn(i); - while (i-- > 0) { - if (x1.isOdd()) { - x1.iadd(delta); - } - - x1.iushrn(1); - } - } - - for (var j = 0, jm = 1; (b.words[0] & jm) === 0 && j < 26; ++j, jm <<= 1); - if (j > 0) { - b.iushrn(j); - while (j-- > 0) { - if (x2.isOdd()) { - x2.iadd(delta); - } - - x2.iushrn(1); - } - } - - if (a.cmp(b) >= 0) { - a.isub(b); - x1.isub(x2); - } else { - b.isub(a); - x2.isub(x1); - } - } - - var res; - if (a.cmpn(1) === 0) { - res = x1; - } else { - res = x2; - } - - if (res.cmpn(0) < 0) { - res.iadd(p); - } - - return res; - }; - - BN.prototype.gcd = function gcd (num) { - if (this.isZero()) return num.abs(); - if (num.isZero()) return this.abs(); - - var a = this.clone(); - var b = num.clone(); - a.negative = 0; - b.negative = 0; - - // Remove common factor of two - for (var shift = 0; a.isEven() && b.isEven(); shift++) { - a.iushrn(1); - b.iushrn(1); - } - - do { - while (a.isEven()) { - a.iushrn(1); - } - while (b.isEven()) { - b.iushrn(1); - } - - var r = a.cmp(b); - if (r < 0) { - // Swap `a` and `b` to make `a` always bigger than `b` - var t = a; - a = b; - b = t; - } else if (r === 0 || b.cmpn(1) === 0) { - break; - } - - a.isub(b); - } while (true); - - return b.iushln(shift); - }; - - // Invert number in the field F(num) - BN.prototype.invm = function invm (num) { - return this.egcd(num).a.umod(num); - }; - - BN.prototype.isEven = function isEven () { - return (this.words[0] & 1) === 0; - }; - - BN.prototype.isOdd = function isOdd () { - return (this.words[0] & 1) === 1; - }; - - // And first word and num - BN.prototype.andln = function andln (num) { - return this.words[0] & num; - }; - - // Increment at the bit position in-line - BN.prototype.bincn = function bincn (bit) { - assert(typeof bit === 'number'); - var r = bit % 26; - var s = (bit - r) / 26; - var q = 1 << r; - - // Fast case: bit is much higher than all existing words - if (this.length <= s) { - this._expand(s + 1); - this.words[s] |= q; - return this; - } - - // Add bit and propagate, if needed - var carry = q; - for (var i = s; carry !== 0 && i < this.length; i++) { - var w = this.words[i] | 0; - w += carry; - carry = w >>> 26; - w &= 0x3ffffff; - this.words[i] = w; - } - if (carry !== 0) { - this.words[i] = carry; - this.length++; - } - return this; - }; - - BN.prototype.isZero = function isZero () { - return this.length === 1 && this.words[0] === 0; - }; - - BN.prototype.cmpn = function cmpn (num) { - var negative = num < 0; - - if (this.negative !== 0 && !negative) return -1; - if (this.negative === 0 && negative) return 1; - - this.strip(); - - var res; - if (this.length > 1) { - res = 1; - } else { - if (negative) { - num = -num; - } - - assert(num <= 0x3ffffff, 'Number is too big'); - - var w = this.words[0] | 0; - res = w === num ? 0 : w < num ? -1 : 1; - } - if (this.negative !== 0) return -res | 0; - return res; - }; - - // Compare two numbers and return: - // 1 - if `this` > `num` - // 0 - if `this` == `num` - // -1 - if `this` < `num` - BN.prototype.cmp = function cmp (num) { - if (this.negative !== 0 && num.negative === 0) return -1; - if (this.negative === 0 && num.negative !== 0) return 1; - - var res = this.ucmp(num); - if (this.negative !== 0) return -res | 0; - return res; - }; - - // Unsigned comparison - BN.prototype.ucmp = function ucmp (num) { - // At this point both numbers have the same sign - if (this.length > num.length) return 1; - if (this.length < num.length) return -1; - - var res = 0; - for (var i = this.length - 1; i >= 0; i--) { - var a = this.words[i] | 0; - var b = num.words[i] | 0; - - if (a === b) continue; - if (a < b) { - res = -1; - } else if (a > b) { - res = 1; - } - break; - } - return res; - }; - - BN.prototype.gtn = function gtn (num) { - return this.cmpn(num) === 1; - }; - - BN.prototype.gt = function gt (num) { - return this.cmp(num) === 1; - }; - - BN.prototype.gten = function gten (num) { - return this.cmpn(num) >= 0; - }; - - BN.prototype.gte = function gte (num) { - return this.cmp(num) >= 0; - }; - - BN.prototype.ltn = function ltn (num) { - return this.cmpn(num) === -1; - }; - - BN.prototype.lt = function lt (num) { - return this.cmp(num) === -1; - }; - - BN.prototype.lten = function lten (num) { - return this.cmpn(num) <= 0; - }; - - BN.prototype.lte = function lte (num) { - return this.cmp(num) <= 0; - }; - - BN.prototype.eqn = function eqn (num) { - return this.cmpn(num) === 0; - }; - - BN.prototype.eq = function eq (num) { - return this.cmp(num) === 0; - }; - - // - // A reduce context, could be using montgomery or something better, depending - // on the `m` itself. - // - BN.red = function red (num) { - return new Red(num); - }; - - BN.prototype.toRed = function toRed (ctx) { - assert(!this.red, 'Already a number in reduction context'); - assert(this.negative === 0, 'red works only with positives'); - return ctx.convertTo(this)._forceRed(ctx); - }; - - BN.prototype.fromRed = function fromRed () { - assert(this.red, 'fromRed works only with numbers in reduction context'); - return this.red.convertFrom(this); - }; - - BN.prototype._forceRed = function _forceRed (ctx) { - this.red = ctx; - return this; - }; - - BN.prototype.forceRed = function forceRed (ctx) { - assert(!this.red, 'Already a number in reduction context'); - return this._forceRed(ctx); - }; - - BN.prototype.redAdd = function redAdd (num) { - assert(this.red, 'redAdd works only with red numbers'); - return this.red.add(this, num); - }; - - BN.prototype.redIAdd = function redIAdd (num) { - assert(this.red, 'redIAdd works only with red numbers'); - return this.red.iadd(this, num); - }; - - BN.prototype.redSub = function redSub (num) { - assert(this.red, 'redSub works only with red numbers'); - return this.red.sub(this, num); - }; - - BN.prototype.redISub = function redISub (num) { - assert(this.red, 'redISub works only with red numbers'); - return this.red.isub(this, num); - }; - - BN.prototype.redShl = function redShl (num) { - assert(this.red, 'redShl works only with red numbers'); - return this.red.shl(this, num); - }; - - BN.prototype.redMul = function redMul (num) { - assert(this.red, 'redMul works only with red numbers'); - this.red._verify2(this, num); - return this.red.mul(this, num); - }; - - BN.prototype.redIMul = function redIMul (num) { - assert(this.red, 'redMul works only with red numbers'); - this.red._verify2(this, num); - return this.red.imul(this, num); - }; - - BN.prototype.redSqr = function redSqr () { - assert(this.red, 'redSqr works only with red numbers'); - this.red._verify1(this); - return this.red.sqr(this); - }; - - BN.prototype.redISqr = function redISqr () { - assert(this.red, 'redISqr works only with red numbers'); - this.red._verify1(this); - return this.red.isqr(this); - }; - - // Square root over p - BN.prototype.redSqrt = function redSqrt () { - assert(this.red, 'redSqrt works only with red numbers'); - this.red._verify1(this); - return this.red.sqrt(this); - }; - - BN.prototype.redInvm = function redInvm () { - assert(this.red, 'redInvm works only with red numbers'); - this.red._verify1(this); - return this.red.invm(this); - }; - - // Return negative clone of `this` % `red modulo` - BN.prototype.redNeg = function redNeg () { - assert(this.red, 'redNeg works only with red numbers'); - this.red._verify1(this); - return this.red.neg(this); - }; - - BN.prototype.redPow = function redPow (num) { - assert(this.red && !num.red, 'redPow(normalNum)'); - this.red._verify1(this); - return this.red.pow(this, num); - }; - - // Prime numbers with efficient reduction - var primes = { - k256: null, - p224: null, - p192: null, - p25519: null - }; - - // Pseudo-Mersenne prime - function MPrime (name, p) { - // P = 2 ^ N - K - this.name = name; - this.p = new BN(p, 16); - this.n = this.p.bitLength(); - this.k = new BN(1).iushln(this.n).isub(this.p); - - this.tmp = this._tmp(); - } - - MPrime.prototype._tmp = function _tmp () { - var tmp = new BN(null); - tmp.words = new Array(Math.ceil(this.n / 13)); - return tmp; - }; - - MPrime.prototype.ireduce = function ireduce (num) { - // Assumes that `num` is less than `P^2` - // num = HI * (2 ^ N - K) + HI * K + LO = HI * K + LO (mod P) - var r = num; - var rlen; - - do { - this.split(r, this.tmp); - r = this.imulK(r); - r = r.iadd(this.tmp); - rlen = r.bitLength(); - } while (rlen > this.n); - - var cmp = rlen < this.n ? -1 : r.ucmp(this.p); - if (cmp === 0) { - r.words[0] = 0; - r.length = 1; - } else if (cmp > 0) { - r.isub(this.p); - } else { - if (r.strip !== undefined) { - // r is BN v4 instance - r.strip(); - } else { - // r is BN v5 instance - r._strip(); - } - } - - return r; - }; - - MPrime.prototype.split = function split (input, out) { - input.iushrn(this.n, 0, out); - }; - - MPrime.prototype.imulK = function imulK (num) { - return num.imul(this.k); - }; - - function K256 () { - MPrime.call( - this, - 'k256', - 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f'); - } - inherits(K256, MPrime); - - K256.prototype.split = function split (input, output) { - // 256 = 9 * 26 + 22 - var mask = 0x3fffff; - - var outLen = Math.min(input.length, 9); - for (var i = 0; i < outLen; i++) { - output.words[i] = input.words[i]; - } - output.length = outLen; - - if (input.length <= 9) { - input.words[0] = 0; - input.length = 1; - return; - } - - // Shift by 9 limbs - var prev = input.words[9]; - output.words[output.length++] = prev & mask; - - for (i = 10; i < input.length; i++) { - var next = input.words[i] | 0; - input.words[i - 10] = ((next & mask) << 4) | (prev >>> 22); - prev = next; - } - prev >>>= 22; - input.words[i - 10] = prev; - if (prev === 0 && input.length > 10) { - input.length -= 10; - } else { - input.length -= 9; - } - }; - - K256.prototype.imulK = function imulK (num) { - // K = 0x1000003d1 = [ 0x40, 0x3d1 ] - num.words[num.length] = 0; - num.words[num.length + 1] = 0; - num.length += 2; - - // bounded at: 0x40 * 0x3ffffff + 0x3d0 = 0x100000390 - var lo = 0; - for (var i = 0; i < num.length; i++) { - var w = num.words[i] | 0; - lo += w * 0x3d1; - num.words[i] = lo & 0x3ffffff; - lo = w * 0x40 + ((lo / 0x4000000) | 0); - } - - // Fast length reduction - if (num.words[num.length - 1] === 0) { - num.length--; - if (num.words[num.length - 1] === 0) { - num.length--; - } - } - return num; - }; - - function P224 () { - MPrime.call( - this, - 'p224', - 'ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001'); - } - inherits(P224, MPrime); - - function P192 () { - MPrime.call( - this, - 'p192', - 'ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff'); - } - inherits(P192, MPrime); - - function P25519 () { - // 2 ^ 255 - 19 - MPrime.call( - this, - '25519', - '7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed'); - } - inherits(P25519, MPrime); - - P25519.prototype.imulK = function imulK (num) { - // K = 0x13 - var carry = 0; - for (var i = 0; i < num.length; i++) { - var hi = (num.words[i] | 0) * 0x13 + carry; - var lo = hi & 0x3ffffff; - hi >>>= 26; - - num.words[i] = lo; - carry = hi; - } - if (carry !== 0) { - num.words[num.length++] = carry; - } - return num; - }; - - // Exported mostly for testing purposes, use plain name instead - BN._prime = function prime (name) { - // Cached version of prime - if (primes[name]) return primes[name]; - - var prime; - if (name === 'k256') { - prime = new K256(); - } else if (name === 'p224') { - prime = new P224(); - } else if (name === 'p192') { - prime = new P192(); - } else if (name === 'p25519') { - prime = new P25519(); - } else { - throw new Error('Unknown prime ' + name); - } - primes[name] = prime; - - return prime; - }; - - // - // Base reduction engine - // - function Red (m) { - if (typeof m === 'string') { - var prime = BN._prime(m); - this.m = prime.p; - this.prime = prime; - } else { - assert(m.gtn(1), 'modulus must be greater than 1'); - this.m = m; - this.prime = null; - } - } - - Red.prototype._verify1 = function _verify1 (a) { - assert(a.negative === 0, 'red works only with positives'); - assert(a.red, 'red works only with red numbers'); - }; - - Red.prototype._verify2 = function _verify2 (a, b) { - assert((a.negative | b.negative) === 0, 'red works only with positives'); - assert(a.red && a.red === b.red, - 'red works only with red numbers'); - }; - - Red.prototype.imod = function imod (a) { - if (this.prime) return this.prime.ireduce(a)._forceRed(this); - return a.umod(this.m)._forceRed(this); - }; - - Red.prototype.neg = function neg (a) { - if (a.isZero()) { - return a.clone(); - } - - return this.m.sub(a)._forceRed(this); - }; - - Red.prototype.add = function add (a, b) { - this._verify2(a, b); - - var res = a.add(b); - if (res.cmp(this.m) >= 0) { - res.isub(this.m); - } - return res._forceRed(this); - }; - - Red.prototype.iadd = function iadd (a, b) { - this._verify2(a, b); - - var res = a.iadd(b); - if (res.cmp(this.m) >= 0) { - res.isub(this.m); - } - return res; - }; - - Red.prototype.sub = function sub (a, b) { - this._verify2(a, b); - - var res = a.sub(b); - if (res.cmpn(0) < 0) { - res.iadd(this.m); - } - return res._forceRed(this); - }; - - Red.prototype.isub = function isub (a, b) { - this._verify2(a, b); - - var res = a.isub(b); - if (res.cmpn(0) < 0) { - res.iadd(this.m); - } - return res; - }; - - Red.prototype.shl = function shl (a, num) { - this._verify1(a); - return this.imod(a.ushln(num)); - }; - - Red.prototype.imul = function imul (a, b) { - this._verify2(a, b); - return this.imod(a.imul(b)); - }; - - Red.prototype.mul = function mul (a, b) { - this._verify2(a, b); - return this.imod(a.mul(b)); - }; - - Red.prototype.isqr = function isqr (a) { - return this.imul(a, a.clone()); - }; - - Red.prototype.sqr = function sqr (a) { - return this.mul(a, a); - }; - - Red.prototype.sqrt = function sqrt (a) { - if (a.isZero()) return a.clone(); - - var mod3 = this.m.andln(3); - assert(mod3 % 2 === 1); - - // Fast case - if (mod3 === 3) { - var pow = this.m.add(new BN(1)).iushrn(2); - return this.pow(a, pow); - } - - // Tonelli-Shanks algorithm (Totally unoptimized and slow) - // - // Find Q and S, that Q * 2 ^ S = (P - 1) - var q = this.m.subn(1); - var s = 0; - while (!q.isZero() && q.andln(1) === 0) { - s++; - q.iushrn(1); - } - assert(!q.isZero()); - - var one = new BN(1).toRed(this); - var nOne = one.redNeg(); - - // Find quadratic non-residue - // NOTE: Max is such because of generalized Riemann hypothesis. - var lpow = this.m.subn(1).iushrn(1); - var z = this.m.bitLength(); - z = new BN(2 * z * z).toRed(this); - - while (this.pow(z, lpow).cmp(nOne) !== 0) { - z.redIAdd(nOne); - } - - var c = this.pow(z, q); - var r = this.pow(a, q.addn(1).iushrn(1)); - var t = this.pow(a, q); - var m = s; - while (t.cmp(one) !== 0) { - var tmp = t; - for (var i = 0; tmp.cmp(one) !== 0; i++) { - tmp = tmp.redSqr(); - } - assert(i < m); - var b = this.pow(c, new BN(1).iushln(m - i - 1)); - - r = r.redMul(b); - c = b.redSqr(); - t = t.redMul(c); - m = i; - } - - return r; - }; - - Red.prototype.invm = function invm (a) { - var inv = a._invmp(this.m); - if (inv.negative !== 0) { - inv.negative = 0; - return this.imod(inv).redNeg(); - } else { - return this.imod(inv); - } - }; - - Red.prototype.pow = function pow (a, num) { - if (num.isZero()) return new BN(1).toRed(this); - if (num.cmpn(1) === 0) return a.clone(); - - var windowSize = 4; - var wnd = new Array(1 << windowSize); - wnd[0] = new BN(1).toRed(this); - wnd[1] = a; - for (var i = 2; i < wnd.length; i++) { - wnd[i] = this.mul(wnd[i - 1], a); - } - - var res = wnd[0]; - var current = 0; - var currentLen = 0; - var start = num.bitLength() % 26; - if (start === 0) { - start = 26; - } - - for (i = num.length - 1; i >= 0; i--) { - var word = num.words[i]; - for (var j = start - 1; j >= 0; j--) { - var bit = (word >> j) & 1; - if (res !== wnd[0]) { - res = this.sqr(res); - } - - if (bit === 0 && current === 0) { - currentLen = 0; - continue; - } - - current <<= 1; - current |= bit; - currentLen++; - if (currentLen !== windowSize && (i !== 0 || j !== 0)) continue; - - res = this.mul(res, wnd[current]); - currentLen = 0; - current = 0; - } - start = 26; - } - - return res; - }; - - Red.prototype.convertTo = function convertTo (num) { - var r = num.umod(this.m); - - return r === num ? r.clone() : r; - }; - - Red.prototype.convertFrom = function convertFrom (num) { - var res = num.clone(); - res.red = null; - return res; - }; - - // - // Montgomery method engine - // - - BN.mont = function mont (num) { - return new Mont(num); - }; - - function Mont (m) { - Red.call(this, m); - - this.shift = this.m.bitLength(); - if (this.shift % 26 !== 0) { - this.shift += 26 - (this.shift % 26); - } - - this.r = new BN(1).iushln(this.shift); - this.r2 = this.imod(this.r.sqr()); - this.rinv = this.r._invmp(this.m); - - this.minv = this.rinv.mul(this.r).isubn(1).div(this.m); - this.minv = this.minv.umod(this.r); - this.minv = this.r.sub(this.minv); - } - inherits(Mont, Red); - - Mont.prototype.convertTo = function convertTo (num) { - return this.imod(num.ushln(this.shift)); - }; - - Mont.prototype.convertFrom = function convertFrom (num) { - var r = this.imod(num.mul(this.rinv)); - r.red = null; - return r; - }; - - Mont.prototype.imul = function imul (a, b) { - if (a.isZero() || b.isZero()) { - a.words[0] = 0; - a.length = 1; - return a; - } - - var t = a.imul(b); - var c = t.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m); - var u = t.isub(c).iushrn(this.shift); - var res = u; - - if (u.cmp(this.m) >= 0) { - res = u.isub(this.m); - } else if (u.cmpn(0) < 0) { - res = u.iadd(this.m); - } - - return res._forceRed(this); - }; - - Mont.prototype.mul = function mul (a, b) { - if (a.isZero() || b.isZero()) return new BN(0)._forceRed(this); - - var t = a.mul(b); - var c = t.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m); - var u = t.isub(c).iushrn(this.shift); - var res = u; - if (u.cmp(this.m) >= 0) { - res = u.isub(this.m); - } else if (u.cmpn(0) < 0) { - res = u.iadd(this.m); - } - - return res._forceRed(this); - }; - - Mont.prototype.invm = function invm (a) { - // (AR)^-1 * R^2 = (A^-1 * R^-1) * R^2 = A^-1 * R - var res = this.imod(a._invmp(this.m).mul(this.r2)); - return res._forceRed(this); - }; -})(typeof module === 'undefined' || module, this); - -},{"buffer":34}],21:[function(require,module,exports){ -// Copyright 2011 Mark Cavage All rights reserved. - - -module.exports = { - - newInvalidAsn1Error: function (msg) { - var e = new Error(); - e.name = 'InvalidAsn1Error'; - e.message = msg || ''; - return e; - } - -}; - -},{}],22:[function(require,module,exports){ -// Copyright 2011 Mark Cavage All rights reserved. - -var errors = require('./errors'); -var types = require('./types'); - -var Reader = require('./reader'); -var Writer = require('./writer'); - - -// --- Exports - -module.exports = { - - Reader: Reader, - - Writer: Writer - -}; - -for (var t in types) { - if (types.hasOwnProperty(t)) - module.exports[t] = types[t]; -} -for (var e in errors) { - if (errors.hasOwnProperty(e)) - module.exports[e] = errors[e]; -} - -},{"./errors":21,"./reader":23,"./types":24,"./writer":25}],23:[function(require,module,exports){ -// Copyright 2011 Mark Cavage All rights reserved. - -var assert = require('assert'); -var Buffer = require('safer-buffer').Buffer; - -var ASN1 = require('./types'); -var errors = require('./errors'); - - -// --- Globals - -var newInvalidAsn1Error = errors.newInvalidAsn1Error; - - - -// --- API - -function Reader(data) { - if (!data || !Buffer.isBuffer(data)) - throw new TypeError('data must be a node Buffer'); - - this._buf = data; - this._size = data.length; - - // These hold the "current" state - this._len = 0; - this._offset = 0; -} - -Object.defineProperty(Reader.prototype, 'length', { - enumerable: true, - get: function () { return (this._len); } -}); - -Object.defineProperty(Reader.prototype, 'offset', { - enumerable: true, - get: function () { return (this._offset); } -}); - -Object.defineProperty(Reader.prototype, 'remain', { - get: function () { return (this._size - this._offset); } -}); - -Object.defineProperty(Reader.prototype, 'buffer', { - get: function () { return (this._buf.slice(this._offset)); } -}); - - -/** - * Reads a single byte and advances offset; you can pass in `true` to make this - * a "peek" operation (i.e., get the byte, but don't advance the offset). - * - * @param {Boolean} peek true means don't move offset. - * @return {Number} the next byte, null if not enough data. - */ -Reader.prototype.readByte = function (peek) { - if (this._size - this._offset < 1) - return null; - - var b = this._buf[this._offset] & 0xff; - - if (!peek) - this._offset += 1; - - return b; -}; - - -Reader.prototype.peek = function () { - return this.readByte(true); -}; - - -/** - * Reads a (potentially) variable length off the BER buffer. This call is - * not really meant to be called directly, as callers have to manipulate - * the internal buffer afterwards. - * - * As a result of this call, you can call `Reader.length`, until the - * next thing called that does a readLength. - * - * @return {Number} the amount of offset to advance the buffer. - * @throws {InvalidAsn1Error} on bad ASN.1 - */ -Reader.prototype.readLength = function (offset) { - if (offset === undefined) - offset = this._offset; - - if (offset >= this._size) - return null; - - var lenB = this._buf[offset++] & 0xff; - if (lenB === null) - return null; - - if ((lenB & 0x80) === 0x80) { - lenB &= 0x7f; - - if (lenB === 0) - throw newInvalidAsn1Error('Indefinite length not supported'); - - if (lenB > 4) - throw newInvalidAsn1Error('encoding too long'); - - if (this._size - offset < lenB) - return null; - - this._len = 0; - for (var i = 0; i < lenB; i++) - this._len = (this._len << 8) + (this._buf[offset++] & 0xff); - - } else { - // Wasn't a variable length - this._len = lenB; - } - - return offset; -}; - - -/** - * Parses the next sequence in this BER buffer. - * - * To get the length of the sequence, call `Reader.length`. - * - * @return {Number} the sequence's tag. - */ -Reader.prototype.readSequence = function (tag) { - var seq = this.peek(); - if (seq === null) - return null; - if (tag !== undefined && tag !== seq) - throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) + - ': got 0x' + seq.toString(16)); - - var o = this.readLength(this._offset + 1); // stored in `length` - if (o === null) - return null; - - this._offset = o; - return seq; -}; - - -Reader.prototype.readInt = function () { - return this._readTag(ASN1.Integer); -}; - - -Reader.prototype.readBoolean = function () { - return (this._readTag(ASN1.Boolean) === 0 ? false : true); -}; - - -Reader.prototype.readEnumeration = function () { - return this._readTag(ASN1.Enumeration); -}; - - -Reader.prototype.readString = function (tag, retbuf) { - if (!tag) - tag = ASN1.OctetString; - - var b = this.peek(); - if (b === null) - return null; - - if (b !== tag) - throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) + - ': got 0x' + b.toString(16)); - - var o = this.readLength(this._offset + 1); // stored in `length` - - if (o === null) - return null; - - if (this.length > this._size - o) - return null; - - this._offset = o; - - if (this.length === 0) - return retbuf ? Buffer.alloc(0) : ''; - - var str = this._buf.slice(this._offset, this._offset + this.length); - this._offset += this.length; - - return retbuf ? str : str.toString('utf8'); -}; - -Reader.prototype.readOID = function (tag) { - if (!tag) - tag = ASN1.OID; - - var b = this.readString(tag, true); - if (b === null) - return null; - - var values = []; - var value = 0; - - for (var i = 0; i < b.length; i++) { - var byte = b[i] & 0xff; - - value <<= 7; - value += byte & 0x7f; - if ((byte & 0x80) === 0) { - values.push(value); - value = 0; - } - } - - value = values.shift(); - values.unshift(value % 40); - values.unshift((value / 40) >> 0); - - return values.join('.'); -}; - - -Reader.prototype._readTag = function (tag) { - assert.ok(tag !== undefined); - - var b = this.peek(); - - if (b === null) - return null; - - if (b !== tag) - throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) + - ': got 0x' + b.toString(16)); - - var o = this.readLength(this._offset + 1); // stored in `length` - if (o === null) - return null; - - if (this.length > 4) - throw newInvalidAsn1Error('Integer too long: ' + this.length); - - if (this.length > this._size - o) - return null; - this._offset = o; - - var fb = this._buf[this._offset]; - var value = 0; - - for (var i = 0; i < this.length; i++) { - value <<= 8; - value |= (this._buf[this._offset++] & 0xff); - } - - if ((fb & 0x80) === 0x80 && i !== 4) - value -= (1 << (i * 8)); - - return value >> 0; -}; - - - -// --- Exported API - -module.exports = Reader; - -},{"./errors":21,"./types":24,"assert":27,"safer-buffer":222}],24:[function(require,module,exports){ -// Copyright 2011 Mark Cavage All rights reserved. - - -module.exports = { - EOC: 0, - Boolean: 1, - Integer: 2, - BitString: 3, - OctetString: 4, - Null: 5, - OID: 6, - ObjectDescriptor: 7, - External: 8, - Real: 9, // float - Enumeration: 10, - PDV: 11, - Utf8String: 12, - RelativeOID: 13, - Sequence: 16, - Set: 17, - NumericString: 18, - PrintableString: 19, - T61String: 20, - VideotexString: 21, - IA5String: 22, - UTCTime: 23, - GeneralizedTime: 24, - GraphicString: 25, - VisibleString: 26, - GeneralString: 28, - UniversalString: 29, - CharacterString: 30, - BMPString: 31, - Constructor: 32, - Context: 128 -}; - -},{}],25:[function(require,module,exports){ -// Copyright 2011 Mark Cavage All rights reserved. - -var assert = require('assert'); -var Buffer = require('safer-buffer').Buffer; -var ASN1 = require('./types'); -var errors = require('./errors'); - - -// --- Globals - -var newInvalidAsn1Error = errors.newInvalidAsn1Error; - -var DEFAULT_OPTS = { - size: 1024, - growthFactor: 8 -}; - - -// --- Helpers - -function merge(from, to) { - assert.ok(from); - assert.equal(typeof (from), 'object'); - assert.ok(to); - assert.equal(typeof (to), 'object'); - - var keys = Object.getOwnPropertyNames(from); - keys.forEach(function (key) { - if (to[key]) - return; - - var value = Object.getOwnPropertyDescriptor(from, key); - Object.defineProperty(to, key, value); - }); - - return to; -} - - - -// --- API - -function Writer(options) { - options = merge(DEFAULT_OPTS, options || {}); - - this._buf = Buffer.alloc(options.size || 1024); - this._size = this._buf.length; - this._offset = 0; - this._options = options; - - // A list of offsets in the buffer where we need to insert - // sequence tag/len pairs. - this._seq = []; -} - -Object.defineProperty(Writer.prototype, 'buffer', { - get: function () { - if (this._seq.length) - throw newInvalidAsn1Error(this._seq.length + ' unended sequence(s)'); - - return (this._buf.slice(0, this._offset)); - } -}); - -Writer.prototype.writeByte = function (b) { - if (typeof (b) !== 'number') - throw new TypeError('argument must be a Number'); - - this._ensure(1); - this._buf[this._offset++] = b; -}; - - -Writer.prototype.writeInt = function (i, tag) { - if (typeof (i) !== 'number') - throw new TypeError('argument must be a Number'); - if (typeof (tag) !== 'number') - tag = ASN1.Integer; - - var sz = 4; - - while ((((i & 0xff800000) === 0) || ((i & 0xff800000) === 0xff800000 >> 0)) && - (sz > 1)) { - sz--; - i <<= 8; - } - - if (sz > 4) - throw newInvalidAsn1Error('BER ints cannot be > 0xffffffff'); - - this._ensure(2 + sz); - this._buf[this._offset++] = tag; - this._buf[this._offset++] = sz; - - while (sz-- > 0) { - this._buf[this._offset++] = ((i & 0xff000000) >>> 24); - i <<= 8; - } - -}; - - -Writer.prototype.writeNull = function () { - this.writeByte(ASN1.Null); - this.writeByte(0x00); -}; - - -Writer.prototype.writeEnumeration = function (i, tag) { - if (typeof (i) !== 'number') - throw new TypeError('argument must be a Number'); - if (typeof (tag) !== 'number') - tag = ASN1.Enumeration; - - return this.writeInt(i, tag); -}; - - -Writer.prototype.writeBoolean = function (b, tag) { - if (typeof (b) !== 'boolean') - throw new TypeError('argument must be a Boolean'); - if (typeof (tag) !== 'number') - tag = ASN1.Boolean; - - this._ensure(3); - this._buf[this._offset++] = tag; - this._buf[this._offset++] = 0x01; - this._buf[this._offset++] = b ? 0xff : 0x00; -}; - - -Writer.prototype.writeString = function (s, tag) { - if (typeof (s) !== 'string') - throw new TypeError('argument must be a string (was: ' + typeof (s) + ')'); - if (typeof (tag) !== 'number') - tag = ASN1.OctetString; - - var len = Buffer.byteLength(s); - this.writeByte(tag); - this.writeLength(len); - if (len) { - this._ensure(len); - this._buf.write(s, this._offset); - this._offset += len; - } -}; - - -Writer.prototype.writeBuffer = function (buf, tag) { - if (typeof (tag) !== 'number') - throw new TypeError('tag must be a number'); - if (!Buffer.isBuffer(buf)) - throw new TypeError('argument must be a buffer'); - - this.writeByte(tag); - this.writeLength(buf.length); - this._ensure(buf.length); - buf.copy(this._buf, this._offset, 0, buf.length); - this._offset += buf.length; -}; - - -Writer.prototype.writeStringArray = function (strings) { - if ((!strings instanceof Array)) - throw new TypeError('argument must be an Array[String]'); - - var self = this; - strings.forEach(function (s) { - self.writeString(s); - }); -}; - -// This is really to solve DER cases, but whatever for now -Writer.prototype.writeOID = function (s, tag) { - if (typeof (s) !== 'string') - throw new TypeError('argument must be a string'); - if (typeof (tag) !== 'number') - tag = ASN1.OID; - - if (!/^([0-9]+\.){3,}[0-9]+$/.test(s)) - throw new Error('argument is not a valid OID string'); - - function encodeOctet(bytes, octet) { - if (octet < 128) { - bytes.push(octet); - } else if (octet < 16384) { - bytes.push((octet >>> 7) | 0x80); - bytes.push(octet & 0x7F); - } else if (octet < 2097152) { - bytes.push((octet >>> 14) | 0x80); - bytes.push(((octet >>> 7) | 0x80) & 0xFF); - bytes.push(octet & 0x7F); - } else if (octet < 268435456) { - bytes.push((octet >>> 21) | 0x80); - bytes.push(((octet >>> 14) | 0x80) & 0xFF); - bytes.push(((octet >>> 7) | 0x80) & 0xFF); - bytes.push(octet & 0x7F); - } else { - bytes.push(((octet >>> 28) | 0x80) & 0xFF); - bytes.push(((octet >>> 21) | 0x80) & 0xFF); - bytes.push(((octet >>> 14) | 0x80) & 0xFF); - bytes.push(((octet >>> 7) | 0x80) & 0xFF); - bytes.push(octet & 0x7F); - } - } - - var tmp = s.split('.'); - var bytes = []; - bytes.push(parseInt(tmp[0], 10) * 40 + parseInt(tmp[1], 10)); - tmp.slice(2).forEach(function (b) { - encodeOctet(bytes, parseInt(b, 10)); - }); - - var self = this; - this._ensure(2 + bytes.length); - this.writeByte(tag); - this.writeLength(bytes.length); - bytes.forEach(function (b) { - self.writeByte(b); - }); -}; - - -Writer.prototype.writeLength = function (len) { - if (typeof (len) !== 'number') - throw new TypeError('argument must be a Number'); - - this._ensure(4); - - if (len <= 0x7f) { - this._buf[this._offset++] = len; - } else if (len <= 0xff) { - this._buf[this._offset++] = 0x81; - this._buf[this._offset++] = len; - } else if (len <= 0xffff) { - this._buf[this._offset++] = 0x82; - this._buf[this._offset++] = len >> 8; - this._buf[this._offset++] = len; - } else if (len <= 0xffffff) { - this._buf[this._offset++] = 0x83; - this._buf[this._offset++] = len >> 16; - this._buf[this._offset++] = len >> 8; - this._buf[this._offset++] = len; - } else { - throw newInvalidAsn1Error('Length too long (> 4 bytes)'); - } -}; - -Writer.prototype.startSequence = function (tag) { - if (typeof (tag) !== 'number') - tag = ASN1.Sequence | ASN1.Constructor; - - this.writeByte(tag); - this._seq.push(this._offset); - this._ensure(3); - this._offset += 3; -}; - - -Writer.prototype.endSequence = function () { - var seq = this._seq.pop(); - var start = seq + 3; - var len = this._offset - start; - - if (len <= 0x7f) { - this._shift(start, len, -2); - this._buf[seq] = len; - } else if (len <= 0xff) { - this._shift(start, len, -1); - this._buf[seq] = 0x81; - this._buf[seq + 1] = len; - } else if (len <= 0xffff) { - this._buf[seq] = 0x82; - this._buf[seq + 1] = len >> 8; - this._buf[seq + 2] = len; - } else if (len <= 0xffffff) { - this._shift(start, len, 1); - this._buf[seq] = 0x83; - this._buf[seq + 1] = len >> 16; - this._buf[seq + 2] = len >> 8; - this._buf[seq + 3] = len; - } else { - throw newInvalidAsn1Error('Sequence too long'); - } -}; - - -Writer.prototype._shift = function (start, len, shift) { - assert.ok(start !== undefined); - assert.ok(len !== undefined); - assert.ok(shift); - - this._buf.copy(this._buf, start + shift, start, start + len); - this._offset += shift; -}; - -Writer.prototype._ensure = function (len) { - assert.ok(len); - - if (this._size - this._offset < len) { - var sz = this._size * this._options.growthFactor; - if (sz - this._offset < len) - sz += len; - - var buf = Buffer.alloc(sz); - - this._buf.copy(buf, 0, 0, this._offset); - this._buf = buf; - this._size = sz; - } -}; - - - -// --- Exported API - -module.exports = Writer; - -},{"./errors":21,"./types":24,"assert":27,"safer-buffer":222}],26:[function(require,module,exports){ -// Copyright 2011 Mark Cavage All rights reserved. - -// If you have no idea what ASN.1 or BER is, see this: -// ftp://ftp.rsa.com/pub/pkcs/ascii/layman.asc - -var Ber = require('./ber/index'); - - - -// --- Exported API - -module.exports = { - - Ber: Ber, - - BerReader: Ber.Reader, - - BerWriter: Ber.Writer - -}; - -},{"./ber/index":22}],27:[function(require,module,exports){ -(function (global){(function (){ -'use strict'; - -var objectAssign = require('object.assign/polyfill')(); - -// compare and isBuffer taken from https://github.com/feross/buffer/blob/680e9e5e488f22aac27599a57dc844a6315928dd/index.js -// original notice: - -/*! - * The buffer module from node.js, for the browser. - * - * @author Feross Aboukhadijeh - * @license MIT - */ -function compare(a, b) { - if (a === b) { - return 0; - } - - var x = a.length; - var y = b.length; - - for (var i = 0, len = Math.min(x, y); i < len; ++i) { - if (a[i] !== b[i]) { - x = a[i]; - y = b[i]; - break; - } - } - - if (x < y) { - return -1; - } - if (y < x) { - return 1; - } - return 0; -} -function isBuffer(b) { - if (global.Buffer && typeof global.Buffer.isBuffer === 'function') { - return global.Buffer.isBuffer(b); - } - return !!(b != null && b._isBuffer); -} - -// based on node assert, original notice: -// NB: The URL to the CommonJS spec is kept just for tradition. -// node-assert has evolved a lot since then, both in API and behavior. - -// http://wiki.commonjs.org/wiki/Unit_Testing/1.0 -// -// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8! -// -// Originally from narwhal.js (http://narwhaljs.org) -// Copyright (c) 2009 Thomas Robinson <280north.com> -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the 'Software'), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -var util = require('util/'); -var hasOwn = Object.prototype.hasOwnProperty; -var pSlice = Array.prototype.slice; -var functionsHaveNames = (function () { - return function foo() {}.name === 'foo'; -}()); -function pToString (obj) { - return Object.prototype.toString.call(obj); -} -function isView(arrbuf) { - if (isBuffer(arrbuf)) { - return false; - } - if (typeof global.ArrayBuffer !== 'function') { - return false; - } - if (typeof ArrayBuffer.isView === 'function') { - return ArrayBuffer.isView(arrbuf); - } - if (!arrbuf) { - return false; - } - if (arrbuf instanceof DataView) { - return true; - } - if (arrbuf.buffer && arrbuf.buffer instanceof ArrayBuffer) { - return true; - } - return false; -} -// 1. The assert module provides functions that throw -// AssertionError's when particular conditions are not met. The -// assert module must conform to the following interface. - -var assert = module.exports = ok; - -// 2. The AssertionError is defined in assert. -// new assert.AssertionError({ message: message, -// actual: actual, -// expected: expected }) - -var regex = /\s*function\s+([^\(\s]*)\s*/; -// based on https://github.com/ljharb/function.prototype.name/blob/adeeeec8bfcc6068b187d7d9fb3d5bb1d3a30899/implementation.js -function getName(func) { - if (!util.isFunction(func)) { - return; - } - if (functionsHaveNames) { - return func.name; - } - var str = func.toString(); - var match = str.match(regex); - return match && match[1]; -} -assert.AssertionError = function AssertionError(options) { - this.name = 'AssertionError'; - this.actual = options.actual; - this.expected = options.expected; - this.operator = options.operator; - if (options.message) { - this.message = options.message; - this.generatedMessage = false; - } else { - this.message = getMessage(this); - this.generatedMessage = true; - } - var stackStartFunction = options.stackStartFunction || fail; - if (Error.captureStackTrace) { - Error.captureStackTrace(this, stackStartFunction); - } else { - // non v8 browsers so we can have a stacktrace - var err = new Error(); - if (err.stack) { - var out = err.stack; - - // try to strip useless frames - var fn_name = getName(stackStartFunction); - var idx = out.indexOf('\n' + fn_name); - if (idx >= 0) { - // once we have located the function frame - // we need to strip out everything before it (and its line) - var next_line = out.indexOf('\n', idx + 1); - out = out.substring(next_line + 1); - } - - this.stack = out; - } - } -}; - -// assert.AssertionError instanceof Error -util.inherits(assert.AssertionError, Error); - -function truncate(s, n) { - if (typeof s === 'string') { - return s.length < n ? s : s.slice(0, n); - } else { - return s; - } -} -function inspect(something) { - if (functionsHaveNames || !util.isFunction(something)) { - return util.inspect(something); - } - var rawname = getName(something); - var name = rawname ? ': ' + rawname : ''; - return '[Function' + name + ']'; -} -function getMessage(self) { - return truncate(inspect(self.actual), 128) + ' ' + - self.operator + ' ' + - truncate(inspect(self.expected), 128); -} - -// At present only the three keys mentioned above are used and -// understood by the spec. Implementations or sub modules can pass -// other keys to the AssertionError's constructor - they will be -// ignored. - -// 3. All of the following functions must throw an AssertionError -// when a corresponding condition is not met, with a message that -// may be undefined if not provided. All assertion methods provide -// both the actual and expected values to the assertion error for -// display purposes. - -function fail(actual, expected, message, operator, stackStartFunction) { - throw new assert.AssertionError({ - message: message, - actual: actual, - expected: expected, - operator: operator, - stackStartFunction: stackStartFunction - }); -} - -// EXTENSION! allows for well behaved errors defined elsewhere. -assert.fail = fail; - -// 4. Pure assertion tests whether a value is truthy, as determined -// by !!guard. -// assert.ok(guard, message_opt); -// This statement is equivalent to assert.equal(true, !!guard, -// message_opt);. To test strictly for the value true, use -// assert.strictEqual(true, guard, message_opt);. - -function ok(value, message) { - if (!value) fail(value, true, message, '==', assert.ok); -} -assert.ok = ok; - -// 5. The equality assertion tests shallow, coercive equality with -// ==. -// assert.equal(actual, expected, message_opt); - -assert.equal = function equal(actual, expected, message) { - if (actual != expected) fail(actual, expected, message, '==', assert.equal); -}; - -// 6. The non-equality assertion tests for whether two objects are not equal -// with != assert.notEqual(actual, expected, message_opt); - -assert.notEqual = function notEqual(actual, expected, message) { - if (actual == expected) { - fail(actual, expected, message, '!=', assert.notEqual); - } -}; - -// 7. The equivalence assertion tests a deep equality relation. -// assert.deepEqual(actual, expected, message_opt); - -assert.deepEqual = function deepEqual(actual, expected, message) { - if (!_deepEqual(actual, expected, false)) { - fail(actual, expected, message, 'deepEqual', assert.deepEqual); - } -}; - -assert.deepStrictEqual = function deepStrictEqual(actual, expected, message) { - if (!_deepEqual(actual, expected, true)) { - fail(actual, expected, message, 'deepStrictEqual', assert.deepStrictEqual); - } -}; - -function _deepEqual(actual, expected, strict, memos) { - // 7.1. All identical values are equivalent, as determined by ===. - if (actual === expected) { - return true; - } else if (isBuffer(actual) && isBuffer(expected)) { - return compare(actual, expected) === 0; - - // 7.2. If the expected value is a Date object, the actual value is - // equivalent if it is also a Date object that refers to the same time. - } else if (util.isDate(actual) && util.isDate(expected)) { - return actual.getTime() === expected.getTime(); - - // 7.3 If the expected value is a RegExp object, the actual value is - // equivalent if it is also a RegExp object with the same source and - // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`). - } else if (util.isRegExp(actual) && util.isRegExp(expected)) { - return actual.source === expected.source && - actual.global === expected.global && - actual.multiline === expected.multiline && - actual.lastIndex === expected.lastIndex && - actual.ignoreCase === expected.ignoreCase; - - // 7.4. Other pairs that do not both pass typeof value == 'object', - // equivalence is determined by ==. - } else if ((actual === null || typeof actual !== 'object') && - (expected === null || typeof expected !== 'object')) { - return strict ? actual === expected : actual == expected; - - // If both values are instances of typed arrays, wrap their underlying - // ArrayBuffers in a Buffer each to increase performance - // This optimization requires the arrays to have the same type as checked by - // Object.prototype.toString (aka pToString). Never perform binary - // comparisons for Float*Arrays, though, since e.g. +0 === -0 but their - // bit patterns are not identical. - } else if (isView(actual) && isView(expected) && - pToString(actual) === pToString(expected) && - !(actual instanceof Float32Array || - actual instanceof Float64Array)) { - return compare(new Uint8Array(actual.buffer), - new Uint8Array(expected.buffer)) === 0; - - // 7.5 For all other Object pairs, including Array objects, equivalence is - // determined by having the same number of owned properties (as verified - // with Object.prototype.hasOwnProperty.call), the same set of keys - // (although not necessarily the same order), equivalent values for every - // corresponding key, and an identical 'prototype' property. Note: this - // accounts for both named and indexed properties on Arrays. - } else if (isBuffer(actual) !== isBuffer(expected)) { - return false; - } else { - memos = memos || {actual: [], expected: []}; - - var actualIndex = memos.actual.indexOf(actual); - if (actualIndex !== -1) { - if (actualIndex === memos.expected.indexOf(expected)) { - return true; - } - } - - memos.actual.push(actual); - memos.expected.push(expected); - - return objEquiv(actual, expected, strict, memos); - } -} - -function isArguments(object) { - return Object.prototype.toString.call(object) == '[object Arguments]'; -} - -function objEquiv(a, b, strict, actualVisitedObjects) { - if (a === null || a === undefined || b === null || b === undefined) - return false; - // if one is a primitive, the other must be same - if (util.isPrimitive(a) || util.isPrimitive(b)) - return a === b; - if (strict && Object.getPrototypeOf(a) !== Object.getPrototypeOf(b)) - return false; - var aIsArgs = isArguments(a); - var bIsArgs = isArguments(b); - if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs)) - return false; - if (aIsArgs) { - a = pSlice.call(a); - b = pSlice.call(b); - return _deepEqual(a, b, strict); - } - var ka = objectKeys(a); - var kb = objectKeys(b); - var key, i; - // having the same number of owned properties (keys incorporates - // hasOwnProperty) - if (ka.length !== kb.length) - return false; - //the same set of keys (although not necessarily the same order), - ka.sort(); - kb.sort(); - //~~~cheap key test - for (i = ka.length - 1; i >= 0; i--) { - if (ka[i] !== kb[i]) - return false; - } - //equivalent values for every corresponding key, and - //~~~possibly expensive deep test - for (i = ka.length - 1; i >= 0; i--) { - key = ka[i]; - if (!_deepEqual(a[key], b[key], strict, actualVisitedObjects)) - return false; - } - return true; -} - -// 8. The non-equivalence assertion tests for any deep inequality. -// assert.notDeepEqual(actual, expected, message_opt); - -assert.notDeepEqual = function notDeepEqual(actual, expected, message) { - if (_deepEqual(actual, expected, false)) { - fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual); - } -}; - -assert.notDeepStrictEqual = notDeepStrictEqual; -function notDeepStrictEqual(actual, expected, message) { - if (_deepEqual(actual, expected, true)) { - fail(actual, expected, message, 'notDeepStrictEqual', notDeepStrictEqual); - } -} - - -// 9. The strict equality assertion tests strict equality, as determined by ===. -// assert.strictEqual(actual, expected, message_opt); - -assert.strictEqual = function strictEqual(actual, expected, message) { - if (actual !== expected) { - fail(actual, expected, message, '===', assert.strictEqual); - } -}; - -// 10. The strict non-equality assertion tests for strict inequality, as -// determined by !==. assert.notStrictEqual(actual, expected, message_opt); - -assert.notStrictEqual = function notStrictEqual(actual, expected, message) { - if (actual === expected) { - fail(actual, expected, message, '!==', assert.notStrictEqual); - } -}; - -function expectedException(actual, expected) { - if (!actual || !expected) { - return false; - } - - if (Object.prototype.toString.call(expected) == '[object RegExp]') { - return expected.test(actual); - } - - try { - if (actual instanceof expected) { - return true; - } - } catch (e) { - // Ignore. The instanceof check doesn't work for arrow functions. - } - - if (Error.isPrototypeOf(expected)) { - return false; - } - - return expected.call({}, actual) === true; -} - -function _tryBlock(block) { - var error; - try { - block(); - } catch (e) { - error = e; - } - return error; -} - -function _throws(shouldThrow, block, expected, message) { - var actual; - - if (typeof block !== 'function') { - throw new TypeError('"block" argument must be a function'); - } - - if (typeof expected === 'string') { - message = expected; - expected = null; - } - - actual = _tryBlock(block); - - message = (expected && expected.name ? ' (' + expected.name + ').' : '.') + - (message ? ' ' + message : '.'); - - if (shouldThrow && !actual) { - fail(actual, expected, 'Missing expected exception' + message); - } - - var userProvidedMessage = typeof message === 'string'; - var isUnwantedException = !shouldThrow && util.isError(actual); - var isUnexpectedException = !shouldThrow && actual && !expected; - - if ((isUnwantedException && - userProvidedMessage && - expectedException(actual, expected)) || - isUnexpectedException) { - fail(actual, expected, 'Got unwanted exception' + message); - } - - if ((shouldThrow && actual && expected && - !expectedException(actual, expected)) || (!shouldThrow && actual)) { - throw actual; - } -} - -// 11. Expected to throw an error: -// assert.throws(block, Error_opt, message_opt); - -assert.throws = function(block, /*optional*/error, /*optional*/message) { - _throws(true, block, error, message); -}; - -// EXTENSION! This is annoying to write outside this module. -assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) { - _throws(false, block, error, message); -}; - -assert.ifError = function(err) { if (err) throw err; }; - -// Expose a strict only variant of assert -function strict(value, message) { - if (!value) fail(value, true, message, '==', strict); -} -assert.strict = objectAssign(strict, assert, { - equal: assert.strictEqual, - deepEqual: assert.deepStrictEqual, - notEqual: assert.notStrictEqual, - notDeepEqual: assert.notDeepStrictEqual -}); -assert.strict.strict = assert.strict; - -var objectKeys = Object.keys || function (obj) { - var keys = []; - for (var key in obj) { - if (hasOwn.call(obj, key)) keys.push(key); - } - return keys; -}; - -}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"object.assign/polyfill":186,"util/":30}],28:[function(require,module,exports){ -if (typeof Object.create === 'function') { - // implementation from standard node.js 'util' module - module.exports = function inherits(ctor, superCtor) { - ctor.super_ = superCtor - ctor.prototype = Object.create(superCtor.prototype, { - constructor: { - value: ctor, - enumerable: false, - writable: true, - configurable: true - } - }); - }; -} else { - // old school shim for old browsers - module.exports = function inherits(ctor, superCtor) { - ctor.super_ = superCtor - var TempCtor = function () {} - TempCtor.prototype = superCtor.prototype - ctor.prototype = new TempCtor() - ctor.prototype.constructor = ctor - } -} - -},{}],29:[function(require,module,exports){ -module.exports = function isBuffer(arg) { - return arg && typeof arg === 'object' - && typeof arg.copy === 'function' - && typeof arg.fill === 'function' - && typeof arg.readUInt8 === 'function'; -} -},{}],30:[function(require,module,exports){ -(function (process,global){(function (){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -var formatRegExp = /%[sdj%]/g; -exports.format = function(f) { - if (!isString(f)) { - var objects = []; - for (var i = 0; i < arguments.length; i++) { - objects.push(inspect(arguments[i])); - } - return objects.join(' '); - } - - var i = 1; - var args = arguments; - var len = args.length; - var str = String(f).replace(formatRegExp, function(x) { - if (x === '%%') return '%'; - if (i >= len) return x; - switch (x) { - case '%s': return String(args[i++]); - case '%d': return Number(args[i++]); - case '%j': - try { - return JSON.stringify(args[i++]); - } catch (_) { - return '[Circular]'; - } - default: - return x; - } - }); - for (var x = args[i]; i < len; x = args[++i]) { - if (isNull(x) || !isObject(x)) { - str += ' ' + x; - } else { - str += ' ' + inspect(x); - } - } - return str; -}; - - -// Mark that a method should not be used. -// Returns a modified function which warns once by default. -// If --no-deprecation is set, then it is a no-op. -exports.deprecate = function(fn, msg) { - // Allow for deprecating things in the process of starting up. - if (isUndefined(global.process)) { - return function() { - return exports.deprecate(fn, msg).apply(this, arguments); - }; - } - - if (process.noDeprecation === true) { - return fn; - } - - var warned = false; - function deprecated() { - if (!warned) { - if (process.throwDeprecation) { - throw new Error(msg); - } else if (process.traceDeprecation) { - console.trace(msg); - } else { - console.error(msg); - } - warned = true; - } - return fn.apply(this, arguments); - } - - return deprecated; -}; - - -var debugs = {}; -var debugEnviron; -exports.debuglog = function(set) { - if (isUndefined(debugEnviron)) - debugEnviron = process.env.NODE_DEBUG || ''; - set = set.toUpperCase(); - if (!debugs[set]) { - if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) { - var pid = process.pid; - debugs[set] = function() { - var msg = exports.format.apply(exports, arguments); - console.error('%s %d: %s', set, pid, msg); - }; - } else { - debugs[set] = function() {}; - } - } - return debugs[set]; -}; - - -/** - * Echos the value of a value. Trys to print the value out - * in the best way possible given the different types. - * - * @param {Object} obj The object to print out. - * @param {Object} opts Optional options object that alters the output. - */ -/* legacy: obj, showHidden, depth, colors*/ -function inspect(obj, opts) { - // default options - var ctx = { - seen: [], - stylize: stylizeNoColor - }; - // legacy... - if (arguments.length >= 3) ctx.depth = arguments[2]; - if (arguments.length >= 4) ctx.colors = arguments[3]; - if (isBoolean(opts)) { - // legacy... - ctx.showHidden = opts; - } else if (opts) { - // got an "options" object - exports._extend(ctx, opts); - } - // set default options - if (isUndefined(ctx.showHidden)) ctx.showHidden = false; - if (isUndefined(ctx.depth)) ctx.depth = 2; - if (isUndefined(ctx.colors)) ctx.colors = false; - if (isUndefined(ctx.customInspect)) ctx.customInspect = true; - if (ctx.colors) ctx.stylize = stylizeWithColor; - return formatValue(ctx, obj, ctx.depth); -} -exports.inspect = inspect; - - -// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics -inspect.colors = { - 'bold' : [1, 22], - 'italic' : [3, 23], - 'underline' : [4, 24], - 'inverse' : [7, 27], - 'white' : [37, 39], - 'grey' : [90, 39], - 'black' : [30, 39], - 'blue' : [34, 39], - 'cyan' : [36, 39], - 'green' : [32, 39], - 'magenta' : [35, 39], - 'red' : [31, 39], - 'yellow' : [33, 39] -}; - -// Don't use 'blue' not visible on cmd.exe -inspect.styles = { - 'special': 'cyan', - 'number': 'yellow', - 'boolean': 'yellow', - 'undefined': 'grey', - 'null': 'bold', - 'string': 'green', - 'date': 'magenta', - // "name": intentionally not styling - 'regexp': 'red' -}; - - -function stylizeWithColor(str, styleType) { - var style = inspect.styles[styleType]; - - if (style) { - return '\u001b[' + inspect.colors[style][0] + 'm' + str + - '\u001b[' + inspect.colors[style][1] + 'm'; - } else { - return str; - } -} - - -function stylizeNoColor(str, styleType) { - return str; -} - - -function arrayToHash(array) { - var hash = {}; - - array.forEach(function(val, idx) { - hash[val] = true; - }); - - return hash; -} - - -function formatValue(ctx, value, recurseTimes) { - // Provide a hook for user-specified inspect functions. - // Check that value is an object with an inspect function on it - if (ctx.customInspect && - value && - isFunction(value.inspect) && - // Filter out the util module, it's inspect function is special - value.inspect !== exports.inspect && - // Also filter out any prototype objects using the circular check. - !(value.constructor && value.constructor.prototype === value)) { - var ret = value.inspect(recurseTimes, ctx); - if (!isString(ret)) { - ret = formatValue(ctx, ret, recurseTimes); - } - return ret; - } - - // Primitive types cannot have properties - var primitive = formatPrimitive(ctx, value); - if (primitive) { - return primitive; - } - - // Look up the keys of the object. - var keys = Object.keys(value); - var visibleKeys = arrayToHash(keys); - - if (ctx.showHidden) { - keys = Object.getOwnPropertyNames(value); - } - - // IE doesn't make error fields non-enumerable - // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx - if (isError(value) - && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) { - return formatError(value); - } - - // Some type of object without properties can be shortcutted. - if (keys.length === 0) { - if (isFunction(value)) { - var name = value.name ? ': ' + value.name : ''; - return ctx.stylize('[Function' + name + ']', 'special'); - } - if (isRegExp(value)) { - return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); - } - if (isDate(value)) { - return ctx.stylize(Date.prototype.toString.call(value), 'date'); - } - if (isError(value)) { - return formatError(value); - } - } - - var base = '', array = false, braces = ['{', '}']; - - // Make Array say that they are Array - if (isArray(value)) { - array = true; - braces = ['[', ']']; - } - - // Make functions say that they are functions - if (isFunction(value)) { - var n = value.name ? ': ' + value.name : ''; - base = ' [Function' + n + ']'; - } - - // Make RegExps say that they are RegExps - if (isRegExp(value)) { - base = ' ' + RegExp.prototype.toString.call(value); - } - - // Make dates with properties first say the date - if (isDate(value)) { - base = ' ' + Date.prototype.toUTCString.call(value); - } - - // Make error with message first say the error - if (isError(value)) { - base = ' ' + formatError(value); - } - - if (keys.length === 0 && (!array || value.length == 0)) { - return braces[0] + base + braces[1]; - } - - if (recurseTimes < 0) { - if (isRegExp(value)) { - return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); - } else { - return ctx.stylize('[Object]', 'special'); - } - } - - ctx.seen.push(value); - - var output; - if (array) { - output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); - } else { - output = keys.map(function(key) { - return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); - }); - } - - ctx.seen.pop(); - - return reduceToSingleString(output, base, braces); -} - - -function formatPrimitive(ctx, value) { - if (isUndefined(value)) - return ctx.stylize('undefined', 'undefined'); - if (isString(value)) { - var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') - .replace(/'/g, "\\'") - .replace(/\\"/g, '"') + '\''; - return ctx.stylize(simple, 'string'); - } - if (isNumber(value)) - return ctx.stylize('' + value, 'number'); - if (isBoolean(value)) - return ctx.stylize('' + value, 'boolean'); - // For some reason typeof null is "object", so special case here. - if (isNull(value)) - return ctx.stylize('null', 'null'); -} - - -function formatError(value) { - return '[' + Error.prototype.toString.call(value) + ']'; -} - - -function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { - var output = []; - for (var i = 0, l = value.length; i < l; ++i) { - if (hasOwnProperty(value, String(i))) { - output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, - String(i), true)); - } else { - output.push(''); - } - } - keys.forEach(function(key) { - if (!key.match(/^\d+$/)) { - output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, - key, true)); - } - }); - return output; -} - - -function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { - var name, str, desc; - desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; - if (desc.get) { - if (desc.set) { - str = ctx.stylize('[Getter/Setter]', 'special'); - } else { - str = ctx.stylize('[Getter]', 'special'); - } - } else { - if (desc.set) { - str = ctx.stylize('[Setter]', 'special'); - } - } - if (!hasOwnProperty(visibleKeys, key)) { - name = '[' + key + ']'; - } - if (!str) { - if (ctx.seen.indexOf(desc.value) < 0) { - if (isNull(recurseTimes)) { - str = formatValue(ctx, desc.value, null); - } else { - str = formatValue(ctx, desc.value, recurseTimes - 1); - } - if (str.indexOf('\n') > -1) { - if (array) { - str = str.split('\n').map(function(line) { - return ' ' + line; - }).join('\n').substr(2); - } else { - str = '\n' + str.split('\n').map(function(line) { - return ' ' + line; - }).join('\n'); - } - } - } else { - str = ctx.stylize('[Circular]', 'special'); - } - } - if (isUndefined(name)) { - if (array && key.match(/^\d+$/)) { - return str; - } - name = JSON.stringify('' + key); - if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { - name = name.substr(1, name.length - 2); - name = ctx.stylize(name, 'name'); - } else { - name = name.replace(/'/g, "\\'") - .replace(/\\"/g, '"') - .replace(/(^"|"$)/g, "'"); - name = ctx.stylize(name, 'string'); - } - } - - return name + ': ' + str; -} - - -function reduceToSingleString(output, base, braces) { - var numLinesEst = 0; - var length = output.reduce(function(prev, cur) { - numLinesEst++; - if (cur.indexOf('\n') >= 0) numLinesEst++; - return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; - }, 0); - - if (length > 60) { - return braces[0] + - (base === '' ? '' : base + '\n ') + - ' ' + - output.join(',\n ') + - ' ' + - braces[1]; - } - - return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; -} - - -// NOTE: These type checking functions intentionally don't use `instanceof` -// because it is fragile and can be easily faked with `Object.create()`. -function isArray(ar) { - return Array.isArray(ar); -} -exports.isArray = isArray; - -function isBoolean(arg) { - return typeof arg === 'boolean'; -} -exports.isBoolean = isBoolean; - -function isNull(arg) { - return arg === null; -} -exports.isNull = isNull; - -function isNullOrUndefined(arg) { - return arg == null; -} -exports.isNullOrUndefined = isNullOrUndefined; - -function isNumber(arg) { - return typeof arg === 'number'; -} -exports.isNumber = isNumber; - -function isString(arg) { - return typeof arg === 'string'; -} -exports.isString = isString; - -function isSymbol(arg) { - return typeof arg === 'symbol'; -} -exports.isSymbol = isSymbol; - -function isUndefined(arg) { - return arg === void 0; -} -exports.isUndefined = isUndefined; - -function isRegExp(re) { - return isObject(re) && objectToString(re) === '[object RegExp]'; -} -exports.isRegExp = isRegExp; - -function isObject(arg) { - return typeof arg === 'object' && arg !== null; -} -exports.isObject = isObject; - -function isDate(d) { - return isObject(d) && objectToString(d) === '[object Date]'; -} -exports.isDate = isDate; - -function isError(e) { - return isObject(e) && - (objectToString(e) === '[object Error]' || e instanceof Error); -} -exports.isError = isError; - -function isFunction(arg) { - return typeof arg === 'function'; -} -exports.isFunction = isFunction; - -function isPrimitive(arg) { - return arg === null || - typeof arg === 'boolean' || - typeof arg === 'number' || - typeof arg === 'string' || - typeof arg === 'symbol' || // ES6 symbol - typeof arg === 'undefined'; -} -exports.isPrimitive = isPrimitive; - -exports.isBuffer = require('./support/isBuffer'); - -function objectToString(o) { - return Object.prototype.toString.call(o); -} - - -function pad(n) { - return n < 10 ? '0' + n.toString(10) : n.toString(10); -} - - -var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', - 'Oct', 'Nov', 'Dec']; - -// 26 Feb 16:19:34 -function timestamp() { - var d = new Date(); - var time = [pad(d.getHours()), - pad(d.getMinutes()), - pad(d.getSeconds())].join(':'); - return [d.getDate(), months[d.getMonth()], time].join(' '); -} - - -// log is just a thin wrapper to console.log that prepends a timestamp -exports.log = function() { - console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments)); -}; - - -/** - * Inherit the prototype methods from one constructor into another. - * - * The Function.prototype.inherits from lang.js rewritten as a standalone - * function (not on Function.prototype). NOTE: If this file is to be loaded - * during bootstrapping this function needs to be rewritten using some native - * functions as prototype setup using normal JavaScript does not work as - * expected during bootstrapping (see mirror.js in r114903). - * - * @param {function} ctor Constructor function which needs to inherit the - * prototype. - * @param {function} superCtor Constructor function to inherit prototype from. - */ -exports.inherits = require('inherits'); - -exports._extend = function(origin, add) { - // Don't do anything if add isn't an object - if (!add || !isObject(add)) return origin; - - var keys = Object.keys(add); - var i = keys.length; - while (i--) { - origin[keys[i]] = add[keys[i]]; - } - return origin; -}; - -function hasOwnProperty(obj, prop) { - return Object.prototype.hasOwnProperty.call(obj, prop); -} - -}).call(this)}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./support/isBuffer":29,"_process":199,"inherits":28}],31:[function(require,module,exports){ -'use strict' - -exports.byteLength = byteLength -exports.toByteArray = toByteArray -exports.fromByteArray = fromByteArray - -var lookup = [] -var revLookup = [] -var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array - -var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' -for (var i = 0, len = code.length; i < len; ++i) { - lookup[i] = code[i] - revLookup[code.charCodeAt(i)] = i -} - -// Support decoding URL-safe base64 strings, as Node.js does. -// See: https://en.wikipedia.org/wiki/Base64#URL_applications -revLookup['-'.charCodeAt(0)] = 62 -revLookup['_'.charCodeAt(0)] = 63 - -function getLens (b64) { - var len = b64.length - - if (len % 4 > 0) { - throw new Error('Invalid string. Length must be a multiple of 4') - } - - // Trim off extra bytes after placeholder bytes are found - // See: https://github.com/beatgammit/base64-js/issues/42 - var validLen = b64.indexOf('=') - if (validLen === -1) validLen = len - - var placeHoldersLen = validLen === len - ? 0 - : 4 - (validLen % 4) - - return [validLen, placeHoldersLen] -} - -// base64 is 4/3 + up to two characters of the original data -function byteLength (b64) { - var lens = getLens(b64) - var validLen = lens[0] - var placeHoldersLen = lens[1] - return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen -} - -function _byteLength (b64, validLen, placeHoldersLen) { - return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen -} - -function toByteArray (b64) { - var tmp - var lens = getLens(b64) - var validLen = lens[0] - var placeHoldersLen = lens[1] - - var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen)) - - var curByte = 0 - - // if there are placeholders, only get up to the last complete 4 chars - var len = placeHoldersLen > 0 - ? validLen - 4 - : validLen - - var i - for (i = 0; i < len; i += 4) { - tmp = - (revLookup[b64.charCodeAt(i)] << 18) | - (revLookup[b64.charCodeAt(i + 1)] << 12) | - (revLookup[b64.charCodeAt(i + 2)] << 6) | - revLookup[b64.charCodeAt(i + 3)] - arr[curByte++] = (tmp >> 16) & 0xFF - arr[curByte++] = (tmp >> 8) & 0xFF - arr[curByte++] = tmp & 0xFF - } - - if (placeHoldersLen === 2) { - tmp = - (revLookup[b64.charCodeAt(i)] << 2) | - (revLookup[b64.charCodeAt(i + 1)] >> 4) - arr[curByte++] = tmp & 0xFF - } - - if (placeHoldersLen === 1) { - tmp = - (revLookup[b64.charCodeAt(i)] << 10) | - (revLookup[b64.charCodeAt(i + 1)] << 4) | - (revLookup[b64.charCodeAt(i + 2)] >> 2) - arr[curByte++] = (tmp >> 8) & 0xFF - arr[curByte++] = tmp & 0xFF - } - - return arr -} - -function tripletToBase64 (num) { - return lookup[num >> 18 & 0x3F] + - lookup[num >> 12 & 0x3F] + - lookup[num >> 6 & 0x3F] + - lookup[num & 0x3F] -} - -function encodeChunk (uint8, start, end) { - var tmp - var output = [] - for (var i = start; i < end; i += 3) { - tmp = - ((uint8[i] << 16) & 0xFF0000) + - ((uint8[i + 1] << 8) & 0xFF00) + - (uint8[i + 2] & 0xFF) - output.push(tripletToBase64(tmp)) - } - return output.join('') -} - -function fromByteArray (uint8) { - var tmp - var len = uint8.length - var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes - var parts = [] - var maxChunkLength = 16383 // must be multiple of 3 - - // go through the array every three bytes, we'll deal with trailing stuff later - for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) { - parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength))) - } - - // pad the end with zeros, but make sure to not forget the extra bytes - if (extraBytes === 1) { - tmp = uint8[len - 1] - parts.push( - lookup[tmp >> 2] + - lookup[(tmp << 4) & 0x3F] + - '==' - ) - } else if (extraBytes === 2) { - tmp = (uint8[len - 2] << 8) + uint8[len - 1] - parts.push( - lookup[tmp >> 10] + - lookup[(tmp >> 4) & 0x3F] + - lookup[(tmp << 2) & 0x3F] + - '=' - ) - } - - return parts.join('') -} - -},{}],32:[function(require,module,exports){ -(function (module, exports) { - 'use strict'; - - // Utils - function assert (val, msg) { - if (!val) throw new Error(msg || 'Assertion failed'); - } - - // Could use `inherits` module, but don't want to move from single file - // architecture yet. - function inherits (ctor, superCtor) { - ctor.super_ = superCtor; - var TempCtor = function () {}; - TempCtor.prototype = superCtor.prototype; - ctor.prototype = new TempCtor(); - ctor.prototype.constructor = ctor; - } - - // BN - - function BN (number, base, endian) { - if (BN.isBN(number)) { - return number; - } - - this.negative = 0; - this.words = null; - this.length = 0; - - // Reduction context - this.red = null; - - if (number !== null) { - if (base === 'le' || base === 'be') { - endian = base; - base = 10; - } - - this._init(number || 0, base || 10, endian || 'be'); - } - } - if (typeof module === 'object') { - module.exports = BN; - } else { - exports.BN = BN; - } - - BN.BN = BN; - BN.wordSize = 26; - - var Buffer; - try { - if (typeof window !== 'undefined' && typeof window.Buffer !== 'undefined') { - Buffer = window.Buffer; - } else { - Buffer = require('buffer').Buffer; - } - } catch (e) { - } - - BN.isBN = function isBN (num) { - if (num instanceof BN) { - return true; - } - - return num !== null && typeof num === 'object' && - num.constructor.wordSize === BN.wordSize && Array.isArray(num.words); - }; - - BN.max = function max (left, right) { - if (left.cmp(right) > 0) return left; - return right; - }; - - BN.min = function min (left, right) { - if (left.cmp(right) < 0) return left; - return right; - }; - - BN.prototype._init = function init (number, base, endian) { - if (typeof number === 'number') { - return this._initNumber(number, base, endian); - } - - if (typeof number === 'object') { - return this._initArray(number, base, endian); - } - - if (base === 'hex') { - base = 16; - } - assert(base === (base | 0) && base >= 2 && base <= 36); - - number = number.toString().replace(/\s+/g, ''); - var start = 0; - if (number[0] === '-') { - start++; - this.negative = 1; - } - - if (start < number.length) { - if (base === 16) { - this._parseHex(number, start, endian); - } else { - this._parseBase(number, base, start); - if (endian === 'le') { - this._initArray(this.toArray(), base, endian); - } - } - } - }; - - BN.prototype._initNumber = function _initNumber (number, base, endian) { - if (number < 0) { - this.negative = 1; - number = -number; - } - if (number < 0x4000000) { - this.words = [number & 0x3ffffff]; - this.length = 1; - } else if (number < 0x10000000000000) { - this.words = [ - number & 0x3ffffff, - (number / 0x4000000) & 0x3ffffff - ]; - this.length = 2; - } else { - assert(number < 0x20000000000000); // 2 ^ 53 (unsafe) - this.words = [ - number & 0x3ffffff, - (number / 0x4000000) & 0x3ffffff, - 1 - ]; - this.length = 3; - } - - if (endian !== 'le') return; - - // Reverse the bytes - this._initArray(this.toArray(), base, endian); - }; - - BN.prototype._initArray = function _initArray (number, base, endian) { - // Perhaps a Uint8Array - assert(typeof number.length === 'number'); - if (number.length <= 0) { - this.words = [0]; - this.length = 1; - return this; - } - - this.length = Math.ceil(number.length / 3); - this.words = new Array(this.length); - for (var i = 0; i < this.length; i++) { - this.words[i] = 0; - } - - var j, w; - var off = 0; - if (endian === 'be') { - for (i = number.length - 1, j = 0; i >= 0; i -= 3) { - w = number[i] | (number[i - 1] << 8) | (number[i - 2] << 16); - this.words[j] |= (w << off) & 0x3ffffff; - this.words[j + 1] = (w >>> (26 - off)) & 0x3ffffff; - off += 24; - if (off >= 26) { - off -= 26; - j++; - } - } - } else if (endian === 'le') { - for (i = 0, j = 0; i < number.length; i += 3) { - w = number[i] | (number[i + 1] << 8) | (number[i + 2] << 16); - this.words[j] |= (w << off) & 0x3ffffff; - this.words[j + 1] = (w >>> (26 - off)) & 0x3ffffff; - off += 24; - if (off >= 26) { - off -= 26; - j++; - } - } - } - return this._strip(); - }; - - function parseHex4Bits (string, index) { - var c = string.charCodeAt(index); - // '0' - '9' - if (c >= 48 && c <= 57) { - return c - 48; - // 'A' - 'F' - } else if (c >= 65 && c <= 70) { - return c - 55; - // 'a' - 'f' - } else if (c >= 97 && c <= 102) { - return c - 87; - } else { - assert(false, 'Invalid character in ' + string); - } - } - - function parseHexByte (string, lowerBound, index) { - var r = parseHex4Bits(string, index); - if (index - 1 >= lowerBound) { - r |= parseHex4Bits(string, index - 1) << 4; - } - return r; - } - - BN.prototype._parseHex = function _parseHex (number, start, endian) { - // Create possibly bigger array to ensure that it fits the number - this.length = Math.ceil((number.length - start) / 6); - this.words = new Array(this.length); - for (var i = 0; i < this.length; i++) { - this.words[i] = 0; - } - - // 24-bits chunks - var off = 0; - var j = 0; - - var w; - if (endian === 'be') { - for (i = number.length - 1; i >= start; i -= 2) { - w = parseHexByte(number, start, i) << off; - this.words[j] |= w & 0x3ffffff; - if (off >= 18) { - off -= 18; - j += 1; - this.words[j] |= w >>> 26; - } else { - off += 8; - } - } - } else { - var parseLength = number.length - start; - for (i = parseLength % 2 === 0 ? start + 1 : start; i < number.length; i += 2) { - w = parseHexByte(number, start, i) << off; - this.words[j] |= w & 0x3ffffff; - if (off >= 18) { - off -= 18; - j += 1; - this.words[j] |= w >>> 26; - } else { - off += 8; - } - } - } - - this._strip(); - }; - - function parseBase (str, start, end, mul) { - var r = 0; - var b = 0; - var len = Math.min(str.length, end); - for (var i = start; i < len; i++) { - var c = str.charCodeAt(i) - 48; - - r *= mul; - - // 'a' - if (c >= 49) { - b = c - 49 + 0xa; - - // 'A' - } else if (c >= 17) { - b = c - 17 + 0xa; - - // '0' - '9' - } else { - b = c; - } - assert(c >= 0 && b < mul, 'Invalid character'); - r += b; - } - return r; - } - - BN.prototype._parseBase = function _parseBase (number, base, start) { - // Initialize as zero - this.words = [0]; - this.length = 1; - - // Find length of limb in base - for (var limbLen = 0, limbPow = 1; limbPow <= 0x3ffffff; limbPow *= base) { - limbLen++; - } - limbLen--; - limbPow = (limbPow / base) | 0; - - var total = number.length - start; - var mod = total % limbLen; - var end = Math.min(total, total - mod) + start; - - var word = 0; - for (var i = start; i < end; i += limbLen) { - word = parseBase(number, i, i + limbLen, base); - - this.imuln(limbPow); - if (this.words[0] + word < 0x4000000) { - this.words[0] += word; - } else { - this._iaddn(word); - } - } - - if (mod !== 0) { - var pow = 1; - word = parseBase(number, i, number.length, base); - - for (i = 0; i < mod; i++) { - pow *= base; - } - - this.imuln(pow); - if (this.words[0] + word < 0x4000000) { - this.words[0] += word; - } else { - this._iaddn(word); - } - } - - this._strip(); - }; - - BN.prototype.copy = function copy (dest) { - dest.words = new Array(this.length); - for (var i = 0; i < this.length; i++) { - dest.words[i] = this.words[i]; - } - dest.length = this.length; - dest.negative = this.negative; - dest.red = this.red; - }; - - function move (dest, src) { - dest.words = src.words; - dest.length = src.length; - dest.negative = src.negative; - dest.red = src.red; - } - - BN.prototype._move = function _move (dest) { - move(dest, this); - }; - - BN.prototype.clone = function clone () { - var r = new BN(null); - this.copy(r); - return r; - }; - - BN.prototype._expand = function _expand (size) { - while (this.length < size) { - this.words[this.length++] = 0; - } - return this; - }; - - // Remove leading `0` from `this` - BN.prototype._strip = function strip () { - while (this.length > 1 && this.words[this.length - 1] === 0) { - this.length--; - } - return this._normSign(); - }; - - BN.prototype._normSign = function _normSign () { - // -0 = 0 - if (this.length === 1 && this.words[0] === 0) { - this.negative = 0; - } - return this; - }; - - // Check Symbol.for because not everywhere where Symbol defined - // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol#Browser_compatibility - if (typeof Symbol !== 'undefined' && typeof Symbol.for === 'function') { - try { - BN.prototype[Symbol.for('nodejs.util.inspect.custom')] = inspect; - } catch (e) { - BN.prototype.inspect = inspect; - } - } else { - BN.prototype.inspect = inspect; - } - - function inspect () { - return (this.red ? ''; - } - - /* - - var zeros = []; - var groupSizes = []; - var groupBases = []; - - var s = ''; - var i = -1; - while (++i < BN.wordSize) { - zeros[i] = s; - s += '0'; - } - groupSizes[0] = 0; - groupSizes[1] = 0; - groupBases[0] = 0; - groupBases[1] = 0; - var base = 2 - 1; - while (++base < 36 + 1) { - var groupSize = 0; - var groupBase = 1; - while (groupBase < (1 << BN.wordSize) / base) { - groupBase *= base; - groupSize += 1; - } - groupSizes[base] = groupSize; - groupBases[base] = groupBase; - } - - */ - - var zeros = [ - '', - '0', - '00', - '000', - '0000', - '00000', - '000000', - '0000000', - '00000000', - '000000000', - '0000000000', - '00000000000', - '000000000000', - '0000000000000', - '00000000000000', - '000000000000000', - '0000000000000000', - '00000000000000000', - '000000000000000000', - '0000000000000000000', - '00000000000000000000', - '000000000000000000000', - '0000000000000000000000', - '00000000000000000000000', - '000000000000000000000000', - '0000000000000000000000000' - ]; - - var groupSizes = [ - 0, 0, - 25, 16, 12, 11, 10, 9, 8, - 8, 7, 7, 7, 7, 6, 6, - 6, 6, 6, 6, 6, 5, 5, - 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5 - ]; - - var groupBases = [ - 0, 0, - 33554432, 43046721, 16777216, 48828125, 60466176, 40353607, 16777216, - 43046721, 10000000, 19487171, 35831808, 62748517, 7529536, 11390625, - 16777216, 24137569, 34012224, 47045881, 64000000, 4084101, 5153632, - 6436343, 7962624, 9765625, 11881376, 14348907, 17210368, 20511149, - 24300000, 28629151, 33554432, 39135393, 45435424, 52521875, 60466176 - ]; - - BN.prototype.toString = function toString (base, padding) { - base = base || 10; - padding = padding | 0 || 1; - - var out; - if (base === 16 || base === 'hex') { - out = ''; - var off = 0; - var carry = 0; - for (var i = 0; i < this.length; i++) { - var w = this.words[i]; - var word = (((w << off) | carry) & 0xffffff).toString(16); - carry = (w >>> (24 - off)) & 0xffffff; - off += 2; - if (off >= 26) { - off -= 26; - i--; - } - if (carry !== 0 || i !== this.length - 1) { - out = zeros[6 - word.length] + word + out; - } else { - out = word + out; - } - } - if (carry !== 0) { - out = carry.toString(16) + out; - } - while (out.length % padding !== 0) { - out = '0' + out; - } - if (this.negative !== 0) { - out = '-' + out; - } - return out; - } - - if (base === (base | 0) && base >= 2 && base <= 36) { - // var groupSize = Math.floor(BN.wordSize * Math.LN2 / Math.log(base)); - var groupSize = groupSizes[base]; - // var groupBase = Math.pow(base, groupSize); - var groupBase = groupBases[base]; - out = ''; - var c = this.clone(); - c.negative = 0; - while (!c.isZero()) { - var r = c.modrn(groupBase).toString(base); - c = c.idivn(groupBase); - - if (!c.isZero()) { - out = zeros[groupSize - r.length] + r + out; - } else { - out = r + out; - } - } - if (this.isZero()) { - out = '0' + out; - } - while (out.length % padding !== 0) { - out = '0' + out; - } - if (this.negative !== 0) { - out = '-' + out; - } - return out; - } - - assert(false, 'Base should be between 2 and 36'); - }; - - BN.prototype.toNumber = function toNumber () { - var ret = this.words[0]; - if (this.length === 2) { - ret += this.words[1] * 0x4000000; - } else if (this.length === 3 && this.words[2] === 0x01) { - // NOTE: at this stage it is known that the top bit is set - ret += 0x10000000000000 + (this.words[1] * 0x4000000); - } else if (this.length > 2) { - assert(false, 'Number can only safely store up to 53 bits'); - } - return (this.negative !== 0) ? -ret : ret; - }; - - BN.prototype.toJSON = function toJSON () { - return this.toString(16, 2); - }; - - if (Buffer) { - BN.prototype.toBuffer = function toBuffer (endian, length) { - return this.toArrayLike(Buffer, endian, length); - }; - } - - BN.prototype.toArray = function toArray (endian, length) { - return this.toArrayLike(Array, endian, length); - }; - - var allocate = function allocate (ArrayType, size) { - if (ArrayType.allocUnsafe) { - return ArrayType.allocUnsafe(size); - } - return new ArrayType(size); - }; - - BN.prototype.toArrayLike = function toArrayLike (ArrayType, endian, length) { - this._strip(); - - var byteLength = this.byteLength(); - var reqLength = length || Math.max(1, byteLength); - assert(byteLength <= reqLength, 'byte array longer than desired length'); - assert(reqLength > 0, 'Requested array length <= 0'); - - var res = allocate(ArrayType, reqLength); - var postfix = endian === 'le' ? 'LE' : 'BE'; - this['_toArrayLike' + postfix](res, byteLength); - return res; - }; - - BN.prototype._toArrayLikeLE = function _toArrayLikeLE (res, byteLength) { - var position = 0; - var carry = 0; - - for (var i = 0, shift = 0; i < this.length; i++) { - var word = (this.words[i] << shift) | carry; - - res[position++] = word & 0xff; - if (position < res.length) { - res[position++] = (word >> 8) & 0xff; - } - if (position < res.length) { - res[position++] = (word >> 16) & 0xff; - } - - if (shift === 6) { - if (position < res.length) { - res[position++] = (word >> 24) & 0xff; - } - carry = 0; - shift = 0; - } else { - carry = word >>> 24; - shift += 2; - } - } - - if (position < res.length) { - res[position++] = carry; - - while (position < res.length) { - res[position++] = 0; - } - } - }; - - BN.prototype._toArrayLikeBE = function _toArrayLikeBE (res, byteLength) { - var position = res.length - 1; - var carry = 0; - - for (var i = 0, shift = 0; i < this.length; i++) { - var word = (this.words[i] << shift) | carry; - - res[position--] = word & 0xff; - if (position >= 0) { - res[position--] = (word >> 8) & 0xff; - } - if (position >= 0) { - res[position--] = (word >> 16) & 0xff; - } - - if (shift === 6) { - if (position >= 0) { - res[position--] = (word >> 24) & 0xff; - } - carry = 0; - shift = 0; - } else { - carry = word >>> 24; - shift += 2; - } - } - - if (position >= 0) { - res[position--] = carry; - - while (position >= 0) { - res[position--] = 0; - } - } - }; - - if (Math.clz32) { - BN.prototype._countBits = function _countBits (w) { - return 32 - Math.clz32(w); - }; - } else { - BN.prototype._countBits = function _countBits (w) { - var t = w; - var r = 0; - if (t >= 0x1000) { - r += 13; - t >>>= 13; - } - if (t >= 0x40) { - r += 7; - t >>>= 7; - } - if (t >= 0x8) { - r += 4; - t >>>= 4; - } - if (t >= 0x02) { - r += 2; - t >>>= 2; - } - return r + t; - }; - } - - BN.prototype._zeroBits = function _zeroBits (w) { - // Short-cut - if (w === 0) return 26; - - var t = w; - var r = 0; - if ((t & 0x1fff) === 0) { - r += 13; - t >>>= 13; - } - if ((t & 0x7f) === 0) { - r += 7; - t >>>= 7; - } - if ((t & 0xf) === 0) { - r += 4; - t >>>= 4; - } - if ((t & 0x3) === 0) { - r += 2; - t >>>= 2; - } - if ((t & 0x1) === 0) { - r++; - } - return r; - }; - - // Return number of used bits in a BN - BN.prototype.bitLength = function bitLength () { - var w = this.words[this.length - 1]; - var hi = this._countBits(w); - return (this.length - 1) * 26 + hi; - }; - - function toBitArray (num) { - var w = new Array(num.bitLength()); - - for (var bit = 0; bit < w.length; bit++) { - var off = (bit / 26) | 0; - var wbit = bit % 26; - - w[bit] = (num.words[off] >>> wbit) & 0x01; - } - - return w; - } - - // Number of trailing zero bits - BN.prototype.zeroBits = function zeroBits () { - if (this.isZero()) return 0; - - var r = 0; - for (var i = 0; i < this.length; i++) { - var b = this._zeroBits(this.words[i]); - r += b; - if (b !== 26) break; - } - return r; - }; - - BN.prototype.byteLength = function byteLength () { - return Math.ceil(this.bitLength() / 8); - }; - - BN.prototype.toTwos = function toTwos (width) { - if (this.negative !== 0) { - return this.abs().inotn(width).iaddn(1); - } - return this.clone(); - }; - - BN.prototype.fromTwos = function fromTwos (width) { - if (this.testn(width - 1)) { - return this.notn(width).iaddn(1).ineg(); - } - return this.clone(); - }; - - BN.prototype.isNeg = function isNeg () { - return this.negative !== 0; - }; - - // Return negative clone of `this` - BN.prototype.neg = function neg () { - return this.clone().ineg(); - }; - - BN.prototype.ineg = function ineg () { - if (!this.isZero()) { - this.negative ^= 1; - } - - return this; - }; - - // Or `num` with `this` in-place - BN.prototype.iuor = function iuor (num) { - while (this.length < num.length) { - this.words[this.length++] = 0; - } - - for (var i = 0; i < num.length; i++) { - this.words[i] = this.words[i] | num.words[i]; - } - - return this._strip(); - }; - - BN.prototype.ior = function ior (num) { - assert((this.negative | num.negative) === 0); - return this.iuor(num); - }; - - // Or `num` with `this` - BN.prototype.or = function or (num) { - if (this.length > num.length) return this.clone().ior(num); - return num.clone().ior(this); - }; - - BN.prototype.uor = function uor (num) { - if (this.length > num.length) return this.clone().iuor(num); - return num.clone().iuor(this); - }; - - // And `num` with `this` in-place - BN.prototype.iuand = function iuand (num) { - // b = min-length(num, this) - var b; - if (this.length > num.length) { - b = num; - } else { - b = this; - } - - for (var i = 0; i < b.length; i++) { - this.words[i] = this.words[i] & num.words[i]; - } - - this.length = b.length; - - return this._strip(); - }; - - BN.prototype.iand = function iand (num) { - assert((this.negative | num.negative) === 0); - return this.iuand(num); - }; - - // And `num` with `this` - BN.prototype.and = function and (num) { - if (this.length > num.length) return this.clone().iand(num); - return num.clone().iand(this); - }; - - BN.prototype.uand = function uand (num) { - if (this.length > num.length) return this.clone().iuand(num); - return num.clone().iuand(this); - }; - - // Xor `num` with `this` in-place - BN.prototype.iuxor = function iuxor (num) { - // a.length > b.length - var a; - var b; - if (this.length > num.length) { - a = this; - b = num; - } else { - a = num; - b = this; - } - - for (var i = 0; i < b.length; i++) { - this.words[i] = a.words[i] ^ b.words[i]; - } - - if (this !== a) { - for (; i < a.length; i++) { - this.words[i] = a.words[i]; - } - } - - this.length = a.length; - - return this._strip(); - }; - - BN.prototype.ixor = function ixor (num) { - assert((this.negative | num.negative) === 0); - return this.iuxor(num); - }; - - // Xor `num` with `this` - BN.prototype.xor = function xor (num) { - if (this.length > num.length) return this.clone().ixor(num); - return num.clone().ixor(this); - }; - - BN.prototype.uxor = function uxor (num) { - if (this.length > num.length) return this.clone().iuxor(num); - return num.clone().iuxor(this); - }; - - // Not ``this`` with ``width`` bitwidth - BN.prototype.inotn = function inotn (width) { - assert(typeof width === 'number' && width >= 0); - - var bytesNeeded = Math.ceil(width / 26) | 0; - var bitsLeft = width % 26; - - // Extend the buffer with leading zeroes - this._expand(bytesNeeded); - - if (bitsLeft > 0) { - bytesNeeded--; - } - - // Handle complete words - for (var i = 0; i < bytesNeeded; i++) { - this.words[i] = ~this.words[i] & 0x3ffffff; - } - - // Handle the residue - if (bitsLeft > 0) { - this.words[i] = ~this.words[i] & (0x3ffffff >> (26 - bitsLeft)); - } - - // And remove leading zeroes - return this._strip(); - }; - - BN.prototype.notn = function notn (width) { - return this.clone().inotn(width); - }; - - // Set `bit` of `this` - BN.prototype.setn = function setn (bit, val) { - assert(typeof bit === 'number' && bit >= 0); - - var off = (bit / 26) | 0; - var wbit = bit % 26; - - this._expand(off + 1); - - if (val) { - this.words[off] = this.words[off] | (1 << wbit); - } else { - this.words[off] = this.words[off] & ~(1 << wbit); - } - - return this._strip(); - }; - - // Add `num` to `this` in-place - BN.prototype.iadd = function iadd (num) { - var r; - - // negative + positive - if (this.negative !== 0 && num.negative === 0) { - this.negative = 0; - r = this.isub(num); - this.negative ^= 1; - return this._normSign(); - - // positive + negative - } else if (this.negative === 0 && num.negative !== 0) { - num.negative = 0; - r = this.isub(num); - num.negative = 1; - return r._normSign(); - } - - // a.length > b.length - var a, b; - if (this.length > num.length) { - a = this; - b = num; - } else { - a = num; - b = this; - } - - var carry = 0; - for (var i = 0; i < b.length; i++) { - r = (a.words[i] | 0) + (b.words[i] | 0) + carry; - this.words[i] = r & 0x3ffffff; - carry = r >>> 26; - } - for (; carry !== 0 && i < a.length; i++) { - r = (a.words[i] | 0) + carry; - this.words[i] = r & 0x3ffffff; - carry = r >>> 26; - } - - this.length = a.length; - if (carry !== 0) { - this.words[this.length] = carry; - this.length++; - // Copy the rest of the words - } else if (a !== this) { - for (; i < a.length; i++) { - this.words[i] = a.words[i]; - } - } - - return this; - }; - - // Add `num` to `this` - BN.prototype.add = function add (num) { - var res; - if (num.negative !== 0 && this.negative === 0) { - num.negative = 0; - res = this.sub(num); - num.negative ^= 1; - return res; - } else if (num.negative === 0 && this.negative !== 0) { - this.negative = 0; - res = num.sub(this); - this.negative = 1; - return res; - } - - if (this.length > num.length) return this.clone().iadd(num); - - return num.clone().iadd(this); - }; - - // Subtract `num` from `this` in-place - BN.prototype.isub = function isub (num) { - // this - (-num) = this + num - if (num.negative !== 0) { - num.negative = 0; - var r = this.iadd(num); - num.negative = 1; - return r._normSign(); - - // -this - num = -(this + num) - } else if (this.negative !== 0) { - this.negative = 0; - this.iadd(num); - this.negative = 1; - return this._normSign(); - } - - // At this point both numbers are positive - var cmp = this.cmp(num); - - // Optimization - zeroify - if (cmp === 0) { - this.negative = 0; - this.length = 1; - this.words[0] = 0; - return this; - } - - // a > b - var a, b; - if (cmp > 0) { - a = this; - b = num; - } else { - a = num; - b = this; - } - - var carry = 0; - for (var i = 0; i < b.length; i++) { - r = (a.words[i] | 0) - (b.words[i] | 0) + carry; - carry = r >> 26; - this.words[i] = r & 0x3ffffff; - } - for (; carry !== 0 && i < a.length; i++) { - r = (a.words[i] | 0) + carry; - carry = r >> 26; - this.words[i] = r & 0x3ffffff; - } - - // Copy rest of the words - if (carry === 0 && i < a.length && a !== this) { - for (; i < a.length; i++) { - this.words[i] = a.words[i]; - } - } - - this.length = Math.max(this.length, i); - - if (a !== this) { - this.negative = 1; - } - - return this._strip(); - }; - - // Subtract `num` from `this` - BN.prototype.sub = function sub (num) { - return this.clone().isub(num); - }; - - function smallMulTo (self, num, out) { - out.negative = num.negative ^ self.negative; - var len = (self.length + num.length) | 0; - out.length = len; - len = (len - 1) | 0; - - // Peel one iteration (compiler can't do it, because of code complexity) - var a = self.words[0] | 0; - var b = num.words[0] | 0; - var r = a * b; - - var lo = r & 0x3ffffff; - var carry = (r / 0x4000000) | 0; - out.words[0] = lo; - - for (var k = 1; k < len; k++) { - // Sum all words with the same `i + j = k` and accumulate `ncarry`, - // note that ncarry could be >= 0x3ffffff - var ncarry = carry >>> 26; - var rword = carry & 0x3ffffff; - var maxJ = Math.min(k, num.length - 1); - for (var j = Math.max(0, k - self.length + 1); j <= maxJ; j++) { - var i = (k - j) | 0; - a = self.words[i] | 0; - b = num.words[j] | 0; - r = a * b + rword; - ncarry += (r / 0x4000000) | 0; - rword = r & 0x3ffffff; - } - out.words[k] = rword | 0; - carry = ncarry | 0; - } - if (carry !== 0) { - out.words[k] = carry | 0; - } else { - out.length--; - } - - return out._strip(); - } - - // TODO(indutny): it may be reasonable to omit it for users who don't need - // to work with 256-bit numbers, otherwise it gives 20% improvement for 256-bit - // multiplication (like elliptic secp256k1). - var comb10MulTo = function comb10MulTo (self, num, out) { - var a = self.words; - var b = num.words; - var o = out.words; - var c = 0; - var lo; - var mid; - var hi; - var a0 = a[0] | 0; - var al0 = a0 & 0x1fff; - var ah0 = a0 >>> 13; - var a1 = a[1] | 0; - var al1 = a1 & 0x1fff; - var ah1 = a1 >>> 13; - var a2 = a[2] | 0; - var al2 = a2 & 0x1fff; - var ah2 = a2 >>> 13; - var a3 = a[3] | 0; - var al3 = a3 & 0x1fff; - var ah3 = a3 >>> 13; - var a4 = a[4] | 0; - var al4 = a4 & 0x1fff; - var ah4 = a4 >>> 13; - var a5 = a[5] | 0; - var al5 = a5 & 0x1fff; - var ah5 = a5 >>> 13; - var a6 = a[6] | 0; - var al6 = a6 & 0x1fff; - var ah6 = a6 >>> 13; - var a7 = a[7] | 0; - var al7 = a7 & 0x1fff; - var ah7 = a7 >>> 13; - var a8 = a[8] | 0; - var al8 = a8 & 0x1fff; - var ah8 = a8 >>> 13; - var a9 = a[9] | 0; - var al9 = a9 & 0x1fff; - var ah9 = a9 >>> 13; - var b0 = b[0] | 0; - var bl0 = b0 & 0x1fff; - var bh0 = b0 >>> 13; - var b1 = b[1] | 0; - var bl1 = b1 & 0x1fff; - var bh1 = b1 >>> 13; - var b2 = b[2] | 0; - var bl2 = b2 & 0x1fff; - var bh2 = b2 >>> 13; - var b3 = b[3] | 0; - var bl3 = b3 & 0x1fff; - var bh3 = b3 >>> 13; - var b4 = b[4] | 0; - var bl4 = b4 & 0x1fff; - var bh4 = b4 >>> 13; - var b5 = b[5] | 0; - var bl5 = b5 & 0x1fff; - var bh5 = b5 >>> 13; - var b6 = b[6] | 0; - var bl6 = b6 & 0x1fff; - var bh6 = b6 >>> 13; - var b7 = b[7] | 0; - var bl7 = b7 & 0x1fff; - var bh7 = b7 >>> 13; - var b8 = b[8] | 0; - var bl8 = b8 & 0x1fff; - var bh8 = b8 >>> 13; - var b9 = b[9] | 0; - var bl9 = b9 & 0x1fff; - var bh9 = b9 >>> 13; - - out.negative = self.negative ^ num.negative; - out.length = 19; - /* k = 0 */ - lo = Math.imul(al0, bl0); - mid = Math.imul(al0, bh0); - mid = (mid + Math.imul(ah0, bl0)) | 0; - hi = Math.imul(ah0, bh0); - var w0 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w0 >>> 26)) | 0; - w0 &= 0x3ffffff; - /* k = 1 */ - lo = Math.imul(al1, bl0); - mid = Math.imul(al1, bh0); - mid = (mid + Math.imul(ah1, bl0)) | 0; - hi = Math.imul(ah1, bh0); - lo = (lo + Math.imul(al0, bl1)) | 0; - mid = (mid + Math.imul(al0, bh1)) | 0; - mid = (mid + Math.imul(ah0, bl1)) | 0; - hi = (hi + Math.imul(ah0, bh1)) | 0; - var w1 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w1 >>> 26)) | 0; - w1 &= 0x3ffffff; - /* k = 2 */ - lo = Math.imul(al2, bl0); - mid = Math.imul(al2, bh0); - mid = (mid + Math.imul(ah2, bl0)) | 0; - hi = Math.imul(ah2, bh0); - lo = (lo + Math.imul(al1, bl1)) | 0; - mid = (mid + Math.imul(al1, bh1)) | 0; - mid = (mid + Math.imul(ah1, bl1)) | 0; - hi = (hi + Math.imul(ah1, bh1)) | 0; - lo = (lo + Math.imul(al0, bl2)) | 0; - mid = (mid + Math.imul(al0, bh2)) | 0; - mid = (mid + Math.imul(ah0, bl2)) | 0; - hi = (hi + Math.imul(ah0, bh2)) | 0; - var w2 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w2 >>> 26)) | 0; - w2 &= 0x3ffffff; - /* k = 3 */ - lo = Math.imul(al3, bl0); - mid = Math.imul(al3, bh0); - mid = (mid + Math.imul(ah3, bl0)) | 0; - hi = Math.imul(ah3, bh0); - lo = (lo + Math.imul(al2, bl1)) | 0; - mid = (mid + Math.imul(al2, bh1)) | 0; - mid = (mid + Math.imul(ah2, bl1)) | 0; - hi = (hi + Math.imul(ah2, bh1)) | 0; - lo = (lo + Math.imul(al1, bl2)) | 0; - mid = (mid + Math.imul(al1, bh2)) | 0; - mid = (mid + Math.imul(ah1, bl2)) | 0; - hi = (hi + Math.imul(ah1, bh2)) | 0; - lo = (lo + Math.imul(al0, bl3)) | 0; - mid = (mid + Math.imul(al0, bh3)) | 0; - mid = (mid + Math.imul(ah0, bl3)) | 0; - hi = (hi + Math.imul(ah0, bh3)) | 0; - var w3 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w3 >>> 26)) | 0; - w3 &= 0x3ffffff; - /* k = 4 */ - lo = Math.imul(al4, bl0); - mid = Math.imul(al4, bh0); - mid = (mid + Math.imul(ah4, bl0)) | 0; - hi = Math.imul(ah4, bh0); - lo = (lo + Math.imul(al3, bl1)) | 0; - mid = (mid + Math.imul(al3, bh1)) | 0; - mid = (mid + Math.imul(ah3, bl1)) | 0; - hi = (hi + Math.imul(ah3, bh1)) | 0; - lo = (lo + Math.imul(al2, bl2)) | 0; - mid = (mid + Math.imul(al2, bh2)) | 0; - mid = (mid + Math.imul(ah2, bl2)) | 0; - hi = (hi + Math.imul(ah2, bh2)) | 0; - lo = (lo + Math.imul(al1, bl3)) | 0; - mid = (mid + Math.imul(al1, bh3)) | 0; - mid = (mid + Math.imul(ah1, bl3)) | 0; - hi = (hi + Math.imul(ah1, bh3)) | 0; - lo = (lo + Math.imul(al0, bl4)) | 0; - mid = (mid + Math.imul(al0, bh4)) | 0; - mid = (mid + Math.imul(ah0, bl4)) | 0; - hi = (hi + Math.imul(ah0, bh4)) | 0; - var w4 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w4 >>> 26)) | 0; - w4 &= 0x3ffffff; - /* k = 5 */ - lo = Math.imul(al5, bl0); - mid = Math.imul(al5, bh0); - mid = (mid + Math.imul(ah5, bl0)) | 0; - hi = Math.imul(ah5, bh0); - lo = (lo + Math.imul(al4, bl1)) | 0; - mid = (mid + Math.imul(al4, bh1)) | 0; - mid = (mid + Math.imul(ah4, bl1)) | 0; - hi = (hi + Math.imul(ah4, bh1)) | 0; - lo = (lo + Math.imul(al3, bl2)) | 0; - mid = (mid + Math.imul(al3, bh2)) | 0; - mid = (mid + Math.imul(ah3, bl2)) | 0; - hi = (hi + Math.imul(ah3, bh2)) | 0; - lo = (lo + Math.imul(al2, bl3)) | 0; - mid = (mid + Math.imul(al2, bh3)) | 0; - mid = (mid + Math.imul(ah2, bl3)) | 0; - hi = (hi + Math.imul(ah2, bh3)) | 0; - lo = (lo + Math.imul(al1, bl4)) | 0; - mid = (mid + Math.imul(al1, bh4)) | 0; - mid = (mid + Math.imul(ah1, bl4)) | 0; - hi = (hi + Math.imul(ah1, bh4)) | 0; - lo = (lo + Math.imul(al0, bl5)) | 0; - mid = (mid + Math.imul(al0, bh5)) | 0; - mid = (mid + Math.imul(ah0, bl5)) | 0; - hi = (hi + Math.imul(ah0, bh5)) | 0; - var w5 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w5 >>> 26)) | 0; - w5 &= 0x3ffffff; - /* k = 6 */ - lo = Math.imul(al6, bl0); - mid = Math.imul(al6, bh0); - mid = (mid + Math.imul(ah6, bl0)) | 0; - hi = Math.imul(ah6, bh0); - lo = (lo + Math.imul(al5, bl1)) | 0; - mid = (mid + Math.imul(al5, bh1)) | 0; - mid = (mid + Math.imul(ah5, bl1)) | 0; - hi = (hi + Math.imul(ah5, bh1)) | 0; - lo = (lo + Math.imul(al4, bl2)) | 0; - mid = (mid + Math.imul(al4, bh2)) | 0; - mid = (mid + Math.imul(ah4, bl2)) | 0; - hi = (hi + Math.imul(ah4, bh2)) | 0; - lo = (lo + Math.imul(al3, bl3)) | 0; - mid = (mid + Math.imul(al3, bh3)) | 0; - mid = (mid + Math.imul(ah3, bl3)) | 0; - hi = (hi + Math.imul(ah3, bh3)) | 0; - lo = (lo + Math.imul(al2, bl4)) | 0; - mid = (mid + Math.imul(al2, bh4)) | 0; - mid = (mid + Math.imul(ah2, bl4)) | 0; - hi = (hi + Math.imul(ah2, bh4)) | 0; - lo = (lo + Math.imul(al1, bl5)) | 0; - mid = (mid + Math.imul(al1, bh5)) | 0; - mid = (mid + Math.imul(ah1, bl5)) | 0; - hi = (hi + Math.imul(ah1, bh5)) | 0; - lo = (lo + Math.imul(al0, bl6)) | 0; - mid = (mid + Math.imul(al0, bh6)) | 0; - mid = (mid + Math.imul(ah0, bl6)) | 0; - hi = (hi + Math.imul(ah0, bh6)) | 0; - var w6 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w6 >>> 26)) | 0; - w6 &= 0x3ffffff; - /* k = 7 */ - lo = Math.imul(al7, bl0); - mid = Math.imul(al7, bh0); - mid = (mid + Math.imul(ah7, bl0)) | 0; - hi = Math.imul(ah7, bh0); - lo = (lo + Math.imul(al6, bl1)) | 0; - mid = (mid + Math.imul(al6, bh1)) | 0; - mid = (mid + Math.imul(ah6, bl1)) | 0; - hi = (hi + Math.imul(ah6, bh1)) | 0; - lo = (lo + Math.imul(al5, bl2)) | 0; - mid = (mid + Math.imul(al5, bh2)) | 0; - mid = (mid + Math.imul(ah5, bl2)) | 0; - hi = (hi + Math.imul(ah5, bh2)) | 0; - lo = (lo + Math.imul(al4, bl3)) | 0; - mid = (mid + Math.imul(al4, bh3)) | 0; - mid = (mid + Math.imul(ah4, bl3)) | 0; - hi = (hi + Math.imul(ah4, bh3)) | 0; - lo = (lo + Math.imul(al3, bl4)) | 0; - mid = (mid + Math.imul(al3, bh4)) | 0; - mid = (mid + Math.imul(ah3, bl4)) | 0; - hi = (hi + Math.imul(ah3, bh4)) | 0; - lo = (lo + Math.imul(al2, bl5)) | 0; - mid = (mid + Math.imul(al2, bh5)) | 0; - mid = (mid + Math.imul(ah2, bl5)) | 0; - hi = (hi + Math.imul(ah2, bh5)) | 0; - lo = (lo + Math.imul(al1, bl6)) | 0; - mid = (mid + Math.imul(al1, bh6)) | 0; - mid = (mid + Math.imul(ah1, bl6)) | 0; - hi = (hi + Math.imul(ah1, bh6)) | 0; - lo = (lo + Math.imul(al0, bl7)) | 0; - mid = (mid + Math.imul(al0, bh7)) | 0; - mid = (mid + Math.imul(ah0, bl7)) | 0; - hi = (hi + Math.imul(ah0, bh7)) | 0; - var w7 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w7 >>> 26)) | 0; - w7 &= 0x3ffffff; - /* k = 8 */ - lo = Math.imul(al8, bl0); - mid = Math.imul(al8, bh0); - mid = (mid + Math.imul(ah8, bl0)) | 0; - hi = Math.imul(ah8, bh0); - lo = (lo + Math.imul(al7, bl1)) | 0; - mid = (mid + Math.imul(al7, bh1)) | 0; - mid = (mid + Math.imul(ah7, bl1)) | 0; - hi = (hi + Math.imul(ah7, bh1)) | 0; - lo = (lo + Math.imul(al6, bl2)) | 0; - mid = (mid + Math.imul(al6, bh2)) | 0; - mid = (mid + Math.imul(ah6, bl2)) | 0; - hi = (hi + Math.imul(ah6, bh2)) | 0; - lo = (lo + Math.imul(al5, bl3)) | 0; - mid = (mid + Math.imul(al5, bh3)) | 0; - mid = (mid + Math.imul(ah5, bl3)) | 0; - hi = (hi + Math.imul(ah5, bh3)) | 0; - lo = (lo + Math.imul(al4, bl4)) | 0; - mid = (mid + Math.imul(al4, bh4)) | 0; - mid = (mid + Math.imul(ah4, bl4)) | 0; - hi = (hi + Math.imul(ah4, bh4)) | 0; - lo = (lo + Math.imul(al3, bl5)) | 0; - mid = (mid + Math.imul(al3, bh5)) | 0; - mid = (mid + Math.imul(ah3, bl5)) | 0; - hi = (hi + Math.imul(ah3, bh5)) | 0; - lo = (lo + Math.imul(al2, bl6)) | 0; - mid = (mid + Math.imul(al2, bh6)) | 0; - mid = (mid + Math.imul(ah2, bl6)) | 0; - hi = (hi + Math.imul(ah2, bh6)) | 0; - lo = (lo + Math.imul(al1, bl7)) | 0; - mid = (mid + Math.imul(al1, bh7)) | 0; - mid = (mid + Math.imul(ah1, bl7)) | 0; - hi = (hi + Math.imul(ah1, bh7)) | 0; - lo = (lo + Math.imul(al0, bl8)) | 0; - mid = (mid + Math.imul(al0, bh8)) | 0; - mid = (mid + Math.imul(ah0, bl8)) | 0; - hi = (hi + Math.imul(ah0, bh8)) | 0; - var w8 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w8 >>> 26)) | 0; - w8 &= 0x3ffffff; - /* k = 9 */ - lo = Math.imul(al9, bl0); - mid = Math.imul(al9, bh0); - mid = (mid + Math.imul(ah9, bl0)) | 0; - hi = Math.imul(ah9, bh0); - lo = (lo + Math.imul(al8, bl1)) | 0; - mid = (mid + Math.imul(al8, bh1)) | 0; - mid = (mid + Math.imul(ah8, bl1)) | 0; - hi = (hi + Math.imul(ah8, bh1)) | 0; - lo = (lo + Math.imul(al7, bl2)) | 0; - mid = (mid + Math.imul(al7, bh2)) | 0; - mid = (mid + Math.imul(ah7, bl2)) | 0; - hi = (hi + Math.imul(ah7, bh2)) | 0; - lo = (lo + Math.imul(al6, bl3)) | 0; - mid = (mid + Math.imul(al6, bh3)) | 0; - mid = (mid + Math.imul(ah6, bl3)) | 0; - hi = (hi + Math.imul(ah6, bh3)) | 0; - lo = (lo + Math.imul(al5, bl4)) | 0; - mid = (mid + Math.imul(al5, bh4)) | 0; - mid = (mid + Math.imul(ah5, bl4)) | 0; - hi = (hi + Math.imul(ah5, bh4)) | 0; - lo = (lo + Math.imul(al4, bl5)) | 0; - mid = (mid + Math.imul(al4, bh5)) | 0; - mid = (mid + Math.imul(ah4, bl5)) | 0; - hi = (hi + Math.imul(ah4, bh5)) | 0; - lo = (lo + Math.imul(al3, bl6)) | 0; - mid = (mid + Math.imul(al3, bh6)) | 0; - mid = (mid + Math.imul(ah3, bl6)) | 0; - hi = (hi + Math.imul(ah3, bh6)) | 0; - lo = (lo + Math.imul(al2, bl7)) | 0; - mid = (mid + Math.imul(al2, bh7)) | 0; - mid = (mid + Math.imul(ah2, bl7)) | 0; - hi = (hi + Math.imul(ah2, bh7)) | 0; - lo = (lo + Math.imul(al1, bl8)) | 0; - mid = (mid + Math.imul(al1, bh8)) | 0; - mid = (mid + Math.imul(ah1, bl8)) | 0; - hi = (hi + Math.imul(ah1, bh8)) | 0; - lo = (lo + Math.imul(al0, bl9)) | 0; - mid = (mid + Math.imul(al0, bh9)) | 0; - mid = (mid + Math.imul(ah0, bl9)) | 0; - hi = (hi + Math.imul(ah0, bh9)) | 0; - var w9 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w9 >>> 26)) | 0; - w9 &= 0x3ffffff; - /* k = 10 */ - lo = Math.imul(al9, bl1); - mid = Math.imul(al9, bh1); - mid = (mid + Math.imul(ah9, bl1)) | 0; - hi = Math.imul(ah9, bh1); - lo = (lo + Math.imul(al8, bl2)) | 0; - mid = (mid + Math.imul(al8, bh2)) | 0; - mid = (mid + Math.imul(ah8, bl2)) | 0; - hi = (hi + Math.imul(ah8, bh2)) | 0; - lo = (lo + Math.imul(al7, bl3)) | 0; - mid = (mid + Math.imul(al7, bh3)) | 0; - mid = (mid + Math.imul(ah7, bl3)) | 0; - hi = (hi + Math.imul(ah7, bh3)) | 0; - lo = (lo + Math.imul(al6, bl4)) | 0; - mid = (mid + Math.imul(al6, bh4)) | 0; - mid = (mid + Math.imul(ah6, bl4)) | 0; - hi = (hi + Math.imul(ah6, bh4)) | 0; - lo = (lo + Math.imul(al5, bl5)) | 0; - mid = (mid + Math.imul(al5, bh5)) | 0; - mid = (mid + Math.imul(ah5, bl5)) | 0; - hi = (hi + Math.imul(ah5, bh5)) | 0; - lo = (lo + Math.imul(al4, bl6)) | 0; - mid = (mid + Math.imul(al4, bh6)) | 0; - mid = (mid + Math.imul(ah4, bl6)) | 0; - hi = (hi + Math.imul(ah4, bh6)) | 0; - lo = (lo + Math.imul(al3, bl7)) | 0; - mid = (mid + Math.imul(al3, bh7)) | 0; - mid = (mid + Math.imul(ah3, bl7)) | 0; - hi = (hi + Math.imul(ah3, bh7)) | 0; - lo = (lo + Math.imul(al2, bl8)) | 0; - mid = (mid + Math.imul(al2, bh8)) | 0; - mid = (mid + Math.imul(ah2, bl8)) | 0; - hi = (hi + Math.imul(ah2, bh8)) | 0; - lo = (lo + Math.imul(al1, bl9)) | 0; - mid = (mid + Math.imul(al1, bh9)) | 0; - mid = (mid + Math.imul(ah1, bl9)) | 0; - hi = (hi + Math.imul(ah1, bh9)) | 0; - var w10 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w10 >>> 26)) | 0; - w10 &= 0x3ffffff; - /* k = 11 */ - lo = Math.imul(al9, bl2); - mid = Math.imul(al9, bh2); - mid = (mid + Math.imul(ah9, bl2)) | 0; - hi = Math.imul(ah9, bh2); - lo = (lo + Math.imul(al8, bl3)) | 0; - mid = (mid + Math.imul(al8, bh3)) | 0; - mid = (mid + Math.imul(ah8, bl3)) | 0; - hi = (hi + Math.imul(ah8, bh3)) | 0; - lo = (lo + Math.imul(al7, bl4)) | 0; - mid = (mid + Math.imul(al7, bh4)) | 0; - mid = (mid + Math.imul(ah7, bl4)) | 0; - hi = (hi + Math.imul(ah7, bh4)) | 0; - lo = (lo + Math.imul(al6, bl5)) | 0; - mid = (mid + Math.imul(al6, bh5)) | 0; - mid = (mid + Math.imul(ah6, bl5)) | 0; - hi = (hi + Math.imul(ah6, bh5)) | 0; - lo = (lo + Math.imul(al5, bl6)) | 0; - mid = (mid + Math.imul(al5, bh6)) | 0; - mid = (mid + Math.imul(ah5, bl6)) | 0; - hi = (hi + Math.imul(ah5, bh6)) | 0; - lo = (lo + Math.imul(al4, bl7)) | 0; - mid = (mid + Math.imul(al4, bh7)) | 0; - mid = (mid + Math.imul(ah4, bl7)) | 0; - hi = (hi + Math.imul(ah4, bh7)) | 0; - lo = (lo + Math.imul(al3, bl8)) | 0; - mid = (mid + Math.imul(al3, bh8)) | 0; - mid = (mid + Math.imul(ah3, bl8)) | 0; - hi = (hi + Math.imul(ah3, bh8)) | 0; - lo = (lo + Math.imul(al2, bl9)) | 0; - mid = (mid + Math.imul(al2, bh9)) | 0; - mid = (mid + Math.imul(ah2, bl9)) | 0; - hi = (hi + Math.imul(ah2, bh9)) | 0; - var w11 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w11 >>> 26)) | 0; - w11 &= 0x3ffffff; - /* k = 12 */ - lo = Math.imul(al9, bl3); - mid = Math.imul(al9, bh3); - mid = (mid + Math.imul(ah9, bl3)) | 0; - hi = Math.imul(ah9, bh3); - lo = (lo + Math.imul(al8, bl4)) | 0; - mid = (mid + Math.imul(al8, bh4)) | 0; - mid = (mid + Math.imul(ah8, bl4)) | 0; - hi = (hi + Math.imul(ah8, bh4)) | 0; - lo = (lo + Math.imul(al7, bl5)) | 0; - mid = (mid + Math.imul(al7, bh5)) | 0; - mid = (mid + Math.imul(ah7, bl5)) | 0; - hi = (hi + Math.imul(ah7, bh5)) | 0; - lo = (lo + Math.imul(al6, bl6)) | 0; - mid = (mid + Math.imul(al6, bh6)) | 0; - mid = (mid + Math.imul(ah6, bl6)) | 0; - hi = (hi + Math.imul(ah6, bh6)) | 0; - lo = (lo + Math.imul(al5, bl7)) | 0; - mid = (mid + Math.imul(al5, bh7)) | 0; - mid = (mid + Math.imul(ah5, bl7)) | 0; - hi = (hi + Math.imul(ah5, bh7)) | 0; - lo = (lo + Math.imul(al4, bl8)) | 0; - mid = (mid + Math.imul(al4, bh8)) | 0; - mid = (mid + Math.imul(ah4, bl8)) | 0; - hi = (hi + Math.imul(ah4, bh8)) | 0; - lo = (lo + Math.imul(al3, bl9)) | 0; - mid = (mid + Math.imul(al3, bh9)) | 0; - mid = (mid + Math.imul(ah3, bl9)) | 0; - hi = (hi + Math.imul(ah3, bh9)) | 0; - var w12 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w12 >>> 26)) | 0; - w12 &= 0x3ffffff; - /* k = 13 */ - lo = Math.imul(al9, bl4); - mid = Math.imul(al9, bh4); - mid = (mid + Math.imul(ah9, bl4)) | 0; - hi = Math.imul(ah9, bh4); - lo = (lo + Math.imul(al8, bl5)) | 0; - mid = (mid + Math.imul(al8, bh5)) | 0; - mid = (mid + Math.imul(ah8, bl5)) | 0; - hi = (hi + Math.imul(ah8, bh5)) | 0; - lo = (lo + Math.imul(al7, bl6)) | 0; - mid = (mid + Math.imul(al7, bh6)) | 0; - mid = (mid + Math.imul(ah7, bl6)) | 0; - hi = (hi + Math.imul(ah7, bh6)) | 0; - lo = (lo + Math.imul(al6, bl7)) | 0; - mid = (mid + Math.imul(al6, bh7)) | 0; - mid = (mid + Math.imul(ah6, bl7)) | 0; - hi = (hi + Math.imul(ah6, bh7)) | 0; - lo = (lo + Math.imul(al5, bl8)) | 0; - mid = (mid + Math.imul(al5, bh8)) | 0; - mid = (mid + Math.imul(ah5, bl8)) | 0; - hi = (hi + Math.imul(ah5, bh8)) | 0; - lo = (lo + Math.imul(al4, bl9)) | 0; - mid = (mid + Math.imul(al4, bh9)) | 0; - mid = (mid + Math.imul(ah4, bl9)) | 0; - hi = (hi + Math.imul(ah4, bh9)) | 0; - var w13 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w13 >>> 26)) | 0; - w13 &= 0x3ffffff; - /* k = 14 */ - lo = Math.imul(al9, bl5); - mid = Math.imul(al9, bh5); - mid = (mid + Math.imul(ah9, bl5)) | 0; - hi = Math.imul(ah9, bh5); - lo = (lo + Math.imul(al8, bl6)) | 0; - mid = (mid + Math.imul(al8, bh6)) | 0; - mid = (mid + Math.imul(ah8, bl6)) | 0; - hi = (hi + Math.imul(ah8, bh6)) | 0; - lo = (lo + Math.imul(al7, bl7)) | 0; - mid = (mid + Math.imul(al7, bh7)) | 0; - mid = (mid + Math.imul(ah7, bl7)) | 0; - hi = (hi + Math.imul(ah7, bh7)) | 0; - lo = (lo + Math.imul(al6, bl8)) | 0; - mid = (mid + Math.imul(al6, bh8)) | 0; - mid = (mid + Math.imul(ah6, bl8)) | 0; - hi = (hi + Math.imul(ah6, bh8)) | 0; - lo = (lo + Math.imul(al5, bl9)) | 0; - mid = (mid + Math.imul(al5, bh9)) | 0; - mid = (mid + Math.imul(ah5, bl9)) | 0; - hi = (hi + Math.imul(ah5, bh9)) | 0; - var w14 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w14 >>> 26)) | 0; - w14 &= 0x3ffffff; - /* k = 15 */ - lo = Math.imul(al9, bl6); - mid = Math.imul(al9, bh6); - mid = (mid + Math.imul(ah9, bl6)) | 0; - hi = Math.imul(ah9, bh6); - lo = (lo + Math.imul(al8, bl7)) | 0; - mid = (mid + Math.imul(al8, bh7)) | 0; - mid = (mid + Math.imul(ah8, bl7)) | 0; - hi = (hi + Math.imul(ah8, bh7)) | 0; - lo = (lo + Math.imul(al7, bl8)) | 0; - mid = (mid + Math.imul(al7, bh8)) | 0; - mid = (mid + Math.imul(ah7, bl8)) | 0; - hi = (hi + Math.imul(ah7, bh8)) | 0; - lo = (lo + Math.imul(al6, bl9)) | 0; - mid = (mid + Math.imul(al6, bh9)) | 0; - mid = (mid + Math.imul(ah6, bl9)) | 0; - hi = (hi + Math.imul(ah6, bh9)) | 0; - var w15 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w15 >>> 26)) | 0; - w15 &= 0x3ffffff; - /* k = 16 */ - lo = Math.imul(al9, bl7); - mid = Math.imul(al9, bh7); - mid = (mid + Math.imul(ah9, bl7)) | 0; - hi = Math.imul(ah9, bh7); - lo = (lo + Math.imul(al8, bl8)) | 0; - mid = (mid + Math.imul(al8, bh8)) | 0; - mid = (mid + Math.imul(ah8, bl8)) | 0; - hi = (hi + Math.imul(ah8, bh8)) | 0; - lo = (lo + Math.imul(al7, bl9)) | 0; - mid = (mid + Math.imul(al7, bh9)) | 0; - mid = (mid + Math.imul(ah7, bl9)) | 0; - hi = (hi + Math.imul(ah7, bh9)) | 0; - var w16 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w16 >>> 26)) | 0; - w16 &= 0x3ffffff; - /* k = 17 */ - lo = Math.imul(al9, bl8); - mid = Math.imul(al9, bh8); - mid = (mid + Math.imul(ah9, bl8)) | 0; - hi = Math.imul(ah9, bh8); - lo = (lo + Math.imul(al8, bl9)) | 0; - mid = (mid + Math.imul(al8, bh9)) | 0; - mid = (mid + Math.imul(ah8, bl9)) | 0; - hi = (hi + Math.imul(ah8, bh9)) | 0; - var w17 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w17 >>> 26)) | 0; - w17 &= 0x3ffffff; - /* k = 18 */ - lo = Math.imul(al9, bl9); - mid = Math.imul(al9, bh9); - mid = (mid + Math.imul(ah9, bl9)) | 0; - hi = Math.imul(ah9, bh9); - var w18 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w18 >>> 26)) | 0; - w18 &= 0x3ffffff; - o[0] = w0; - o[1] = w1; - o[2] = w2; - o[3] = w3; - o[4] = w4; - o[5] = w5; - o[6] = w6; - o[7] = w7; - o[8] = w8; - o[9] = w9; - o[10] = w10; - o[11] = w11; - o[12] = w12; - o[13] = w13; - o[14] = w14; - o[15] = w15; - o[16] = w16; - o[17] = w17; - o[18] = w18; - if (c !== 0) { - o[19] = c; - out.length++; - } - return out; - }; - - // Polyfill comb - if (!Math.imul) { - comb10MulTo = smallMulTo; - } - - function bigMulTo (self, num, out) { - out.negative = num.negative ^ self.negative; - out.length = self.length + num.length; - - var carry = 0; - var hncarry = 0; - for (var k = 0; k < out.length - 1; k++) { - // Sum all words with the same `i + j = k` and accumulate `ncarry`, - // note that ncarry could be >= 0x3ffffff - var ncarry = hncarry; - hncarry = 0; - var rword = carry & 0x3ffffff; - var maxJ = Math.min(k, num.length - 1); - for (var j = Math.max(0, k - self.length + 1); j <= maxJ; j++) { - var i = k - j; - var a = self.words[i] | 0; - var b = num.words[j] | 0; - var r = a * b; - - var lo = r & 0x3ffffff; - ncarry = (ncarry + ((r / 0x4000000) | 0)) | 0; - lo = (lo + rword) | 0; - rword = lo & 0x3ffffff; - ncarry = (ncarry + (lo >>> 26)) | 0; - - hncarry += ncarry >>> 26; - ncarry &= 0x3ffffff; - } - out.words[k] = rword; - carry = ncarry; - ncarry = hncarry; - } - if (carry !== 0) { - out.words[k] = carry; - } else { - out.length--; - } - - return out._strip(); - } - - function jumboMulTo (self, num, out) { - // Temporary disable, see https://github.com/indutny/bn.js/issues/211 - // var fftm = new FFTM(); - // return fftm.mulp(self, num, out); - return bigMulTo(self, num, out); - } - - BN.prototype.mulTo = function mulTo (num, out) { - var res; - var len = this.length + num.length; - if (this.length === 10 && num.length === 10) { - res = comb10MulTo(this, num, out); - } else if (len < 63) { - res = smallMulTo(this, num, out); - } else if (len < 1024) { - res = bigMulTo(this, num, out); - } else { - res = jumboMulTo(this, num, out); - } - - return res; - }; - - // Cooley-Tukey algorithm for FFT - // slightly revisited to rely on looping instead of recursion - - function FFTM (x, y) { - this.x = x; - this.y = y; - } - - FFTM.prototype.makeRBT = function makeRBT (N) { - var t = new Array(N); - var l = BN.prototype._countBits(N) - 1; - for (var i = 0; i < N; i++) { - t[i] = this.revBin(i, l, N); - } - - return t; - }; - - // Returns binary-reversed representation of `x` - FFTM.prototype.revBin = function revBin (x, l, N) { - if (x === 0 || x === N - 1) return x; - - var rb = 0; - for (var i = 0; i < l; i++) { - rb |= (x & 1) << (l - i - 1); - x >>= 1; - } - - return rb; - }; - - // Performs "tweedling" phase, therefore 'emulating' - // behaviour of the recursive algorithm - FFTM.prototype.permute = function permute (rbt, rws, iws, rtws, itws, N) { - for (var i = 0; i < N; i++) { - rtws[i] = rws[rbt[i]]; - itws[i] = iws[rbt[i]]; - } - }; - - FFTM.prototype.transform = function transform (rws, iws, rtws, itws, N, rbt) { - this.permute(rbt, rws, iws, rtws, itws, N); - - for (var s = 1; s < N; s <<= 1) { - var l = s << 1; - - var rtwdf = Math.cos(2 * Math.PI / l); - var itwdf = Math.sin(2 * Math.PI / l); - - for (var p = 0; p < N; p += l) { - var rtwdf_ = rtwdf; - var itwdf_ = itwdf; - - for (var j = 0; j < s; j++) { - var re = rtws[p + j]; - var ie = itws[p + j]; - - var ro = rtws[p + j + s]; - var io = itws[p + j + s]; - - var rx = rtwdf_ * ro - itwdf_ * io; - - io = rtwdf_ * io + itwdf_ * ro; - ro = rx; - - rtws[p + j] = re + ro; - itws[p + j] = ie + io; - - rtws[p + j + s] = re - ro; - itws[p + j + s] = ie - io; - - /* jshint maxdepth : false */ - if (j !== l) { - rx = rtwdf * rtwdf_ - itwdf * itwdf_; - - itwdf_ = rtwdf * itwdf_ + itwdf * rtwdf_; - rtwdf_ = rx; - } - } - } - } - }; - - FFTM.prototype.guessLen13b = function guessLen13b (n, m) { - var N = Math.max(m, n) | 1; - var odd = N & 1; - var i = 0; - for (N = N / 2 | 0; N; N = N >>> 1) { - i++; - } - - return 1 << i + 1 + odd; - }; - - FFTM.prototype.conjugate = function conjugate (rws, iws, N) { - if (N <= 1) return; - - for (var i = 0; i < N / 2; i++) { - var t = rws[i]; - - rws[i] = rws[N - i - 1]; - rws[N - i - 1] = t; - - t = iws[i]; - - iws[i] = -iws[N - i - 1]; - iws[N - i - 1] = -t; - } - }; - - FFTM.prototype.normalize13b = function normalize13b (ws, N) { - var carry = 0; - for (var i = 0; i < N / 2; i++) { - var w = Math.round(ws[2 * i + 1] / N) * 0x2000 + - Math.round(ws[2 * i] / N) + - carry; - - ws[i] = w & 0x3ffffff; - - if (w < 0x4000000) { - carry = 0; - } else { - carry = w / 0x4000000 | 0; - } - } - - return ws; - }; - - FFTM.prototype.convert13b = function convert13b (ws, len, rws, N) { - var carry = 0; - for (var i = 0; i < len; i++) { - carry = carry + (ws[i] | 0); - - rws[2 * i] = carry & 0x1fff; carry = carry >>> 13; - rws[2 * i + 1] = carry & 0x1fff; carry = carry >>> 13; - } - - // Pad with zeroes - for (i = 2 * len; i < N; ++i) { - rws[i] = 0; - } - - assert(carry === 0); - assert((carry & ~0x1fff) === 0); - }; - - FFTM.prototype.stub = function stub (N) { - var ph = new Array(N); - for (var i = 0; i < N; i++) { - ph[i] = 0; - } - - return ph; - }; - - FFTM.prototype.mulp = function mulp (x, y, out) { - var N = 2 * this.guessLen13b(x.length, y.length); - - var rbt = this.makeRBT(N); - - var _ = this.stub(N); - - var rws = new Array(N); - var rwst = new Array(N); - var iwst = new Array(N); - - var nrws = new Array(N); - var nrwst = new Array(N); - var niwst = new Array(N); - - var rmws = out.words; - rmws.length = N; - - this.convert13b(x.words, x.length, rws, N); - this.convert13b(y.words, y.length, nrws, N); - - this.transform(rws, _, rwst, iwst, N, rbt); - this.transform(nrws, _, nrwst, niwst, N, rbt); - - for (var i = 0; i < N; i++) { - var rx = rwst[i] * nrwst[i] - iwst[i] * niwst[i]; - iwst[i] = rwst[i] * niwst[i] + iwst[i] * nrwst[i]; - rwst[i] = rx; - } - - this.conjugate(rwst, iwst, N); - this.transform(rwst, iwst, rmws, _, N, rbt); - this.conjugate(rmws, _, N); - this.normalize13b(rmws, N); - - out.negative = x.negative ^ y.negative; - out.length = x.length + y.length; - return out._strip(); - }; - - // Multiply `this` by `num` - BN.prototype.mul = function mul (num) { - var out = new BN(null); - out.words = new Array(this.length + num.length); - return this.mulTo(num, out); - }; - - // Multiply employing FFT - BN.prototype.mulf = function mulf (num) { - var out = new BN(null); - out.words = new Array(this.length + num.length); - return jumboMulTo(this, num, out); - }; - - // In-place Multiplication - BN.prototype.imul = function imul (num) { - return this.clone().mulTo(num, this); - }; - - BN.prototype.imuln = function imuln (num) { - var isNegNum = num < 0; - if (isNegNum) num = -num; - - assert(typeof num === 'number'); - assert(num < 0x4000000); - - // Carry - var carry = 0; - for (var i = 0; i < this.length; i++) { - var w = (this.words[i] | 0) * num; - var lo = (w & 0x3ffffff) + (carry & 0x3ffffff); - carry >>= 26; - carry += (w / 0x4000000) | 0; - // NOTE: lo is 27bit maximum - carry += lo >>> 26; - this.words[i] = lo & 0x3ffffff; - } - - if (carry !== 0) { - this.words[i] = carry; - this.length++; - } - - return isNegNum ? this.ineg() : this; - }; - - BN.prototype.muln = function muln (num) { - return this.clone().imuln(num); - }; - - // `this` * `this` - BN.prototype.sqr = function sqr () { - return this.mul(this); - }; - - // `this` * `this` in-place - BN.prototype.isqr = function isqr () { - return this.imul(this.clone()); - }; - - // Math.pow(`this`, `num`) - BN.prototype.pow = function pow (num) { - var w = toBitArray(num); - if (w.length === 0) return new BN(1); - - // Skip leading zeroes - var res = this; - for (var i = 0; i < w.length; i++, res = res.sqr()) { - if (w[i] !== 0) break; - } - - if (++i < w.length) { - for (var q = res.sqr(); i < w.length; i++, q = q.sqr()) { - if (w[i] === 0) continue; - - res = res.mul(q); - } - } - - return res; - }; - - // Shift-left in-place - BN.prototype.iushln = function iushln (bits) { - assert(typeof bits === 'number' && bits >= 0); - var r = bits % 26; - var s = (bits - r) / 26; - var carryMask = (0x3ffffff >>> (26 - r)) << (26 - r); - var i; - - if (r !== 0) { - var carry = 0; - - for (i = 0; i < this.length; i++) { - var newCarry = this.words[i] & carryMask; - var c = ((this.words[i] | 0) - newCarry) << r; - this.words[i] = c | carry; - carry = newCarry >>> (26 - r); - } - - if (carry) { - this.words[i] = carry; - this.length++; - } - } - - if (s !== 0) { - for (i = this.length - 1; i >= 0; i--) { - this.words[i + s] = this.words[i]; - } - - for (i = 0; i < s; i++) { - this.words[i] = 0; - } - - this.length += s; - } - - return this._strip(); - }; - - BN.prototype.ishln = function ishln (bits) { - // TODO(indutny): implement me - assert(this.negative === 0); - return this.iushln(bits); - }; - - // Shift-right in-place - // NOTE: `hint` is a lowest bit before trailing zeroes - // NOTE: if `extended` is present - it will be filled with destroyed bits - BN.prototype.iushrn = function iushrn (bits, hint, extended) { - assert(typeof bits === 'number' && bits >= 0); - var h; - if (hint) { - h = (hint - (hint % 26)) / 26; - } else { - h = 0; - } - - var r = bits % 26; - var s = Math.min((bits - r) / 26, this.length); - var mask = 0x3ffffff ^ ((0x3ffffff >>> r) << r); - var maskedWords = extended; - - h -= s; - h = Math.max(0, h); - - // Extended mode, copy masked part - if (maskedWords) { - for (var i = 0; i < s; i++) { - maskedWords.words[i] = this.words[i]; - } - maskedWords.length = s; - } - - if (s === 0) { - // No-op, we should not move anything at all - } else if (this.length > s) { - this.length -= s; - for (i = 0; i < this.length; i++) { - this.words[i] = this.words[i + s]; - } - } else { - this.words[0] = 0; - this.length = 1; - } - - var carry = 0; - for (i = this.length - 1; i >= 0 && (carry !== 0 || i >= h); i--) { - var word = this.words[i] | 0; - this.words[i] = (carry << (26 - r)) | (word >>> r); - carry = word & mask; - } - - // Push carried bits as a mask - if (maskedWords && carry !== 0) { - maskedWords.words[maskedWords.length++] = carry; - } - - if (this.length === 0) { - this.words[0] = 0; - this.length = 1; - } - - return this._strip(); - }; - - BN.prototype.ishrn = function ishrn (bits, hint, extended) { - // TODO(indutny): implement me - assert(this.negative === 0); - return this.iushrn(bits, hint, extended); - }; - - // Shift-left - BN.prototype.shln = function shln (bits) { - return this.clone().ishln(bits); - }; - - BN.prototype.ushln = function ushln (bits) { - return this.clone().iushln(bits); - }; - - // Shift-right - BN.prototype.shrn = function shrn (bits) { - return this.clone().ishrn(bits); - }; - - BN.prototype.ushrn = function ushrn (bits) { - return this.clone().iushrn(bits); - }; - - // Test if n bit is set - BN.prototype.testn = function testn (bit) { - assert(typeof bit === 'number' && bit >= 0); - var r = bit % 26; - var s = (bit - r) / 26; - var q = 1 << r; - - // Fast case: bit is much higher than all existing words - if (this.length <= s) return false; - - // Check bit and return - var w = this.words[s]; - - return !!(w & q); - }; - - // Return only lowers bits of number (in-place) - BN.prototype.imaskn = function imaskn (bits) { - assert(typeof bits === 'number' && bits >= 0); - var r = bits % 26; - var s = (bits - r) / 26; - - assert(this.negative === 0, 'imaskn works only with positive numbers'); - - if (this.length <= s) { - return this; - } - - if (r !== 0) { - s++; - } - this.length = Math.min(s, this.length); - - if (r !== 0) { - var mask = 0x3ffffff ^ ((0x3ffffff >>> r) << r); - this.words[this.length - 1] &= mask; - } - - return this._strip(); - }; - - // Return only lowers bits of number - BN.prototype.maskn = function maskn (bits) { - return this.clone().imaskn(bits); - }; - - // Add plain number `num` to `this` - BN.prototype.iaddn = function iaddn (num) { - assert(typeof num === 'number'); - assert(num < 0x4000000); - if (num < 0) return this.isubn(-num); - - // Possible sign change - if (this.negative !== 0) { - if (this.length === 1 && (this.words[0] | 0) <= num) { - this.words[0] = num - (this.words[0] | 0); - this.negative = 0; - return this; - } - - this.negative = 0; - this.isubn(num); - this.negative = 1; - return this; - } - - // Add without checks - return this._iaddn(num); - }; - - BN.prototype._iaddn = function _iaddn (num) { - this.words[0] += num; - - // Carry - for (var i = 0; i < this.length && this.words[i] >= 0x4000000; i++) { - this.words[i] -= 0x4000000; - if (i === this.length - 1) { - this.words[i + 1] = 1; - } else { - this.words[i + 1]++; - } - } - this.length = Math.max(this.length, i + 1); - - return this; - }; - - // Subtract plain number `num` from `this` - BN.prototype.isubn = function isubn (num) { - assert(typeof num === 'number'); - assert(num < 0x4000000); - if (num < 0) return this.iaddn(-num); - - if (this.negative !== 0) { - this.negative = 0; - this.iaddn(num); - this.negative = 1; - return this; - } - - this.words[0] -= num; - - if (this.length === 1 && this.words[0] < 0) { - this.words[0] = -this.words[0]; - this.negative = 1; - } else { - // Carry - for (var i = 0; i < this.length && this.words[i] < 0; i++) { - this.words[i] += 0x4000000; - this.words[i + 1] -= 1; - } - } - - return this._strip(); - }; - - BN.prototype.addn = function addn (num) { - return this.clone().iaddn(num); - }; - - BN.prototype.subn = function subn (num) { - return this.clone().isubn(num); - }; - - BN.prototype.iabs = function iabs () { - this.negative = 0; - - return this; - }; - - BN.prototype.abs = function abs () { - return this.clone().iabs(); - }; - - BN.prototype._ishlnsubmul = function _ishlnsubmul (num, mul, shift) { - var len = num.length + shift; - var i; - - this._expand(len); - - var w; - var carry = 0; - for (i = 0; i < num.length; i++) { - w = (this.words[i + shift] | 0) + carry; - var right = (num.words[i] | 0) * mul; - w -= right & 0x3ffffff; - carry = (w >> 26) - ((right / 0x4000000) | 0); - this.words[i + shift] = w & 0x3ffffff; - } - for (; i < this.length - shift; i++) { - w = (this.words[i + shift] | 0) + carry; - carry = w >> 26; - this.words[i + shift] = w & 0x3ffffff; - } - - if (carry === 0) return this._strip(); - - // Subtraction overflow - assert(carry === -1); - carry = 0; - for (i = 0; i < this.length; i++) { - w = -(this.words[i] | 0) + carry; - carry = w >> 26; - this.words[i] = w & 0x3ffffff; - } - this.negative = 1; - - return this._strip(); - }; - - BN.prototype._wordDiv = function _wordDiv (num, mode) { - var shift = this.length - num.length; - - var a = this.clone(); - var b = num; - - // Normalize - var bhi = b.words[b.length - 1] | 0; - var bhiBits = this._countBits(bhi); - shift = 26 - bhiBits; - if (shift !== 0) { - b = b.ushln(shift); - a.iushln(shift); - bhi = b.words[b.length - 1] | 0; - } - - // Initialize quotient - var m = a.length - b.length; - var q; - - if (mode !== 'mod') { - q = new BN(null); - q.length = m + 1; - q.words = new Array(q.length); - for (var i = 0; i < q.length; i++) { - q.words[i] = 0; - } - } - - var diff = a.clone()._ishlnsubmul(b, 1, m); - if (diff.negative === 0) { - a = diff; - if (q) { - q.words[m] = 1; - } - } - - for (var j = m - 1; j >= 0; j--) { - var qj = (a.words[b.length + j] | 0) * 0x4000000 + - (a.words[b.length + j - 1] | 0); - - // NOTE: (qj / bhi) is (0x3ffffff * 0x4000000 + 0x3ffffff) / 0x2000000 max - // (0x7ffffff) - qj = Math.min((qj / bhi) | 0, 0x3ffffff); - - a._ishlnsubmul(b, qj, j); - while (a.negative !== 0) { - qj--; - a.negative = 0; - a._ishlnsubmul(b, 1, j); - if (!a.isZero()) { - a.negative ^= 1; - } - } - if (q) { - q.words[j] = qj; - } - } - if (q) { - q._strip(); - } - a._strip(); - - // Denormalize - if (mode !== 'div' && shift !== 0) { - a.iushrn(shift); - } - - return { - div: q || null, - mod: a - }; - }; - - // NOTE: 1) `mode` can be set to `mod` to request mod only, - // to `div` to request div only, or be absent to - // request both div & mod - // 2) `positive` is true if unsigned mod is requested - BN.prototype.divmod = function divmod (num, mode, positive) { - assert(!num.isZero()); - - if (this.isZero()) { - return { - div: new BN(0), - mod: new BN(0) - }; - } - - var div, mod, res; - if (this.negative !== 0 && num.negative === 0) { - res = this.neg().divmod(num, mode); - - if (mode !== 'mod') { - div = res.div.neg(); - } - - if (mode !== 'div') { - mod = res.mod.neg(); - if (positive && mod.negative !== 0) { - mod.iadd(num); - } - } - - return { - div: div, - mod: mod - }; - } - - if (this.negative === 0 && num.negative !== 0) { - res = this.divmod(num.neg(), mode); - - if (mode !== 'mod') { - div = res.div.neg(); - } - - return { - div: div, - mod: res.mod - }; - } - - if ((this.negative & num.negative) !== 0) { - res = this.neg().divmod(num.neg(), mode); - - if (mode !== 'div') { - mod = res.mod.neg(); - if (positive && mod.negative !== 0) { - mod.isub(num); - } - } - - return { - div: res.div, - mod: mod - }; - } - - // Both numbers are positive at this point - - // Strip both numbers to approximate shift value - if (num.length > this.length || this.cmp(num) < 0) { - return { - div: new BN(0), - mod: this - }; - } - - // Very short reduction - if (num.length === 1) { - if (mode === 'div') { - return { - div: this.divn(num.words[0]), - mod: null - }; - } - - if (mode === 'mod') { - return { - div: null, - mod: new BN(this.modrn(num.words[0])) - }; - } - - return { - div: this.divn(num.words[0]), - mod: new BN(this.modrn(num.words[0])) - }; - } - - return this._wordDiv(num, mode); - }; - - // Find `this` / `num` - BN.prototype.div = function div (num) { - return this.divmod(num, 'div', false).div; - }; - - // Find `this` % `num` - BN.prototype.mod = function mod (num) { - return this.divmod(num, 'mod', false).mod; - }; - - BN.prototype.umod = function umod (num) { - return this.divmod(num, 'mod', true).mod; - }; - - // Find Round(`this` / `num`) - BN.prototype.divRound = function divRound (num) { - var dm = this.divmod(num); - - // Fast case - exact division - if (dm.mod.isZero()) return dm.div; - - var mod = dm.div.negative !== 0 ? dm.mod.isub(num) : dm.mod; - - var half = num.ushrn(1); - var r2 = num.andln(1); - var cmp = mod.cmp(half); - - // Round down - if (cmp < 0 || (r2 === 1 && cmp === 0)) return dm.div; - - // Round up - return dm.div.negative !== 0 ? dm.div.isubn(1) : dm.div.iaddn(1); - }; - - BN.prototype.modrn = function modrn (num) { - var isNegNum = num < 0; - if (isNegNum) num = -num; - - assert(num <= 0x3ffffff); - var p = (1 << 26) % num; - - var acc = 0; - for (var i = this.length - 1; i >= 0; i--) { - acc = (p * acc + (this.words[i] | 0)) % num; - } - - return isNegNum ? -acc : acc; - }; - - // WARNING: DEPRECATED - BN.prototype.modn = function modn (num) { - return this.modrn(num); - }; - - // In-place division by number - BN.prototype.idivn = function idivn (num) { - var isNegNum = num < 0; - if (isNegNum) num = -num; - - assert(num <= 0x3ffffff); - - var carry = 0; - for (var i = this.length - 1; i >= 0; i--) { - var w = (this.words[i] | 0) + carry * 0x4000000; - this.words[i] = (w / num) | 0; - carry = w % num; - } - - this._strip(); - return isNegNum ? this.ineg() : this; - }; - - BN.prototype.divn = function divn (num) { - return this.clone().idivn(num); - }; - - BN.prototype.egcd = function egcd (p) { - assert(p.negative === 0); - assert(!p.isZero()); - - var x = this; - var y = p.clone(); - - if (x.negative !== 0) { - x = x.umod(p); - } else { - x = x.clone(); - } - - // A * x + B * y = x - var A = new BN(1); - var B = new BN(0); - - // C * x + D * y = y - var C = new BN(0); - var D = new BN(1); - - var g = 0; - - while (x.isEven() && y.isEven()) { - x.iushrn(1); - y.iushrn(1); - ++g; - } - - var yp = y.clone(); - var xp = x.clone(); - - while (!x.isZero()) { - for (var i = 0, im = 1; (x.words[0] & im) === 0 && i < 26; ++i, im <<= 1); - if (i > 0) { - x.iushrn(i); - while (i-- > 0) { - if (A.isOdd() || B.isOdd()) { - A.iadd(yp); - B.isub(xp); - } - - A.iushrn(1); - B.iushrn(1); - } - } - - for (var j = 0, jm = 1; (y.words[0] & jm) === 0 && j < 26; ++j, jm <<= 1); - if (j > 0) { - y.iushrn(j); - while (j-- > 0) { - if (C.isOdd() || D.isOdd()) { - C.iadd(yp); - D.isub(xp); - } - - C.iushrn(1); - D.iushrn(1); - } - } - - if (x.cmp(y) >= 0) { - x.isub(y); - A.isub(C); - B.isub(D); - } else { - y.isub(x); - C.isub(A); - D.isub(B); - } - } - - return { - a: C, - b: D, - gcd: y.iushln(g) - }; - }; - - // This is reduced incarnation of the binary EEA - // above, designated to invert members of the - // _prime_ fields F(p) at a maximal speed - BN.prototype._invmp = function _invmp (p) { - assert(p.negative === 0); - assert(!p.isZero()); - - var a = this; - var b = p.clone(); - - if (a.negative !== 0) { - a = a.umod(p); - } else { - a = a.clone(); - } - - var x1 = new BN(1); - var x2 = new BN(0); - - var delta = b.clone(); - - while (a.cmpn(1) > 0 && b.cmpn(1) > 0) { - for (var i = 0, im = 1; (a.words[0] & im) === 0 && i < 26; ++i, im <<= 1); - if (i > 0) { - a.iushrn(i); - while (i-- > 0) { - if (x1.isOdd()) { - x1.iadd(delta); - } - - x1.iushrn(1); - } - } - - for (var j = 0, jm = 1; (b.words[0] & jm) === 0 && j < 26; ++j, jm <<= 1); - if (j > 0) { - b.iushrn(j); - while (j-- > 0) { - if (x2.isOdd()) { - x2.iadd(delta); - } - - x2.iushrn(1); - } - } - - if (a.cmp(b) >= 0) { - a.isub(b); - x1.isub(x2); - } else { - b.isub(a); - x2.isub(x1); - } - } - - var res; - if (a.cmpn(1) === 0) { - res = x1; - } else { - res = x2; - } - - if (res.cmpn(0) < 0) { - res.iadd(p); - } - - return res; - }; - - BN.prototype.gcd = function gcd (num) { - if (this.isZero()) return num.abs(); - if (num.isZero()) return this.abs(); - - var a = this.clone(); - var b = num.clone(); - a.negative = 0; - b.negative = 0; - - // Remove common factor of two - for (var shift = 0; a.isEven() && b.isEven(); shift++) { - a.iushrn(1); - b.iushrn(1); - } - - do { - while (a.isEven()) { - a.iushrn(1); - } - while (b.isEven()) { - b.iushrn(1); - } - - var r = a.cmp(b); - if (r < 0) { - // Swap `a` and `b` to make `a` always bigger than `b` - var t = a; - a = b; - b = t; - } else if (r === 0 || b.cmpn(1) === 0) { - break; - } - - a.isub(b); - } while (true); - - return b.iushln(shift); - }; - - // Invert number in the field F(num) - BN.prototype.invm = function invm (num) { - return this.egcd(num).a.umod(num); - }; - - BN.prototype.isEven = function isEven () { - return (this.words[0] & 1) === 0; - }; - - BN.prototype.isOdd = function isOdd () { - return (this.words[0] & 1) === 1; - }; - - // And first word and num - BN.prototype.andln = function andln (num) { - return this.words[0] & num; - }; - - // Increment at the bit position in-line - BN.prototype.bincn = function bincn (bit) { - assert(typeof bit === 'number'); - var r = bit % 26; - var s = (bit - r) / 26; - var q = 1 << r; - - // Fast case: bit is much higher than all existing words - if (this.length <= s) { - this._expand(s + 1); - this.words[s] |= q; - return this; - } - - // Add bit and propagate, if needed - var carry = q; - for (var i = s; carry !== 0 && i < this.length; i++) { - var w = this.words[i] | 0; - w += carry; - carry = w >>> 26; - w &= 0x3ffffff; - this.words[i] = w; - } - if (carry !== 0) { - this.words[i] = carry; - this.length++; - } - return this; - }; - - BN.prototype.isZero = function isZero () { - return this.length === 1 && this.words[0] === 0; - }; - - BN.prototype.cmpn = function cmpn (num) { - var negative = num < 0; - - if (this.negative !== 0 && !negative) return -1; - if (this.negative === 0 && negative) return 1; - - this._strip(); - - var res; - if (this.length > 1) { - res = 1; - } else { - if (negative) { - num = -num; - } - - assert(num <= 0x3ffffff, 'Number is too big'); - - var w = this.words[0] | 0; - res = w === num ? 0 : w < num ? -1 : 1; - } - if (this.negative !== 0) return -res | 0; - return res; - }; - - // Compare two numbers and return: - // 1 - if `this` > `num` - // 0 - if `this` == `num` - // -1 - if `this` < `num` - BN.prototype.cmp = function cmp (num) { - if (this.negative !== 0 && num.negative === 0) return -1; - if (this.negative === 0 && num.negative !== 0) return 1; - - var res = this.ucmp(num); - if (this.negative !== 0) return -res | 0; - return res; - }; - - // Unsigned comparison - BN.prototype.ucmp = function ucmp (num) { - // At this point both numbers have the same sign - if (this.length > num.length) return 1; - if (this.length < num.length) return -1; - - var res = 0; - for (var i = this.length - 1; i >= 0; i--) { - var a = this.words[i] | 0; - var b = num.words[i] | 0; - - if (a === b) continue; - if (a < b) { - res = -1; - } else if (a > b) { - res = 1; - } - break; - } - return res; - }; - - BN.prototype.gtn = function gtn (num) { - return this.cmpn(num) === 1; - }; - - BN.prototype.gt = function gt (num) { - return this.cmp(num) === 1; - }; - - BN.prototype.gten = function gten (num) { - return this.cmpn(num) >= 0; - }; - - BN.prototype.gte = function gte (num) { - return this.cmp(num) >= 0; - }; - - BN.prototype.ltn = function ltn (num) { - return this.cmpn(num) === -1; - }; - - BN.prototype.lt = function lt (num) { - return this.cmp(num) === -1; - }; - - BN.prototype.lten = function lten (num) { - return this.cmpn(num) <= 0; - }; - - BN.prototype.lte = function lte (num) { - return this.cmp(num) <= 0; - }; - - BN.prototype.eqn = function eqn (num) { - return this.cmpn(num) === 0; - }; - - BN.prototype.eq = function eq (num) { - return this.cmp(num) === 0; - }; - - // - // A reduce context, could be using montgomery or something better, depending - // on the `m` itself. - // - BN.red = function red (num) { - return new Red(num); - }; - - BN.prototype.toRed = function toRed (ctx) { - assert(!this.red, 'Already a number in reduction context'); - assert(this.negative === 0, 'red works only with positives'); - return ctx.convertTo(this)._forceRed(ctx); - }; - - BN.prototype.fromRed = function fromRed () { - assert(this.red, 'fromRed works only with numbers in reduction context'); - return this.red.convertFrom(this); - }; - - BN.prototype._forceRed = function _forceRed (ctx) { - this.red = ctx; - return this; - }; - - BN.prototype.forceRed = function forceRed (ctx) { - assert(!this.red, 'Already a number in reduction context'); - return this._forceRed(ctx); - }; - - BN.prototype.redAdd = function redAdd (num) { - assert(this.red, 'redAdd works only with red numbers'); - return this.red.add(this, num); - }; - - BN.prototype.redIAdd = function redIAdd (num) { - assert(this.red, 'redIAdd works only with red numbers'); - return this.red.iadd(this, num); - }; - - BN.prototype.redSub = function redSub (num) { - assert(this.red, 'redSub works only with red numbers'); - return this.red.sub(this, num); - }; - - BN.prototype.redISub = function redISub (num) { - assert(this.red, 'redISub works only with red numbers'); - return this.red.isub(this, num); - }; - - BN.prototype.redShl = function redShl (num) { - assert(this.red, 'redShl works only with red numbers'); - return this.red.shl(this, num); - }; - - BN.prototype.redMul = function redMul (num) { - assert(this.red, 'redMul works only with red numbers'); - this.red._verify2(this, num); - return this.red.mul(this, num); - }; - - BN.prototype.redIMul = function redIMul (num) { - assert(this.red, 'redMul works only with red numbers'); - this.red._verify2(this, num); - return this.red.imul(this, num); - }; - - BN.prototype.redSqr = function redSqr () { - assert(this.red, 'redSqr works only with red numbers'); - this.red._verify1(this); - return this.red.sqr(this); - }; - - BN.prototype.redISqr = function redISqr () { - assert(this.red, 'redISqr works only with red numbers'); - this.red._verify1(this); - return this.red.isqr(this); - }; - - // Square root over p - BN.prototype.redSqrt = function redSqrt () { - assert(this.red, 'redSqrt works only with red numbers'); - this.red._verify1(this); - return this.red.sqrt(this); - }; - - BN.prototype.redInvm = function redInvm () { - assert(this.red, 'redInvm works only with red numbers'); - this.red._verify1(this); - return this.red.invm(this); - }; - - // Return negative clone of `this` % `red modulo` - BN.prototype.redNeg = function redNeg () { - assert(this.red, 'redNeg works only with red numbers'); - this.red._verify1(this); - return this.red.neg(this); - }; - - BN.prototype.redPow = function redPow (num) { - assert(this.red && !num.red, 'redPow(normalNum)'); - this.red._verify1(this); - return this.red.pow(this, num); - }; - - // Prime numbers with efficient reduction - var primes = { - k256: null, - p224: null, - p192: null, - p25519: null - }; - - // Pseudo-Mersenne prime - function MPrime (name, p) { - // P = 2 ^ N - K - this.name = name; - this.p = new BN(p, 16); - this.n = this.p.bitLength(); - this.k = new BN(1).iushln(this.n).isub(this.p); - - this.tmp = this._tmp(); - } - - MPrime.prototype._tmp = function _tmp () { - var tmp = new BN(null); - tmp.words = new Array(Math.ceil(this.n / 13)); - return tmp; - }; - - MPrime.prototype.ireduce = function ireduce (num) { - // Assumes that `num` is less than `P^2` - // num = HI * (2 ^ N - K) + HI * K + LO = HI * K + LO (mod P) - var r = num; - var rlen; - - do { - this.split(r, this.tmp); - r = this.imulK(r); - r = r.iadd(this.tmp); - rlen = r.bitLength(); - } while (rlen > this.n); - - var cmp = rlen < this.n ? -1 : r.ucmp(this.p); - if (cmp === 0) { - r.words[0] = 0; - r.length = 1; - } else if (cmp > 0) { - r.isub(this.p); - } else { - if (r.strip !== undefined) { - // r is a BN v4 instance - r.strip(); - } else { - // r is a BN v5 instance - r._strip(); - } - } - - return r; - }; - - MPrime.prototype.split = function split (input, out) { - input.iushrn(this.n, 0, out); - }; - - MPrime.prototype.imulK = function imulK (num) { - return num.imul(this.k); - }; - - function K256 () { - MPrime.call( - this, - 'k256', - 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f'); - } - inherits(K256, MPrime); - - K256.prototype.split = function split (input, output) { - // 256 = 9 * 26 + 22 - var mask = 0x3fffff; - - var outLen = Math.min(input.length, 9); - for (var i = 0; i < outLen; i++) { - output.words[i] = input.words[i]; - } - output.length = outLen; - - if (input.length <= 9) { - input.words[0] = 0; - input.length = 1; - return; - } - - // Shift by 9 limbs - var prev = input.words[9]; - output.words[output.length++] = prev & mask; - - for (i = 10; i < input.length; i++) { - var next = input.words[i] | 0; - input.words[i - 10] = ((next & mask) << 4) | (prev >>> 22); - prev = next; - } - prev >>>= 22; - input.words[i - 10] = prev; - if (prev === 0 && input.length > 10) { - input.length -= 10; - } else { - input.length -= 9; - } - }; - - K256.prototype.imulK = function imulK (num) { - // K = 0x1000003d1 = [ 0x40, 0x3d1 ] - num.words[num.length] = 0; - num.words[num.length + 1] = 0; - num.length += 2; - - // bounded at: 0x40 * 0x3ffffff + 0x3d0 = 0x100000390 - var lo = 0; - for (var i = 0; i < num.length; i++) { - var w = num.words[i] | 0; - lo += w * 0x3d1; - num.words[i] = lo & 0x3ffffff; - lo = w * 0x40 + ((lo / 0x4000000) | 0); - } - - // Fast length reduction - if (num.words[num.length - 1] === 0) { - num.length--; - if (num.words[num.length - 1] === 0) { - num.length--; - } - } - return num; - }; - - function P224 () { - MPrime.call( - this, - 'p224', - 'ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001'); - } - inherits(P224, MPrime); - - function P192 () { - MPrime.call( - this, - 'p192', - 'ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff'); - } - inherits(P192, MPrime); - - function P25519 () { - // 2 ^ 255 - 19 - MPrime.call( - this, - '25519', - '7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed'); - } - inherits(P25519, MPrime); - - P25519.prototype.imulK = function imulK (num) { - // K = 0x13 - var carry = 0; - for (var i = 0; i < num.length; i++) { - var hi = (num.words[i] | 0) * 0x13 + carry; - var lo = hi & 0x3ffffff; - hi >>>= 26; - - num.words[i] = lo; - carry = hi; - } - if (carry !== 0) { - num.words[num.length++] = carry; - } - return num; - }; - - // Exported mostly for testing purposes, use plain name instead - BN._prime = function prime (name) { - // Cached version of prime - if (primes[name]) return primes[name]; - - var prime; - if (name === 'k256') { - prime = new K256(); - } else if (name === 'p224') { - prime = new P224(); - } else if (name === 'p192') { - prime = new P192(); - } else if (name === 'p25519') { - prime = new P25519(); - } else { - throw new Error('Unknown prime ' + name); - } - primes[name] = prime; - - return prime; - }; - - // - // Base reduction engine - // - function Red (m) { - if (typeof m === 'string') { - var prime = BN._prime(m); - this.m = prime.p; - this.prime = prime; - } else { - assert(m.gtn(1), 'modulus must be greater than 1'); - this.m = m; - this.prime = null; - } - } - - Red.prototype._verify1 = function _verify1 (a) { - assert(a.negative === 0, 'red works only with positives'); - assert(a.red, 'red works only with red numbers'); - }; - - Red.prototype._verify2 = function _verify2 (a, b) { - assert((a.negative | b.negative) === 0, 'red works only with positives'); - assert(a.red && a.red === b.red, - 'red works only with red numbers'); - }; - - Red.prototype.imod = function imod (a) { - if (this.prime) return this.prime.ireduce(a)._forceRed(this); - - move(a, a.umod(this.m)._forceRed(this)); - return a; - }; - - Red.prototype.neg = function neg (a) { - if (a.isZero()) { - return a.clone(); - } - - return this.m.sub(a)._forceRed(this); - }; - - Red.prototype.add = function add (a, b) { - this._verify2(a, b); - - var res = a.add(b); - if (res.cmp(this.m) >= 0) { - res.isub(this.m); - } - return res._forceRed(this); - }; - - Red.prototype.iadd = function iadd (a, b) { - this._verify2(a, b); - - var res = a.iadd(b); - if (res.cmp(this.m) >= 0) { - res.isub(this.m); - } - return res; - }; - - Red.prototype.sub = function sub (a, b) { - this._verify2(a, b); - - var res = a.sub(b); - if (res.cmpn(0) < 0) { - res.iadd(this.m); - } - return res._forceRed(this); - }; - - Red.prototype.isub = function isub (a, b) { - this._verify2(a, b); - - var res = a.isub(b); - if (res.cmpn(0) < 0) { - res.iadd(this.m); - } - return res; - }; - - Red.prototype.shl = function shl (a, num) { - this._verify1(a); - return this.imod(a.ushln(num)); - }; - - Red.prototype.imul = function imul (a, b) { - this._verify2(a, b); - return this.imod(a.imul(b)); - }; - - Red.prototype.mul = function mul (a, b) { - this._verify2(a, b); - return this.imod(a.mul(b)); - }; - - Red.prototype.isqr = function isqr (a) { - return this.imul(a, a.clone()); - }; - - Red.prototype.sqr = function sqr (a) { - return this.mul(a, a); - }; - - Red.prototype.sqrt = function sqrt (a) { - if (a.isZero()) return a.clone(); - - var mod3 = this.m.andln(3); - assert(mod3 % 2 === 1); - - // Fast case - if (mod3 === 3) { - var pow = this.m.add(new BN(1)).iushrn(2); - return this.pow(a, pow); - } - - // Tonelli-Shanks algorithm (Totally unoptimized and slow) - // - // Find Q and S, that Q * 2 ^ S = (P - 1) - var q = this.m.subn(1); - var s = 0; - while (!q.isZero() && q.andln(1) === 0) { - s++; - q.iushrn(1); - } - assert(!q.isZero()); - - var one = new BN(1).toRed(this); - var nOne = one.redNeg(); - - // Find quadratic non-residue - // NOTE: Max is such because of generalized Riemann hypothesis. - var lpow = this.m.subn(1).iushrn(1); - var z = this.m.bitLength(); - z = new BN(2 * z * z).toRed(this); - - while (this.pow(z, lpow).cmp(nOne) !== 0) { - z.redIAdd(nOne); - } - - var c = this.pow(z, q); - var r = this.pow(a, q.addn(1).iushrn(1)); - var t = this.pow(a, q); - var m = s; - while (t.cmp(one) !== 0) { - var tmp = t; - for (var i = 0; tmp.cmp(one) !== 0; i++) { - tmp = tmp.redSqr(); - } - assert(i < m); - var b = this.pow(c, new BN(1).iushln(m - i - 1)); - - r = r.redMul(b); - c = b.redSqr(); - t = t.redMul(c); - m = i; - } - - return r; - }; - - Red.prototype.invm = function invm (a) { - var inv = a._invmp(this.m); - if (inv.negative !== 0) { - inv.negative = 0; - return this.imod(inv).redNeg(); - } else { - return this.imod(inv); - } - }; - - Red.prototype.pow = function pow (a, num) { - if (num.isZero()) return new BN(1).toRed(this); - if (num.cmpn(1) === 0) return a.clone(); - - var windowSize = 4; - var wnd = new Array(1 << windowSize); - wnd[0] = new BN(1).toRed(this); - wnd[1] = a; - for (var i = 2; i < wnd.length; i++) { - wnd[i] = this.mul(wnd[i - 1], a); - } - - var res = wnd[0]; - var current = 0; - var currentLen = 0; - var start = num.bitLength() % 26; - if (start === 0) { - start = 26; - } - - for (i = num.length - 1; i >= 0; i--) { - var word = num.words[i]; - for (var j = start - 1; j >= 0; j--) { - var bit = (word >> j) & 1; - if (res !== wnd[0]) { - res = this.sqr(res); - } - - if (bit === 0 && current === 0) { - currentLen = 0; - continue; - } - - current <<= 1; - current |= bit; - currentLen++; - if (currentLen !== windowSize && (i !== 0 || j !== 0)) continue; - - res = this.mul(res, wnd[current]); - currentLen = 0; - current = 0; - } - start = 26; - } - - return res; - }; - - Red.prototype.convertTo = function convertTo (num) { - var r = num.umod(this.m); - - return r === num ? r.clone() : r; - }; - - Red.prototype.convertFrom = function convertFrom (num) { - var res = num.clone(); - res.red = null; - return res; - }; - - // - // Montgomery method engine - // - - BN.mont = function mont (num) { - return new Mont(num); - }; - - function Mont (m) { - Red.call(this, m); - - this.shift = this.m.bitLength(); - if (this.shift % 26 !== 0) { - this.shift += 26 - (this.shift % 26); - } - - this.r = new BN(1).iushln(this.shift); - this.r2 = this.imod(this.r.sqr()); - this.rinv = this.r._invmp(this.m); - - this.minv = this.rinv.mul(this.r).isubn(1).div(this.m); - this.minv = this.minv.umod(this.r); - this.minv = this.r.sub(this.minv); - } - inherits(Mont, Red); - - Mont.prototype.convertTo = function convertTo (num) { - return this.imod(num.ushln(this.shift)); - }; - - Mont.prototype.convertFrom = function convertFrom (num) { - var r = this.imod(num.mul(this.rinv)); - r.red = null; - return r; - }; - - Mont.prototype.imul = function imul (a, b) { - if (a.isZero() || b.isZero()) { - a.words[0] = 0; - a.length = 1; - return a; - } - - var t = a.imul(b); - var c = t.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m); - var u = t.isub(c).iushrn(this.shift); - var res = u; - - if (u.cmp(this.m) >= 0) { - res = u.isub(this.m); - } else if (u.cmpn(0) < 0) { - res = u.iadd(this.m); - } - - return res._forceRed(this); - }; - - Mont.prototype.mul = function mul (a, b) { - if (a.isZero() || b.isZero()) return new BN(0)._forceRed(this); - - var t = a.mul(b); - var c = t.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m); - var u = t.isub(c).iushrn(this.shift); - var res = u; - if (u.cmp(this.m) >= 0) { - res = u.isub(this.m); - } else if (u.cmpn(0) < 0) { - res = u.iadd(this.m); - } - - return res._forceRed(this); - }; - - Mont.prototype.invm = function invm (a) { - // (AR)^-1 * R^2 = (A^-1 * R^-1) * R^2 = A^-1 * R - var res = this.imod(a._invmp(this.m).mul(this.r2)); - return res._forceRed(this); - }; -})(typeof module === 'undefined' || module, this); - -},{"buffer":34}],33:[function(require,module,exports){ -var r; - -module.exports = function rand(len) { - if (!r) - r = new Rand(null); - - return r.generate(len); -}; - -function Rand(rand) { - this.rand = rand; -} -module.exports.Rand = Rand; - -Rand.prototype.generate = function generate(len) { - return this._rand(len); -}; - -// Emulate crypto API using randy -Rand.prototype._rand = function _rand(n) { - if (this.rand.getBytes) - return this.rand.getBytes(n); - - var res = new Uint8Array(n); - for (var i = 0; i < res.length; i++) - res[i] = this.rand.getByte(); - return res; -}; - -if (typeof self === 'object') { - if (self.crypto && self.crypto.getRandomValues) { - // Modern browsers - Rand.prototype._rand = function _rand(n) { - var arr = new Uint8Array(n); - self.crypto.getRandomValues(arr); - return arr; - }; - } else if (self.msCrypto && self.msCrypto.getRandomValues) { - // IE - Rand.prototype._rand = function _rand(n) { - var arr = new Uint8Array(n); - self.msCrypto.getRandomValues(arr); - return arr; - }; - - // Safari's WebWorkers do not have `crypto` - } else if (typeof window === 'object') { - // Old junk - Rand.prototype._rand = function() { - throw new Error('Not implemented yet'); - }; - } -} else { - // Node.js or Web worker with no crypto support - try { - var crypto = require('crypto'); - if (typeof crypto.randomBytes !== 'function') - throw new Error('Not supported'); - - Rand.prototype._rand = function _rand(n) { - return crypto.randomBytes(n); - }; - } catch (e) { - } -} - -},{"crypto":34}],34:[function(require,module,exports){ - -},{}],35:[function(require,module,exports){ -// based on the aes implimentation in triple sec -// https://github.com/keybase/triplesec -// which is in turn based on the one from crypto-js -// https://code.google.com/p/crypto-js/ - -var Buffer = require('safe-buffer').Buffer - -function asUInt32Array (buf) { - if (!Buffer.isBuffer(buf)) buf = Buffer.from(buf) - - var len = (buf.length / 4) | 0 - var out = new Array(len) - - for (var i = 0; i < len; i++) { - out[i] = buf.readUInt32BE(i * 4) - } - - return out -} - -function scrubVec (v) { - for (var i = 0; i < v.length; v++) { - v[i] = 0 - } -} - -function cryptBlock (M, keySchedule, SUB_MIX, SBOX, nRounds) { - var SUB_MIX0 = SUB_MIX[0] - var SUB_MIX1 = SUB_MIX[1] - var SUB_MIX2 = SUB_MIX[2] - var SUB_MIX3 = SUB_MIX[3] - - var s0 = M[0] ^ keySchedule[0] - var s1 = M[1] ^ keySchedule[1] - var s2 = M[2] ^ keySchedule[2] - var s3 = M[3] ^ keySchedule[3] - var t0, t1, t2, t3 - var ksRow = 4 - - for (var round = 1; round < nRounds; round++) { - t0 = SUB_MIX0[s0 >>> 24] ^ SUB_MIX1[(s1 >>> 16) & 0xff] ^ SUB_MIX2[(s2 >>> 8) & 0xff] ^ SUB_MIX3[s3 & 0xff] ^ keySchedule[ksRow++] - t1 = SUB_MIX0[s1 >>> 24] ^ SUB_MIX1[(s2 >>> 16) & 0xff] ^ SUB_MIX2[(s3 >>> 8) & 0xff] ^ SUB_MIX3[s0 & 0xff] ^ keySchedule[ksRow++] - t2 = SUB_MIX0[s2 >>> 24] ^ SUB_MIX1[(s3 >>> 16) & 0xff] ^ SUB_MIX2[(s0 >>> 8) & 0xff] ^ SUB_MIX3[s1 & 0xff] ^ keySchedule[ksRow++] - t3 = SUB_MIX0[s3 >>> 24] ^ SUB_MIX1[(s0 >>> 16) & 0xff] ^ SUB_MIX2[(s1 >>> 8) & 0xff] ^ SUB_MIX3[s2 & 0xff] ^ keySchedule[ksRow++] - s0 = t0 - s1 = t1 - s2 = t2 - s3 = t3 - } - - t0 = ((SBOX[s0 >>> 24] << 24) | (SBOX[(s1 >>> 16) & 0xff] << 16) | (SBOX[(s2 >>> 8) & 0xff] << 8) | SBOX[s3 & 0xff]) ^ keySchedule[ksRow++] - t1 = ((SBOX[s1 >>> 24] << 24) | (SBOX[(s2 >>> 16) & 0xff] << 16) | (SBOX[(s3 >>> 8) & 0xff] << 8) | SBOX[s0 & 0xff]) ^ keySchedule[ksRow++] - t2 = ((SBOX[s2 >>> 24] << 24) | (SBOX[(s3 >>> 16) & 0xff] << 16) | (SBOX[(s0 >>> 8) & 0xff] << 8) | SBOX[s1 & 0xff]) ^ keySchedule[ksRow++] - t3 = ((SBOX[s3 >>> 24] << 24) | (SBOX[(s0 >>> 16) & 0xff] << 16) | (SBOX[(s1 >>> 8) & 0xff] << 8) | SBOX[s2 & 0xff]) ^ keySchedule[ksRow++] - t0 = t0 >>> 0 - t1 = t1 >>> 0 - t2 = t2 >>> 0 - t3 = t3 >>> 0 - - return [t0, t1, t2, t3] -} - -// AES constants -var RCON = [0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36] -var G = (function () { - // Compute double table - var d = new Array(256) - for (var j = 0; j < 256; j++) { - if (j < 128) { - d[j] = j << 1 - } else { - d[j] = (j << 1) ^ 0x11b - } - } - - var SBOX = [] - var INV_SBOX = [] - var SUB_MIX = [[], [], [], []] - var INV_SUB_MIX = [[], [], [], []] - - // Walk GF(2^8) - var x = 0 - var xi = 0 - for (var i = 0; i < 256; ++i) { - // Compute sbox - var sx = xi ^ (xi << 1) ^ (xi << 2) ^ (xi << 3) ^ (xi << 4) - sx = (sx >>> 8) ^ (sx & 0xff) ^ 0x63 - SBOX[x] = sx - INV_SBOX[sx] = x - - // Compute multiplication - var x2 = d[x] - var x4 = d[x2] - var x8 = d[x4] - - // Compute sub bytes, mix columns tables - var t = (d[sx] * 0x101) ^ (sx * 0x1010100) - SUB_MIX[0][x] = (t << 24) | (t >>> 8) - SUB_MIX[1][x] = (t << 16) | (t >>> 16) - SUB_MIX[2][x] = (t << 8) | (t >>> 24) - SUB_MIX[3][x] = t - - // Compute inv sub bytes, inv mix columns tables - t = (x8 * 0x1010101) ^ (x4 * 0x10001) ^ (x2 * 0x101) ^ (x * 0x1010100) - INV_SUB_MIX[0][sx] = (t << 24) | (t >>> 8) - INV_SUB_MIX[1][sx] = (t << 16) | (t >>> 16) - INV_SUB_MIX[2][sx] = (t << 8) | (t >>> 24) - INV_SUB_MIX[3][sx] = t - - if (x === 0) { - x = xi = 1 - } else { - x = x2 ^ d[d[d[x8 ^ x2]]] - xi ^= d[d[xi]] - } - } - - return { - SBOX: SBOX, - INV_SBOX: INV_SBOX, - SUB_MIX: SUB_MIX, - INV_SUB_MIX: INV_SUB_MIX - } -})() - -function AES (key) { - this._key = asUInt32Array(key) - this._reset() -} - -AES.blockSize = 4 * 4 -AES.keySize = 256 / 8 -AES.prototype.blockSize = AES.blockSize -AES.prototype.keySize = AES.keySize -AES.prototype._reset = function () { - var keyWords = this._key - var keySize = keyWords.length - var nRounds = keySize + 6 - var ksRows = (nRounds + 1) * 4 - - var keySchedule = [] - for (var k = 0; k < keySize; k++) { - keySchedule[k] = keyWords[k] - } - - for (k = keySize; k < ksRows; k++) { - var t = keySchedule[k - 1] - - if (k % keySize === 0) { - t = (t << 8) | (t >>> 24) - t = - (G.SBOX[t >>> 24] << 24) | - (G.SBOX[(t >>> 16) & 0xff] << 16) | - (G.SBOX[(t >>> 8) & 0xff] << 8) | - (G.SBOX[t & 0xff]) - - t ^= RCON[(k / keySize) | 0] << 24 - } else if (keySize > 6 && k % keySize === 4) { - t = - (G.SBOX[t >>> 24] << 24) | - (G.SBOX[(t >>> 16) & 0xff] << 16) | - (G.SBOX[(t >>> 8) & 0xff] << 8) | - (G.SBOX[t & 0xff]) - } - - keySchedule[k] = keySchedule[k - keySize] ^ t - } - - var invKeySchedule = [] - for (var ik = 0; ik < ksRows; ik++) { - var ksR = ksRows - ik - var tt = keySchedule[ksR - (ik % 4 ? 0 : 4)] - - if (ik < 4 || ksR <= 4) { - invKeySchedule[ik] = tt - } else { - invKeySchedule[ik] = - G.INV_SUB_MIX[0][G.SBOX[tt >>> 24]] ^ - G.INV_SUB_MIX[1][G.SBOX[(tt >>> 16) & 0xff]] ^ - G.INV_SUB_MIX[2][G.SBOX[(tt >>> 8) & 0xff]] ^ - G.INV_SUB_MIX[3][G.SBOX[tt & 0xff]] - } - } - - this._nRounds = nRounds - this._keySchedule = keySchedule - this._invKeySchedule = invKeySchedule -} - -AES.prototype.encryptBlockRaw = function (M) { - M = asUInt32Array(M) - return cryptBlock(M, this._keySchedule, G.SUB_MIX, G.SBOX, this._nRounds) -} - -AES.prototype.encryptBlock = function (M) { - var out = this.encryptBlockRaw(M) - var buf = Buffer.allocUnsafe(16) - buf.writeUInt32BE(out[0], 0) - buf.writeUInt32BE(out[1], 4) - buf.writeUInt32BE(out[2], 8) - buf.writeUInt32BE(out[3], 12) - return buf -} - -AES.prototype.decryptBlock = function (M) { - M = asUInt32Array(M) - - // swap - var m1 = M[1] - M[1] = M[3] - M[3] = m1 - - var out = cryptBlock(M, this._invKeySchedule, G.INV_SUB_MIX, G.INV_SBOX, this._nRounds) - var buf = Buffer.allocUnsafe(16) - buf.writeUInt32BE(out[0], 0) - buf.writeUInt32BE(out[3], 4) - buf.writeUInt32BE(out[2], 8) - buf.writeUInt32BE(out[1], 12) - return buf -} - -AES.prototype.scrub = function () { - scrubVec(this._keySchedule) - scrubVec(this._invKeySchedule) - scrubVec(this._key) -} - -module.exports.AES = AES - -},{"safe-buffer":221}],36:[function(require,module,exports){ -var aes = require('./aes') -var Buffer = require('safe-buffer').Buffer -var Transform = require('cipher-base') -var inherits = require('inherits') -var GHASH = require('./ghash') -var xor = require('buffer-xor') -var incr32 = require('./incr32') - -function xorTest (a, b) { - var out = 0 - if (a.length !== b.length) out++ - - var len = Math.min(a.length, b.length) - for (var i = 0; i < len; ++i) { - out += (a[i] ^ b[i]) - } - - return out -} - -function calcIv (self, iv, ck) { - if (iv.length === 12) { - self._finID = Buffer.concat([iv, Buffer.from([0, 0, 0, 1])]) - return Buffer.concat([iv, Buffer.from([0, 0, 0, 2])]) - } - var ghash = new GHASH(ck) - var len = iv.length - var toPad = len % 16 - ghash.update(iv) - if (toPad) { - toPad = 16 - toPad - ghash.update(Buffer.alloc(toPad, 0)) - } - ghash.update(Buffer.alloc(8, 0)) - var ivBits = len * 8 - var tail = Buffer.alloc(8) - tail.writeUIntBE(ivBits, 0, 8) - ghash.update(tail) - self._finID = ghash.state - var out = Buffer.from(self._finID) - incr32(out) - return out -} -function StreamCipher (mode, key, iv, decrypt) { - Transform.call(this) - - var h = Buffer.alloc(4, 0) - - this._cipher = new aes.AES(key) - var ck = this._cipher.encryptBlock(h) - this._ghash = new GHASH(ck) - iv = calcIv(this, iv, ck) - - this._prev = Buffer.from(iv) - this._cache = Buffer.allocUnsafe(0) - this._secCache = Buffer.allocUnsafe(0) - this._decrypt = decrypt - this._alen = 0 - this._len = 0 - this._mode = mode - - this._authTag = null - this._called = false -} - -inherits(StreamCipher, Transform) - -StreamCipher.prototype._update = function (chunk) { - if (!this._called && this._alen) { - var rump = 16 - (this._alen % 16) - if (rump < 16) { - rump = Buffer.alloc(rump, 0) - this._ghash.update(rump) - } - } - - this._called = true - var out = this._mode.encrypt(this, chunk) - if (this._decrypt) { - this._ghash.update(chunk) - } else { - this._ghash.update(out) - } - this._len += chunk.length - return out -} - -StreamCipher.prototype._final = function () { - if (this._decrypt && !this._authTag) throw new Error('Unsupported state or unable to authenticate data') - - var tag = xor(this._ghash.final(this._alen * 8, this._len * 8), this._cipher.encryptBlock(this._finID)) - if (this._decrypt && xorTest(tag, this._authTag)) throw new Error('Unsupported state or unable to authenticate data') - - this._authTag = tag - this._cipher.scrub() -} - -StreamCipher.prototype.getAuthTag = function getAuthTag () { - if (this._decrypt || !Buffer.isBuffer(this._authTag)) throw new Error('Attempting to get auth tag in unsupported state') - - return this._authTag -} - -StreamCipher.prototype.setAuthTag = function setAuthTag (tag) { - if (!this._decrypt) throw new Error('Attempting to set auth tag in unsupported state') - - this._authTag = tag -} - -StreamCipher.prototype.setAAD = function setAAD (buf) { - if (this._called) throw new Error('Attempting to set AAD in unsupported state') - - this._ghash.update(buf) - this._alen += buf.length -} - -module.exports = StreamCipher - -},{"./aes":35,"./ghash":40,"./incr32":41,"buffer-xor":62,"cipher-base":77,"inherits":155,"safe-buffer":221}],37:[function(require,module,exports){ -var ciphers = require('./encrypter') -var deciphers = require('./decrypter') -var modes = require('./modes/list.json') - -function getCiphers () { - return Object.keys(modes) -} - -exports.createCipher = exports.Cipher = ciphers.createCipher -exports.createCipheriv = exports.Cipheriv = ciphers.createCipheriv -exports.createDecipher = exports.Decipher = deciphers.createDecipher -exports.createDecipheriv = exports.Decipheriv = deciphers.createDecipheriv -exports.listCiphers = exports.getCiphers = getCiphers - -},{"./decrypter":38,"./encrypter":39,"./modes/list.json":49}],38:[function(require,module,exports){ -var AuthCipher = require('./authCipher') -var Buffer = require('safe-buffer').Buffer -var MODES = require('./modes') -var StreamCipher = require('./streamCipher') -var Transform = require('cipher-base') -var aes = require('./aes') -var ebtk = require('evp_bytestokey') -var inherits = require('inherits') - -function Decipher (mode, key, iv) { - Transform.call(this) - - this._cache = new Splitter() - this._last = void 0 - this._cipher = new aes.AES(key) - this._prev = Buffer.from(iv) - this._mode = mode - this._autopadding = true -} - -inherits(Decipher, Transform) - -Decipher.prototype._update = function (data) { - this._cache.add(data) - var chunk - var thing - var out = [] - while ((chunk = this._cache.get(this._autopadding))) { - thing = this._mode.decrypt(this, chunk) - out.push(thing) - } - return Buffer.concat(out) -} - -Decipher.prototype._final = function () { - var chunk = this._cache.flush() - if (this._autopadding) { - return unpad(this._mode.decrypt(this, chunk)) - } else if (chunk) { - throw new Error('data not multiple of block length') - } -} - -Decipher.prototype.setAutoPadding = function (setTo) { - this._autopadding = !!setTo - return this -} - -function Splitter () { - this.cache = Buffer.allocUnsafe(0) -} - -Splitter.prototype.add = function (data) { - this.cache = Buffer.concat([this.cache, data]) -} - -Splitter.prototype.get = function (autoPadding) { - var out - if (autoPadding) { - if (this.cache.length > 16) { - out = this.cache.slice(0, 16) - this.cache = this.cache.slice(16) - return out - } - } else { - if (this.cache.length >= 16) { - out = this.cache.slice(0, 16) - this.cache = this.cache.slice(16) - return out - } - } - - return null -} - -Splitter.prototype.flush = function () { - if (this.cache.length) return this.cache -} - -function unpad (last) { - var padded = last[15] - if (padded < 1 || padded > 16) { - throw new Error('unable to decrypt data') - } - var i = -1 - while (++i < padded) { - if (last[(i + (16 - padded))] !== padded) { - throw new Error('unable to decrypt data') - } - } - if (padded === 16) return - - return last.slice(0, 16 - padded) -} - -function createDecipheriv (suite, password, iv) { - var config = MODES[suite.toLowerCase()] - if (!config) throw new TypeError('invalid suite type') - - if (typeof iv === 'string') iv = Buffer.from(iv) - if (config.mode !== 'GCM' && iv.length !== config.iv) throw new TypeError('invalid iv length ' + iv.length) - - if (typeof password === 'string') password = Buffer.from(password) - if (password.length !== config.key / 8) throw new TypeError('invalid key length ' + password.length) - - if (config.type === 'stream') { - return new StreamCipher(config.module, password, iv, true) - } else if (config.type === 'auth') { - return new AuthCipher(config.module, password, iv, true) - } - - return new Decipher(config.module, password, iv) -} - -function createDecipher (suite, password) { - var config = MODES[suite.toLowerCase()] - if (!config) throw new TypeError('invalid suite type') - - var keys = ebtk(password, false, config.key, config.iv) - return createDecipheriv(suite, keys.key, keys.iv) -} - -exports.createDecipher = createDecipher -exports.createDecipheriv = createDecipheriv - -},{"./aes":35,"./authCipher":36,"./modes":48,"./streamCipher":51,"cipher-base":77,"evp_bytestokey":130,"inherits":155,"safe-buffer":221}],39:[function(require,module,exports){ -var MODES = require('./modes') -var AuthCipher = require('./authCipher') -var Buffer = require('safe-buffer').Buffer -var StreamCipher = require('./streamCipher') -var Transform = require('cipher-base') -var aes = require('./aes') -var ebtk = require('evp_bytestokey') -var inherits = require('inherits') - -function Cipher (mode, key, iv) { - Transform.call(this) - - this._cache = new Splitter() - this._cipher = new aes.AES(key) - this._prev = Buffer.from(iv) - this._mode = mode - this._autopadding = true -} - -inherits(Cipher, Transform) - -Cipher.prototype._update = function (data) { - this._cache.add(data) - var chunk - var thing - var out = [] - - while ((chunk = this._cache.get())) { - thing = this._mode.encrypt(this, chunk) - out.push(thing) - } - - return Buffer.concat(out) -} - -var PADDING = Buffer.alloc(16, 0x10) - -Cipher.prototype._final = function () { - var chunk = this._cache.flush() - if (this._autopadding) { - chunk = this._mode.encrypt(this, chunk) - this._cipher.scrub() - return chunk - } - - if (!chunk.equals(PADDING)) { - this._cipher.scrub() - throw new Error('data not multiple of block length') - } -} - -Cipher.prototype.setAutoPadding = function (setTo) { - this._autopadding = !!setTo - return this -} - -function Splitter () { - this.cache = Buffer.allocUnsafe(0) -} - -Splitter.prototype.add = function (data) { - this.cache = Buffer.concat([this.cache, data]) -} - -Splitter.prototype.get = function () { - if (this.cache.length > 15) { - var out = this.cache.slice(0, 16) - this.cache = this.cache.slice(16) - return out - } - return null -} - -Splitter.prototype.flush = function () { - var len = 16 - this.cache.length - var padBuff = Buffer.allocUnsafe(len) - - var i = -1 - while (++i < len) { - padBuff.writeUInt8(len, i) - } - - return Buffer.concat([this.cache, padBuff]) -} - -function createCipheriv (suite, password, iv) { - var config = MODES[suite.toLowerCase()] - if (!config) throw new TypeError('invalid suite type') - - if (typeof password === 'string') password = Buffer.from(password) - if (password.length !== config.key / 8) throw new TypeError('invalid key length ' + password.length) - - if (typeof iv === 'string') iv = Buffer.from(iv) - if (config.mode !== 'GCM' && iv.length !== config.iv) throw new TypeError('invalid iv length ' + iv.length) - - if (config.type === 'stream') { - return new StreamCipher(config.module, password, iv) - } else if (config.type === 'auth') { - return new AuthCipher(config.module, password, iv) - } - - return new Cipher(config.module, password, iv) -} - -function createCipher (suite, password) { - var config = MODES[suite.toLowerCase()] - if (!config) throw new TypeError('invalid suite type') - - var keys = ebtk(password, false, config.key, config.iv) - return createCipheriv(suite, keys.key, keys.iv) -} - -exports.createCipheriv = createCipheriv -exports.createCipher = createCipher - -},{"./aes":35,"./authCipher":36,"./modes":48,"./streamCipher":51,"cipher-base":77,"evp_bytestokey":130,"inherits":155,"safe-buffer":221}],40:[function(require,module,exports){ -var Buffer = require('safe-buffer').Buffer -var ZEROES = Buffer.alloc(16, 0) - -function toArray (buf) { - return [ - buf.readUInt32BE(0), - buf.readUInt32BE(4), - buf.readUInt32BE(8), - buf.readUInt32BE(12) - ] -} - -function fromArray (out) { - var buf = Buffer.allocUnsafe(16) - buf.writeUInt32BE(out[0] >>> 0, 0) - buf.writeUInt32BE(out[1] >>> 0, 4) - buf.writeUInt32BE(out[2] >>> 0, 8) - buf.writeUInt32BE(out[3] >>> 0, 12) - return buf -} - -function GHASH (key) { - this.h = key - this.state = Buffer.alloc(16, 0) - this.cache = Buffer.allocUnsafe(0) -} - -// from http://bitwiseshiftleft.github.io/sjcl/doc/symbols/src/core_gcm.js.html -// by Juho Vähä-Herttua -GHASH.prototype.ghash = function (block) { - var i = -1 - while (++i < block.length) { - this.state[i] ^= block[i] - } - this._multiply() -} - -GHASH.prototype._multiply = function () { - var Vi = toArray(this.h) - var Zi = [0, 0, 0, 0] - var j, xi, lsbVi - var i = -1 - while (++i < 128) { - xi = (this.state[~~(i / 8)] & (1 << (7 - (i % 8)))) !== 0 - if (xi) { - // Z_i+1 = Z_i ^ V_i - Zi[0] ^= Vi[0] - Zi[1] ^= Vi[1] - Zi[2] ^= Vi[2] - Zi[3] ^= Vi[3] - } - - // Store the value of LSB(V_i) - lsbVi = (Vi[3] & 1) !== 0 - - // V_i+1 = V_i >> 1 - for (j = 3; j > 0; j--) { - Vi[j] = (Vi[j] >>> 1) | ((Vi[j - 1] & 1) << 31) - } - Vi[0] = Vi[0] >>> 1 - - // If LSB(V_i) is 1, V_i+1 = (V_i >> 1) ^ R - if (lsbVi) { - Vi[0] = Vi[0] ^ (0xe1 << 24) - } - } - this.state = fromArray(Zi) -} - -GHASH.prototype.update = function (buf) { - this.cache = Buffer.concat([this.cache, buf]) - var chunk - while (this.cache.length >= 16) { - chunk = this.cache.slice(0, 16) - this.cache = this.cache.slice(16) - this.ghash(chunk) - } -} - -GHASH.prototype.final = function (abl, bl) { - if (this.cache.length) { - this.ghash(Buffer.concat([this.cache, ZEROES], 16)) - } - - this.ghash(fromArray([0, abl, 0, bl])) - return this.state -} - -module.exports = GHASH - -},{"safe-buffer":221}],41:[function(require,module,exports){ -function incr32 (iv) { - var len = iv.length - var item - while (len--) { - item = iv.readUInt8(len) - if (item === 255) { - iv.writeUInt8(0, len) - } else { - item++ - iv.writeUInt8(item, len) - break - } - } -} -module.exports = incr32 - -},{}],42:[function(require,module,exports){ -var xor = require('buffer-xor') - -exports.encrypt = function (self, block) { - var data = xor(block, self._prev) - - self._prev = self._cipher.encryptBlock(data) - return self._prev -} - -exports.decrypt = function (self, block) { - var pad = self._prev - - self._prev = block - var out = self._cipher.decryptBlock(block) - - return xor(out, pad) -} - -},{"buffer-xor":62}],43:[function(require,module,exports){ -var Buffer = require('safe-buffer').Buffer -var xor = require('buffer-xor') - -function encryptStart (self, data, decrypt) { - var len = data.length - var out = xor(data, self._cache) - self._cache = self._cache.slice(len) - self._prev = Buffer.concat([self._prev, decrypt ? data : out]) - return out -} - -exports.encrypt = function (self, data, decrypt) { - var out = Buffer.allocUnsafe(0) - var len - - while (data.length) { - if (self._cache.length === 0) { - self._cache = self._cipher.encryptBlock(self._prev) - self._prev = Buffer.allocUnsafe(0) - } - - if (self._cache.length <= data.length) { - len = self._cache.length - out = Buffer.concat([out, encryptStart(self, data.slice(0, len), decrypt)]) - data = data.slice(len) - } else { - out = Buffer.concat([out, encryptStart(self, data, decrypt)]) - break - } - } - - return out -} - -},{"buffer-xor":62,"safe-buffer":221}],44:[function(require,module,exports){ -var Buffer = require('safe-buffer').Buffer - -function encryptByte (self, byteParam, decrypt) { - var pad - var i = -1 - var len = 8 - var out = 0 - var bit, value - while (++i < len) { - pad = self._cipher.encryptBlock(self._prev) - bit = (byteParam & (1 << (7 - i))) ? 0x80 : 0 - value = pad[0] ^ bit - out += ((value & 0x80) >> (i % 8)) - self._prev = shiftIn(self._prev, decrypt ? bit : value) - } - return out -} - -function shiftIn (buffer, value) { - var len = buffer.length - var i = -1 - var out = Buffer.allocUnsafe(buffer.length) - buffer = Buffer.concat([buffer, Buffer.from([value])]) - - while (++i < len) { - out[i] = buffer[i] << 1 | buffer[i + 1] >> (7) - } - - return out -} - -exports.encrypt = function (self, chunk, decrypt) { - var len = chunk.length - var out = Buffer.allocUnsafe(len) - var i = -1 - - while (++i < len) { - out[i] = encryptByte(self, chunk[i], decrypt) - } - - return out -} - -},{"safe-buffer":221}],45:[function(require,module,exports){ -var Buffer = require('safe-buffer').Buffer - -function encryptByte (self, byteParam, decrypt) { - var pad = self._cipher.encryptBlock(self._prev) - var out = pad[0] ^ byteParam - - self._prev = Buffer.concat([ - self._prev.slice(1), - Buffer.from([decrypt ? byteParam : out]) - ]) - - return out -} - -exports.encrypt = function (self, chunk, decrypt) { - var len = chunk.length - var out = Buffer.allocUnsafe(len) - var i = -1 - - while (++i < len) { - out[i] = encryptByte(self, chunk[i], decrypt) - } - - return out -} - -},{"safe-buffer":221}],46:[function(require,module,exports){ -var xor = require('buffer-xor') -var Buffer = require('safe-buffer').Buffer -var incr32 = require('../incr32') - -function getBlock (self) { - var out = self._cipher.encryptBlockRaw(self._prev) - incr32(self._prev) - return out -} - -var blockSize = 16 -exports.encrypt = function (self, chunk) { - var chunkNum = Math.ceil(chunk.length / blockSize) - var start = self._cache.length - self._cache = Buffer.concat([ - self._cache, - Buffer.allocUnsafe(chunkNum * blockSize) - ]) - for (var i = 0; i < chunkNum; i++) { - var out = getBlock(self) - var offset = start + i * blockSize - self._cache.writeUInt32BE(out[0], offset + 0) - self._cache.writeUInt32BE(out[1], offset + 4) - self._cache.writeUInt32BE(out[2], offset + 8) - self._cache.writeUInt32BE(out[3], offset + 12) - } - var pad = self._cache.slice(0, chunk.length) - self._cache = self._cache.slice(chunk.length) - return xor(chunk, pad) -} - -},{"../incr32":41,"buffer-xor":62,"safe-buffer":221}],47:[function(require,module,exports){ -exports.encrypt = function (self, block) { - return self._cipher.encryptBlock(block) -} - -exports.decrypt = function (self, block) { - return self._cipher.decryptBlock(block) -} - -},{}],48:[function(require,module,exports){ -var modeModules = { - ECB: require('./ecb'), - CBC: require('./cbc'), - CFB: require('./cfb'), - CFB8: require('./cfb8'), - CFB1: require('./cfb1'), - OFB: require('./ofb'), - CTR: require('./ctr'), - GCM: require('./ctr') -} - -var modes = require('./list.json') - -for (var key in modes) { - modes[key].module = modeModules[modes[key].mode] -} - -module.exports = modes - -},{"./cbc":42,"./cfb":43,"./cfb1":44,"./cfb8":45,"./ctr":46,"./ecb":47,"./list.json":49,"./ofb":50}],49:[function(require,module,exports){ -module.exports={ - "aes-128-ecb": { - "cipher": "AES", - "key": 128, - "iv": 0, - "mode": "ECB", - "type": "block" - }, - "aes-192-ecb": { - "cipher": "AES", - "key": 192, - "iv": 0, - "mode": "ECB", - "type": "block" - }, - "aes-256-ecb": { - "cipher": "AES", - "key": 256, - "iv": 0, - "mode": "ECB", - "type": "block" - }, - "aes-128-cbc": { - "cipher": "AES", - "key": 128, - "iv": 16, - "mode": "CBC", - "type": "block" - }, - "aes-192-cbc": { - "cipher": "AES", - "key": 192, - "iv": 16, - "mode": "CBC", - "type": "block" - }, - "aes-256-cbc": { - "cipher": "AES", - "key": 256, - "iv": 16, - "mode": "CBC", - "type": "block" - }, - "aes128": { - "cipher": "AES", - "key": 128, - "iv": 16, - "mode": "CBC", - "type": "block" - }, - "aes192": { - "cipher": "AES", - "key": 192, - "iv": 16, - "mode": "CBC", - "type": "block" - }, - "aes256": { - "cipher": "AES", - "key": 256, - "iv": 16, - "mode": "CBC", - "type": "block" - }, - "aes-128-cfb": { - "cipher": "AES", - "key": 128, - "iv": 16, - "mode": "CFB", - "type": "stream" - }, - "aes-192-cfb": { - "cipher": "AES", - "key": 192, - "iv": 16, - "mode": "CFB", - "type": "stream" - }, - "aes-256-cfb": { - "cipher": "AES", - "key": 256, - "iv": 16, - "mode": "CFB", - "type": "stream" - }, - "aes-128-cfb8": { - "cipher": "AES", - "key": 128, - "iv": 16, - "mode": "CFB8", - "type": "stream" - }, - "aes-192-cfb8": { - "cipher": "AES", - "key": 192, - "iv": 16, - "mode": "CFB8", - "type": "stream" - }, - "aes-256-cfb8": { - "cipher": "AES", - "key": 256, - "iv": 16, - "mode": "CFB8", - "type": "stream" - }, - "aes-128-cfb1": { - "cipher": "AES", - "key": 128, - "iv": 16, - "mode": "CFB1", - "type": "stream" - }, - "aes-192-cfb1": { - "cipher": "AES", - "key": 192, - "iv": 16, - "mode": "CFB1", - "type": "stream" - }, - "aes-256-cfb1": { - "cipher": "AES", - "key": 256, - "iv": 16, - "mode": "CFB1", - "type": "stream" - }, - "aes-128-ofb": { - "cipher": "AES", - "key": 128, - "iv": 16, - "mode": "OFB", - "type": "stream" - }, - "aes-192-ofb": { - "cipher": "AES", - "key": 192, - "iv": 16, - "mode": "OFB", - "type": "stream" - }, - "aes-256-ofb": { - "cipher": "AES", - "key": 256, - "iv": 16, - "mode": "OFB", - "type": "stream" - }, - "aes-128-ctr": { - "cipher": "AES", - "key": 128, - "iv": 16, - "mode": "CTR", - "type": "stream" - }, - "aes-192-ctr": { - "cipher": "AES", - "key": 192, - "iv": 16, - "mode": "CTR", - "type": "stream" - }, - "aes-256-ctr": { - "cipher": "AES", - "key": 256, - "iv": 16, - "mode": "CTR", - "type": "stream" - }, - "aes-128-gcm": { - "cipher": "AES", - "key": 128, - "iv": 12, - "mode": "GCM", - "type": "auth" - }, - "aes-192-gcm": { - "cipher": "AES", - "key": 192, - "iv": 12, - "mode": "GCM", - "type": "auth" - }, - "aes-256-gcm": { - "cipher": "AES", - "key": 256, - "iv": 12, - "mode": "GCM", - "type": "auth" - } -} - -},{}],50:[function(require,module,exports){ -(function (Buffer){(function (){ -var xor = require('buffer-xor') - -function getBlock (self) { - self._prev = self._cipher.encryptBlock(self._prev) - return self._prev -} - -exports.encrypt = function (self, chunk) { - while (self._cache.length < chunk.length) { - self._cache = Buffer.concat([self._cache, getBlock(self)]) - } - - var pad = self._cache.slice(0, chunk.length) - self._cache = self._cache.slice(chunk.length) - return xor(chunk, pad) -} - -}).call(this)}).call(this,require("buffer").Buffer) -},{"buffer":63,"buffer-xor":62}],51:[function(require,module,exports){ -var aes = require('./aes') -var Buffer = require('safe-buffer').Buffer -var Transform = require('cipher-base') -var inherits = require('inherits') - -function StreamCipher (mode, key, iv, decrypt) { - Transform.call(this) - - this._cipher = new aes.AES(key) - this._prev = Buffer.from(iv) - this._cache = Buffer.allocUnsafe(0) - this._secCache = Buffer.allocUnsafe(0) - this._decrypt = decrypt - this._mode = mode -} - -inherits(StreamCipher, Transform) - -StreamCipher.prototype._update = function (chunk) { - return this._mode.encrypt(this, chunk, this._decrypt) -} - -StreamCipher.prototype._final = function () { - this._cipher.scrub() -} - -module.exports = StreamCipher - -},{"./aes":35,"cipher-base":77,"inherits":155,"safe-buffer":221}],52:[function(require,module,exports){ -var DES = require('browserify-des') -var aes = require('browserify-aes/browser') -var aesModes = require('browserify-aes/modes') -var desModes = require('browserify-des/modes') -var ebtk = require('evp_bytestokey') - -function createCipher (suite, password) { - suite = suite.toLowerCase() - - var keyLen, ivLen - if (aesModes[suite]) { - keyLen = aesModes[suite].key - ivLen = aesModes[suite].iv - } else if (desModes[suite]) { - keyLen = desModes[suite].key * 8 - ivLen = desModes[suite].iv - } else { - throw new TypeError('invalid suite type') - } - - var keys = ebtk(password, false, keyLen, ivLen) - return createCipheriv(suite, keys.key, keys.iv) -} - -function createDecipher (suite, password) { - suite = suite.toLowerCase() - - var keyLen, ivLen - if (aesModes[suite]) { - keyLen = aesModes[suite].key - ivLen = aesModes[suite].iv - } else if (desModes[suite]) { - keyLen = desModes[suite].key * 8 - ivLen = desModes[suite].iv - } else { - throw new TypeError('invalid suite type') - } - - var keys = ebtk(password, false, keyLen, ivLen) - return createDecipheriv(suite, keys.key, keys.iv) -} - -function createCipheriv (suite, key, iv) { - suite = suite.toLowerCase() - if (aesModes[suite]) return aes.createCipheriv(suite, key, iv) - if (desModes[suite]) return new DES({ key: key, iv: iv, mode: suite }) - - throw new TypeError('invalid suite type') -} - -function createDecipheriv (suite, key, iv) { - suite = suite.toLowerCase() - if (aesModes[suite]) return aes.createDecipheriv(suite, key, iv) - if (desModes[suite]) return new DES({ key: key, iv: iv, mode: suite, decrypt: true }) - - throw new TypeError('invalid suite type') -} - -function getCiphers () { - return Object.keys(desModes).concat(aes.getCiphers()) -} - -exports.createCipher = exports.Cipher = createCipher -exports.createCipheriv = exports.Cipheriv = createCipheriv -exports.createDecipher = exports.Decipher = createDecipher -exports.createDecipheriv = exports.Decipheriv = createDecipheriv -exports.listCiphers = exports.getCiphers = getCiphers - -},{"browserify-aes/browser":37,"browserify-aes/modes":48,"browserify-des":53,"browserify-des/modes":54,"evp_bytestokey":130}],53:[function(require,module,exports){ -var CipherBase = require('cipher-base') -var des = require('des.js') -var inherits = require('inherits') -var Buffer = require('safe-buffer').Buffer - -var modes = { - 'des-ede3-cbc': des.CBC.instantiate(des.EDE), - 'des-ede3': des.EDE, - 'des-ede-cbc': des.CBC.instantiate(des.EDE), - 'des-ede': des.EDE, - 'des-cbc': des.CBC.instantiate(des.DES), - 'des-ecb': des.DES -} -modes.des = modes['des-cbc'] -modes.des3 = modes['des-ede3-cbc'] -module.exports = DES -inherits(DES, CipherBase) -function DES (opts) { - CipherBase.call(this) - var modeName = opts.mode.toLowerCase() - var mode = modes[modeName] - var type - if (opts.decrypt) { - type = 'decrypt' - } else { - type = 'encrypt' - } - var key = opts.key - if (!Buffer.isBuffer(key)) { - key = Buffer.from(key) - } - if (modeName === 'des-ede' || modeName === 'des-ede-cbc') { - key = Buffer.concat([key, key.slice(0, 8)]) - } - var iv = opts.iv - if (!Buffer.isBuffer(iv)) { - iv = Buffer.from(iv) - } - this._des = mode.create({ - key: key, - iv: iv, - type: type - }) -} -DES.prototype._update = function (data) { - return Buffer.from(this._des.update(data)) -} -DES.prototype._final = function () { - return Buffer.from(this._des.final()) -} - -},{"cipher-base":77,"des.js":93,"inherits":155,"safe-buffer":221}],54:[function(require,module,exports){ -exports['des-ecb'] = { - key: 8, - iv: 0 -} -exports['des-cbc'] = exports.des = { - key: 8, - iv: 8 -} -exports['des-ede3-cbc'] = exports.des3 = { - key: 24, - iv: 8 -} -exports['des-ede3'] = { - key: 24, - iv: 0 -} -exports['des-ede-cbc'] = { - key: 16, - iv: 8 -} -exports['des-ede'] = { - key: 16, - iv: 0 -} - -},{}],55:[function(require,module,exports){ -(function (Buffer){(function (){ -var BN = require('bn.js') -var randomBytes = require('randombytes') - -function blind (priv) { - var r = getr(priv) - var blinder = r.toRed(BN.mont(priv.modulus)).redPow(new BN(priv.publicExponent)).fromRed() - return { blinder: blinder, unblinder: r.invm(priv.modulus) } -} - -function getr (priv) { - var len = priv.modulus.byteLength() - var r - do { - r = new BN(randomBytes(len)) - } while (r.cmp(priv.modulus) >= 0 || !r.umod(priv.prime1) || !r.umod(priv.prime2)) - return r -} - -function crt (msg, priv) { - var blinds = blind(priv) - var len = priv.modulus.byteLength() - var blinded = new BN(msg).mul(blinds.blinder).umod(priv.modulus) - var c1 = blinded.toRed(BN.mont(priv.prime1)) - var c2 = blinded.toRed(BN.mont(priv.prime2)) - var qinv = priv.coefficient - var p = priv.prime1 - var q = priv.prime2 - var m1 = c1.redPow(priv.exponent1).fromRed() - var m2 = c2.redPow(priv.exponent2).fromRed() - var h = m1.isub(m2).imul(qinv).umod(p).imul(q) - return m2.iadd(h).imul(blinds.unblinder).umod(priv.modulus).toArrayLike(Buffer, 'be', len) -} -crt.getr = getr - -module.exports = crt - -}).call(this)}).call(this,require("buffer").Buffer) -},{"bn.js":32,"buffer":63,"randombytes":207}],56:[function(require,module,exports){ -'use strict'; - -module.exports = require('./browser/algorithms.json'); - -},{"./browser/algorithms.json":57}],57:[function(require,module,exports){ -module.exports={ - "sha224WithRSAEncryption": { - "sign": "rsa", - "hash": "sha224", - "id": "302d300d06096086480165030402040500041c" - }, - "RSA-SHA224": { - "sign": "ecdsa/rsa", - "hash": "sha224", - "id": "302d300d06096086480165030402040500041c" - }, - "sha256WithRSAEncryption": { - "sign": "rsa", - "hash": "sha256", - "id": "3031300d060960864801650304020105000420" - }, - "RSA-SHA256": { - "sign": "ecdsa/rsa", - "hash": "sha256", - "id": "3031300d060960864801650304020105000420" - }, - "sha384WithRSAEncryption": { - "sign": "rsa", - "hash": "sha384", - "id": "3041300d060960864801650304020205000430" - }, - "RSA-SHA384": { - "sign": "ecdsa/rsa", - "hash": "sha384", - "id": "3041300d060960864801650304020205000430" - }, - "sha512WithRSAEncryption": { - "sign": "rsa", - "hash": "sha512", - "id": "3051300d060960864801650304020305000440" - }, - "RSA-SHA512": { - "sign": "ecdsa/rsa", - "hash": "sha512", - "id": "3051300d060960864801650304020305000440" - }, - "RSA-SHA1": { - "sign": "rsa", - "hash": "sha1", - "id": "3021300906052b0e03021a05000414" - }, - "ecdsa-with-SHA1": { - "sign": "ecdsa", - "hash": "sha1", - "id": "" - }, - "sha256": { - "sign": "ecdsa", - "hash": "sha256", - "id": "" - }, - "sha224": { - "sign": "ecdsa", - "hash": "sha224", - "id": "" - }, - "sha384": { - "sign": "ecdsa", - "hash": "sha384", - "id": "" - }, - "sha512": { - "sign": "ecdsa", - "hash": "sha512", - "id": "" - }, - "DSA-SHA": { - "sign": "dsa", - "hash": "sha1", - "id": "" - }, - "DSA-SHA1": { - "sign": "dsa", - "hash": "sha1", - "id": "" - }, - "DSA": { - "sign": "dsa", - "hash": "sha1", - "id": "" - }, - "DSA-WITH-SHA224": { - "sign": "dsa", - "hash": "sha224", - "id": "" - }, - "DSA-SHA224": { - "sign": "dsa", - "hash": "sha224", - "id": "" - }, - "DSA-WITH-SHA256": { - "sign": "dsa", - "hash": "sha256", - "id": "" - }, - "DSA-SHA256": { - "sign": "dsa", - "hash": "sha256", - "id": "" - }, - "DSA-WITH-SHA384": { - "sign": "dsa", - "hash": "sha384", - "id": "" - }, - "DSA-SHA384": { - "sign": "dsa", - "hash": "sha384", - "id": "" - }, - "DSA-WITH-SHA512": { - "sign": "dsa", - "hash": "sha512", - "id": "" - }, - "DSA-SHA512": { - "sign": "dsa", - "hash": "sha512", - "id": "" - }, - "DSA-RIPEMD160": { - "sign": "dsa", - "hash": "rmd160", - "id": "" - }, - "ripemd160WithRSA": { - "sign": "rsa", - "hash": "rmd160", - "id": "3021300906052b2403020105000414" - }, - "RSA-RIPEMD160": { - "sign": "rsa", - "hash": "rmd160", - "id": "3021300906052b2403020105000414" - }, - "md5WithRSAEncryption": { - "sign": "rsa", - "hash": "md5", - "id": "3020300c06082a864886f70d020505000410" - }, - "RSA-MD5": { - "sign": "rsa", - "hash": "md5", - "id": "3020300c06082a864886f70d020505000410" - } -} - -},{}],58:[function(require,module,exports){ -module.exports={ - "1.3.132.0.10": "secp256k1", - "1.3.132.0.33": "p224", - "1.2.840.10045.3.1.1": "p192", - "1.2.840.10045.3.1.7": "p256", - "1.3.132.0.34": "p384", - "1.3.132.0.35": "p521" -} - -},{}],59:[function(require,module,exports){ -'use strict'; - -var Buffer = require('safe-buffer').Buffer; -var createHash = require('create-hash'); -var stream = require('readable-stream'); -var inherits = require('inherits'); -var sign = require('./sign'); -var verify = require('./verify'); - -var algorithms = require('./algorithms.json'); -Object.keys(algorithms).forEach(function (key) { - algorithms[key].id = Buffer.from(algorithms[key].id, 'hex'); - algorithms[key.toLowerCase()] = algorithms[key]; -}); - -function Sign(algorithm) { - stream.Writable.call(this); - - var data = algorithms[algorithm]; - if (!data) { throw new Error('Unknown message digest'); } - - this._hashType = data.hash; - this._hash = createHash(data.hash); - this._tag = data.id; - this._signType = data.sign; -} -inherits(Sign, stream.Writable); - -Sign.prototype._write = function _write(data, _, done) { - this._hash.update(data); - done(); -}; - -Sign.prototype.update = function update(data, enc) { - this._hash.update(typeof data === 'string' ? Buffer.from(data, enc) : data); - - return this; -}; - -Sign.prototype.sign = function signMethod(key, enc) { - this.end(); - var hash = this._hash.digest(); - var sig = sign(hash, key, this._hashType, this._signType, this._tag); - - return enc ? sig.toString(enc) : sig; -}; - -function Verify(algorithm) { - stream.Writable.call(this); - - var data = algorithms[algorithm]; - if (!data) { throw new Error('Unknown message digest'); } - - this._hash = createHash(data.hash); - this._tag = data.id; - this._signType = data.sign; -} -inherits(Verify, stream.Writable); - -Verify.prototype._write = function _write(data, _, done) { - this._hash.update(data); - done(); -}; - -Verify.prototype.update = function update(data, enc) { - this._hash.update(typeof data === 'string' ? Buffer.from(data, enc) : data); - - return this; -}; - -Verify.prototype.verify = function verifyMethod(key, sig, enc) { - var sigBuffer = typeof sig === 'string' ? Buffer.from(sig, enc) : sig; - - this.end(); - var hash = this._hash.digest(); - return verify(sigBuffer, hash, key, this._signType, this._tag); -}; - -function createSign(algorithm) { - return new Sign(algorithm); -} - -function createVerify(algorithm) { - return new Verify(algorithm); -} - -module.exports = { - Sign: createSign, - Verify: createVerify, - createSign: createSign, - createVerify: createVerify -}; - -},{"./algorithms.json":57,"./sign":60,"./verify":61,"create-hash":87,"inherits":155,"readable-stream":219,"safe-buffer":221}],60:[function(require,module,exports){ -'use strict'; - -// much of this based on https://github.com/indutny/self-signed/blob/gh-pages/lib/rsa.js -var Buffer = require('safe-buffer').Buffer; -var createHmac = require('create-hmac'); -var crt = require('browserify-rsa'); -var EC = require('elliptic').ec; -var BN = require('bn.js'); -var parseKeys = require('parse-asn1'); -var curves = require('./curves.json'); - -var RSA_PKCS1_PADDING = 1; - -function sign(hash, key, hashType, signType, tag) { - var priv = parseKeys(key); - if (priv.curve) { - // rsa keys can be interpreted as ecdsa ones in openssl - if (signType !== 'ecdsa' && signType !== 'ecdsa/rsa') { throw new Error('wrong private key type'); } - return ecSign(hash, priv); - } else if (priv.type === 'dsa') { - if (signType !== 'dsa') { throw new Error('wrong private key type'); } - return dsaSign(hash, priv, hashType); - } - if (signType !== 'rsa' && signType !== 'ecdsa/rsa') { throw new Error('wrong private key type'); } - if (key.padding !== undefined && key.padding !== RSA_PKCS1_PADDING) { throw new Error('illegal or unsupported padding mode'); } - - hash = Buffer.concat([tag, hash]); - var len = priv.modulus.byteLength(); - var pad = [0, 1]; - while (hash.length + pad.length + 1 < len) { pad.push(0xff); } - pad.push(0x00); - var i = -1; - while (++i < hash.length) { pad.push(hash[i]); } - - var out = crt(pad, priv); - return out; -} - -function ecSign(hash, priv) { - var curveId = curves[priv.curve.join('.')]; - if (!curveId) { throw new Error('unknown curve ' + priv.curve.join('.')); } - - var curve = new EC(curveId); - var key = curve.keyFromPrivate(priv.privateKey); - var out = key.sign(hash); - - return Buffer.from(out.toDER()); -} - -function dsaSign(hash, priv, algo) { - var x = priv.params.priv_key; - var p = priv.params.p; - var q = priv.params.q; - var g = priv.params.g; - var r = new BN(0); - var k; - var H = bits2int(hash, q).mod(q); - var s = false; - var kv = getKey(x, q, hash, algo); - while (s === false) { - k = makeKey(q, kv, algo); - r = makeR(g, k, p, q); - s = k.invm(q).imul(H.add(x.mul(r))).mod(q); - if (s.cmpn(0) === 0) { - s = false; - r = new BN(0); - } - } - return toDER(r, s); -} - -function toDER(r, s) { - r = r.toArray(); - s = s.toArray(); - - // Pad values - if (r[0] & 0x80) { r = [0].concat(r); } - if (s[0] & 0x80) { s = [0].concat(s); } - - var total = r.length + s.length + 4; - var res = [ - 0x30, total, 0x02, r.length - ]; - res = res.concat(r, [0x02, s.length], s); - return Buffer.from(res); -} - -function getKey(x, q, hash, algo) { - x = Buffer.from(x.toArray()); - if (x.length < q.byteLength()) { - var zeros = Buffer.alloc(q.byteLength() - x.length); - x = Buffer.concat([zeros, x]); - } - var hlen = hash.length; - var hbits = bits2octets(hash, q); - var v = Buffer.alloc(hlen); - v.fill(1); - var k = Buffer.alloc(hlen); - k = createHmac(algo, k).update(v).update(Buffer.from([0])).update(x).update(hbits).digest(); - v = createHmac(algo, k).update(v).digest(); - k = createHmac(algo, k).update(v).update(Buffer.from([1])).update(x).update(hbits).digest(); - v = createHmac(algo, k).update(v).digest(); - return { k: k, v: v }; -} - -function bits2int(obits, q) { - var bits = new BN(obits); - var shift = (obits.length << 3) - q.bitLength(); - if (shift > 0) { bits.ishrn(shift); } - return bits; -} - -function bits2octets(bits, q) { - bits = bits2int(bits, q); - bits = bits.mod(q); - var out = Buffer.from(bits.toArray()); - if (out.length < q.byteLength()) { - var zeros = Buffer.alloc(q.byteLength() - out.length); - out = Buffer.concat([zeros, out]); - } - return out; -} - -function makeKey(q, kv, algo) { - var t; - var k; - - do { - t = Buffer.alloc(0); - - while (t.length * 8 < q.bitLength()) { - kv.v = createHmac(algo, kv.k).update(kv.v).digest(); - t = Buffer.concat([t, kv.v]); - } - - k = bits2int(t, q); - kv.k = createHmac(algo, kv.k).update(kv.v).update(Buffer.from([0])).digest(); - kv.v = createHmac(algo, kv.k).update(kv.v).digest(); - } while (k.cmp(q) !== -1); - - return k; -} - -function makeR(g, k, p, q) { - return g.toRed(BN.mont(p)).redPow(k).fromRed().mod(q); -} - -module.exports = sign; -module.exports.getKey = getKey; -module.exports.makeKey = makeKey; - -},{"./curves.json":58,"bn.js":32,"browserify-rsa":55,"create-hmac":89,"elliptic":104,"parse-asn1":191,"safe-buffer":221}],61:[function(require,module,exports){ -'use strict'; - -// much of this based on https://github.com/indutny/self-signed/blob/gh-pages/lib/rsa.js -var Buffer = require('safe-buffer').Buffer; -var BN = require('bn.js'); -var EC = require('elliptic').ec; -var parseKeys = require('parse-asn1'); -var curves = require('./curves.json'); - -function verify(sig, hash, key, signType, tag) { - var pub = parseKeys(key); - if (pub.type === 'ec') { - // rsa keys can be interpreted as ecdsa ones in openssl - if (signType !== 'ecdsa' && signType !== 'ecdsa/rsa') { throw new Error('wrong public key type'); } - return ecVerify(sig, hash, pub); - } else if (pub.type === 'dsa') { - if (signType !== 'dsa') { throw new Error('wrong public key type'); } - return dsaVerify(sig, hash, pub); - } - if (signType !== 'rsa' && signType !== 'ecdsa/rsa') { throw new Error('wrong public key type'); } - - hash = Buffer.concat([tag, hash]); - var len = pub.modulus.byteLength(); - var pad = [1]; - var padNum = 0; - while (hash.length + pad.length + 2 < len) { - pad.push(0xff); - padNum += 1; - } - pad.push(0x00); - var i = -1; - while (++i < hash.length) { - pad.push(hash[i]); - } - pad = Buffer.from(pad); - var red = BN.mont(pub.modulus); - sig = new BN(sig).toRed(red); - - sig = sig.redPow(new BN(pub.publicExponent)); - sig = Buffer.from(sig.fromRed().toArray()); - var out = padNum < 8 ? 1 : 0; - len = Math.min(sig.length, pad.length); - if (sig.length !== pad.length) { out = 1; } - - i = -1; - while (++i < len) { out |= sig[i] ^ pad[i]; } - return out === 0; -} - -function ecVerify(sig, hash, pub) { - var curveId = curves[pub.data.algorithm.curve.join('.')]; - if (!curveId) { throw new Error('unknown curve ' + pub.data.algorithm.curve.join('.')); } - - var curve = new EC(curveId); - var pubkey = pub.data.subjectPrivateKey.data; - - return curve.verify(hash, sig, pubkey); -} - -function dsaVerify(sig, hash, pub) { - var p = pub.data.p; - var q = pub.data.q; - var g = pub.data.g; - var y = pub.data.pub_key; - var unpacked = parseKeys.signature.decode(sig, 'der'); - var s = unpacked.s; - var r = unpacked.r; - checkValue(s, q); - checkValue(r, q); - var montp = BN.mont(p); - var w = s.invm(q); - var v = g.toRed(montp) - .redPow(new BN(hash).mul(w).mod(q)) - .fromRed() - .mul(y.toRed(montp).redPow(r.mul(w).mod(q)).fromRed()) - .mod(p) - .mod(q); - return v.cmp(r) === 0; -} - -function checkValue(b, q) { - if (b.cmpn(0) <= 0) { throw new Error('invalid sig'); } - if (b.cmp(q) >= 0) { throw new Error('invalid sig'); } -} - -module.exports = verify; - -},{"./curves.json":58,"bn.js":32,"elliptic":104,"parse-asn1":191,"safe-buffer":221}],62:[function(require,module,exports){ -(function (Buffer){(function (){ -module.exports = function xor (a, b) { - var length = Math.min(a.length, b.length) - var buffer = new Buffer(length) - - for (var i = 0; i < length; ++i) { - buffer[i] = a[i] ^ b[i] - } - - return buffer -} - -}).call(this)}).call(this,require("buffer").Buffer) -},{"buffer":63}],63:[function(require,module,exports){ -(function (Buffer){(function (){ -/*! - * The buffer module from node.js, for the browser. - * - * @author Feross Aboukhadijeh - * @license MIT - */ -/* eslint-disable no-proto */ - -'use strict' - -var base64 = require('base64-js') -var ieee754 = require('ieee754') - -exports.Buffer = Buffer -exports.SlowBuffer = SlowBuffer -exports.INSPECT_MAX_BYTES = 50 - -var K_MAX_LENGTH = 0x7fffffff -exports.kMaxLength = K_MAX_LENGTH - -/** - * If `Buffer.TYPED_ARRAY_SUPPORT`: - * === true Use Uint8Array implementation (fastest) - * === false Print warning and recommend using `buffer` v4.x which has an Object - * implementation (most compatible, even IE6) - * - * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+, - * Opera 11.6+, iOS 4.2+. - * - * We report that the browser does not support typed arrays if the are not subclassable - * using __proto__. Firefox 4-29 lacks support for adding new properties to `Uint8Array` - * (See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438). IE 10 lacks support - * for __proto__ and has a buggy typed array implementation. - */ -Buffer.TYPED_ARRAY_SUPPORT = typedArraySupport() - -if (!Buffer.TYPED_ARRAY_SUPPORT && typeof console !== 'undefined' && - typeof console.error === 'function') { - console.error( - 'This browser lacks typed array (Uint8Array) support which is required by ' + - '`buffer` v5.x. Use `buffer` v4.x if you require old browser support.' - ) -} - -function typedArraySupport () { - // Can typed array instances can be augmented? - try { - var arr = new Uint8Array(1) - arr.__proto__ = { __proto__: Uint8Array.prototype, foo: function () { return 42 } } - return arr.foo() === 42 - } catch (e) { - return false - } -} - -Object.defineProperty(Buffer.prototype, 'parent', { - enumerable: true, - get: function () { - if (!Buffer.isBuffer(this)) return undefined - return this.buffer - } -}) - -Object.defineProperty(Buffer.prototype, 'offset', { - enumerable: true, - get: function () { - if (!Buffer.isBuffer(this)) return undefined - return this.byteOffset - } -}) - -function createBuffer (length) { - if (length > K_MAX_LENGTH) { - throw new RangeError('The value "' + length + '" is invalid for option "size"') - } - // Return an augmented `Uint8Array` instance - var buf = new Uint8Array(length) - buf.__proto__ = Buffer.prototype - return buf -} - -/** - * The Buffer constructor returns instances of `Uint8Array` that have their - * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of - * `Uint8Array`, so the returned instances will have all the node `Buffer` methods - * and the `Uint8Array` methods. Square bracket notation works as expected -- it - * returns a single octet. - * - * The `Uint8Array` prototype remains unmodified. - */ - -function Buffer (arg, encodingOrOffset, length) { - // Common case. - if (typeof arg === 'number') { - if (typeof encodingOrOffset === 'string') { - throw new TypeError( - 'The "string" argument must be of type string. Received type number' - ) - } - return allocUnsafe(arg) - } - return from(arg, encodingOrOffset, length) -} - -// Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97 -if (typeof Symbol !== 'undefined' && Symbol.species != null && - Buffer[Symbol.species] === Buffer) { - Object.defineProperty(Buffer, Symbol.species, { - value: null, - configurable: true, - enumerable: false, - writable: false - }) -} - -Buffer.poolSize = 8192 // not used by this implementation - -function from (value, encodingOrOffset, length) { - if (typeof value === 'string') { - return fromString(value, encodingOrOffset) - } - - if (ArrayBuffer.isView(value)) { - return fromArrayLike(value) - } - - if (value == null) { - throw TypeError( - 'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' + - 'or Array-like Object. Received type ' + (typeof value) - ) - } - - if (isInstance(value, ArrayBuffer) || - (value && isInstance(value.buffer, ArrayBuffer))) { - return fromArrayBuffer(value, encodingOrOffset, length) - } - - if (typeof value === 'number') { - throw new TypeError( - 'The "value" argument must not be of type number. Received type number' - ) - } - - var valueOf = value.valueOf && value.valueOf() - if (valueOf != null && valueOf !== value) { - return Buffer.from(valueOf, encodingOrOffset, length) - } - - var b = fromObject(value) - if (b) return b - - if (typeof Symbol !== 'undefined' && Symbol.toPrimitive != null && - typeof value[Symbol.toPrimitive] === 'function') { - return Buffer.from( - value[Symbol.toPrimitive]('string'), encodingOrOffset, length - ) - } - - throw new TypeError( - 'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' + - 'or Array-like Object. Received type ' + (typeof value) - ) -} - -/** - * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError - * if value is a number. - * Buffer.from(str[, encoding]) - * Buffer.from(array) - * Buffer.from(buffer) - * Buffer.from(arrayBuffer[, byteOffset[, length]]) - **/ -Buffer.from = function (value, encodingOrOffset, length) { - return from(value, encodingOrOffset, length) -} - -// Note: Change prototype *after* Buffer.from is defined to workaround Chrome bug: -// https://github.com/feross/buffer/pull/148 -Buffer.prototype.__proto__ = Uint8Array.prototype -Buffer.__proto__ = Uint8Array - -function assertSize (size) { - if (typeof size !== 'number') { - throw new TypeError('"size" argument must be of type number') - } else if (size < 0) { - throw new RangeError('The value "' + size + '" is invalid for option "size"') - } -} - -function alloc (size, fill, encoding) { - assertSize(size) - if (size <= 0) { - return createBuffer(size) - } - if (fill !== undefined) { - // Only pay attention to encoding if it's a string. This - // prevents accidentally sending in a number that would - // be interpretted as a start offset. - return typeof encoding === 'string' - ? createBuffer(size).fill(fill, encoding) - : createBuffer(size).fill(fill) - } - return createBuffer(size) -} - -/** - * Creates a new filled Buffer instance. - * alloc(size[, fill[, encoding]]) - **/ -Buffer.alloc = function (size, fill, encoding) { - return alloc(size, fill, encoding) -} - -function allocUnsafe (size) { - assertSize(size) - return createBuffer(size < 0 ? 0 : checked(size) | 0) -} - -/** - * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance. - * */ -Buffer.allocUnsafe = function (size) { - return allocUnsafe(size) -} -/** - * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance. - */ -Buffer.allocUnsafeSlow = function (size) { - return allocUnsafe(size) -} - -function fromString (string, encoding) { - if (typeof encoding !== 'string' || encoding === '') { - encoding = 'utf8' - } - - if (!Buffer.isEncoding(encoding)) { - throw new TypeError('Unknown encoding: ' + encoding) - } - - var length = byteLength(string, encoding) | 0 - var buf = createBuffer(length) - - var actual = buf.write(string, encoding) - - if (actual !== length) { - // Writing a hex string, for example, that contains invalid characters will - // cause everything after the first invalid character to be ignored. (e.g. - // 'abxxcd' will be treated as 'ab') - buf = buf.slice(0, actual) - } - - return buf -} - -function fromArrayLike (array) { - var length = array.length < 0 ? 0 : checked(array.length) | 0 - var buf = createBuffer(length) - for (var i = 0; i < length; i += 1) { - buf[i] = array[i] & 255 - } - return buf -} - -function fromArrayBuffer (array, byteOffset, length) { - if (byteOffset < 0 || array.byteLength < byteOffset) { - throw new RangeError('"offset" is outside of buffer bounds') - } - - if (array.byteLength < byteOffset + (length || 0)) { - throw new RangeError('"length" is outside of buffer bounds') - } - - var buf - if (byteOffset === undefined && length === undefined) { - buf = new Uint8Array(array) - } else if (length === undefined) { - buf = new Uint8Array(array, byteOffset) - } else { - buf = new Uint8Array(array, byteOffset, length) - } - - // Return an augmented `Uint8Array` instance - buf.__proto__ = Buffer.prototype - return buf -} - -function fromObject (obj) { - if (Buffer.isBuffer(obj)) { - var len = checked(obj.length) | 0 - var buf = createBuffer(len) - - if (buf.length === 0) { - return buf - } - - obj.copy(buf, 0, 0, len) - return buf - } - - if (obj.length !== undefined) { - if (typeof obj.length !== 'number' || numberIsNaN(obj.length)) { - return createBuffer(0) - } - return fromArrayLike(obj) - } - - if (obj.type === 'Buffer' && Array.isArray(obj.data)) { - return fromArrayLike(obj.data) - } -} - -function checked (length) { - // Note: cannot use `length < K_MAX_LENGTH` here because that fails when - // length is NaN (which is otherwise coerced to zero.) - if (length >= K_MAX_LENGTH) { - throw new RangeError('Attempt to allocate Buffer larger than maximum ' + - 'size: 0x' + K_MAX_LENGTH.toString(16) + ' bytes') - } - return length | 0 -} - -function SlowBuffer (length) { - if (+length != length) { // eslint-disable-line eqeqeq - length = 0 - } - return Buffer.alloc(+length) -} - -Buffer.isBuffer = function isBuffer (b) { - return b != null && b._isBuffer === true && - b !== Buffer.prototype // so Buffer.isBuffer(Buffer.prototype) will be false -} - -Buffer.compare = function compare (a, b) { - if (isInstance(a, Uint8Array)) a = Buffer.from(a, a.offset, a.byteLength) - if (isInstance(b, Uint8Array)) b = Buffer.from(b, b.offset, b.byteLength) - if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) { - throw new TypeError( - 'The "buf1", "buf2" arguments must be one of type Buffer or Uint8Array' - ) - } - - if (a === b) return 0 - - var x = a.length - var y = b.length - - for (var i = 0, len = Math.min(x, y); i < len; ++i) { - if (a[i] !== b[i]) { - x = a[i] - y = b[i] - break - } - } - - if (x < y) return -1 - if (y < x) return 1 - return 0 -} - -Buffer.isEncoding = function isEncoding (encoding) { - switch (String(encoding).toLowerCase()) { - case 'hex': - case 'utf8': - case 'utf-8': - case 'ascii': - case 'latin1': - case 'binary': - case 'base64': - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return true - default: - return false - } -} - -Buffer.concat = function concat (list, length) { - if (!Array.isArray(list)) { - throw new TypeError('"list" argument must be an Array of Buffers') - } - - if (list.length === 0) { - return Buffer.alloc(0) - } - - var i - if (length === undefined) { - length = 0 - for (i = 0; i < list.length; ++i) { - length += list[i].length - } - } - - var buffer = Buffer.allocUnsafe(length) - var pos = 0 - for (i = 0; i < list.length; ++i) { - var buf = list[i] - if (isInstance(buf, Uint8Array)) { - buf = Buffer.from(buf) - } - if (!Buffer.isBuffer(buf)) { - throw new TypeError('"list" argument must be an Array of Buffers') - } - buf.copy(buffer, pos) - pos += buf.length - } - return buffer -} - -function byteLength (string, encoding) { - if (Buffer.isBuffer(string)) { - return string.length - } - if (ArrayBuffer.isView(string) || isInstance(string, ArrayBuffer)) { - return string.byteLength - } - if (typeof string !== 'string') { - throw new TypeError( - 'The "string" argument must be one of type string, Buffer, or ArrayBuffer. ' + - 'Received type ' + typeof string - ) - } - - var len = string.length - var mustMatch = (arguments.length > 2 && arguments[2] === true) - if (!mustMatch && len === 0) return 0 - - // Use a for loop to avoid recursion - var loweredCase = false - for (;;) { - switch (encoding) { - case 'ascii': - case 'latin1': - case 'binary': - return len - case 'utf8': - case 'utf-8': - return utf8ToBytes(string).length - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return len * 2 - case 'hex': - return len >>> 1 - case 'base64': - return base64ToBytes(string).length - default: - if (loweredCase) { - return mustMatch ? -1 : utf8ToBytes(string).length // assume utf8 - } - encoding = ('' + encoding).toLowerCase() - loweredCase = true - } - } -} -Buffer.byteLength = byteLength - -function slowToString (encoding, start, end) { - var loweredCase = false - - // No need to verify that "this.length <= MAX_UINT32" since it's a read-only - // property of a typed array. - - // This behaves neither like String nor Uint8Array in that we set start/end - // to their upper/lower bounds if the value passed is out of range. - // undefined is handled specially as per ECMA-262 6th Edition, - // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization. - if (start === undefined || start < 0) { - start = 0 - } - // Return early if start > this.length. Done here to prevent potential uint32 - // coercion fail below. - if (start > this.length) { - return '' - } - - if (end === undefined || end > this.length) { - end = this.length - } - - if (end <= 0) { - return '' - } - - // Force coersion to uint32. This will also coerce falsey/NaN values to 0. - end >>>= 0 - start >>>= 0 - - if (end <= start) { - return '' - } - - if (!encoding) encoding = 'utf8' - - while (true) { - switch (encoding) { - case 'hex': - return hexSlice(this, start, end) - - case 'utf8': - case 'utf-8': - return utf8Slice(this, start, end) - - case 'ascii': - return asciiSlice(this, start, end) - - case 'latin1': - case 'binary': - return latin1Slice(this, start, end) - - case 'base64': - return base64Slice(this, start, end) - - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return utf16leSlice(this, start, end) - - default: - if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) - encoding = (encoding + '').toLowerCase() - loweredCase = true - } - } -} - -// This property is used by `Buffer.isBuffer` (and the `is-buffer` npm package) -// to detect a Buffer instance. It's not possible to use `instanceof Buffer` -// reliably in a browserify context because there could be multiple different -// copies of the 'buffer' package in use. This method works even for Buffer -// instances that were created from another copy of the `buffer` package. -// See: https://github.com/feross/buffer/issues/154 -Buffer.prototype._isBuffer = true - -function swap (b, n, m) { - var i = b[n] - b[n] = b[m] - b[m] = i -} - -Buffer.prototype.swap16 = function swap16 () { - var len = this.length - if (len % 2 !== 0) { - throw new RangeError('Buffer size must be a multiple of 16-bits') - } - for (var i = 0; i < len; i += 2) { - swap(this, i, i + 1) - } - return this -} - -Buffer.prototype.swap32 = function swap32 () { - var len = this.length - if (len % 4 !== 0) { - throw new RangeError('Buffer size must be a multiple of 32-bits') - } - for (var i = 0; i < len; i += 4) { - swap(this, i, i + 3) - swap(this, i + 1, i + 2) - } - return this -} - -Buffer.prototype.swap64 = function swap64 () { - var len = this.length - if (len % 8 !== 0) { - throw new RangeError('Buffer size must be a multiple of 64-bits') - } - for (var i = 0; i < len; i += 8) { - swap(this, i, i + 7) - swap(this, i + 1, i + 6) - swap(this, i + 2, i + 5) - swap(this, i + 3, i + 4) - } - return this -} - -Buffer.prototype.toString = function toString () { - var length = this.length - if (length === 0) return '' - if (arguments.length === 0) return utf8Slice(this, 0, length) - return slowToString.apply(this, arguments) -} - -Buffer.prototype.toLocaleString = Buffer.prototype.toString - -Buffer.prototype.equals = function equals (b) { - if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer') - if (this === b) return true - return Buffer.compare(this, b) === 0 -} - -Buffer.prototype.inspect = function inspect () { - var str = '' - var max = exports.INSPECT_MAX_BYTES - str = this.toString('hex', 0, max).replace(/(.{2})/g, '$1 ').trim() - if (this.length > max) str += ' ... ' - return '' -} - -Buffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) { - if (isInstance(target, Uint8Array)) { - target = Buffer.from(target, target.offset, target.byteLength) - } - if (!Buffer.isBuffer(target)) { - throw new TypeError( - 'The "target" argument must be one of type Buffer or Uint8Array. ' + - 'Received type ' + (typeof target) - ) - } - - if (start === undefined) { - start = 0 - } - if (end === undefined) { - end = target ? target.length : 0 - } - if (thisStart === undefined) { - thisStart = 0 - } - if (thisEnd === undefined) { - thisEnd = this.length - } - - if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) { - throw new RangeError('out of range index') - } - - if (thisStart >= thisEnd && start >= end) { - return 0 - } - if (thisStart >= thisEnd) { - return -1 - } - if (start >= end) { - return 1 - } - - start >>>= 0 - end >>>= 0 - thisStart >>>= 0 - thisEnd >>>= 0 - - if (this === target) return 0 - - var x = thisEnd - thisStart - var y = end - start - var len = Math.min(x, y) - - var thisCopy = this.slice(thisStart, thisEnd) - var targetCopy = target.slice(start, end) - - for (var i = 0; i < len; ++i) { - if (thisCopy[i] !== targetCopy[i]) { - x = thisCopy[i] - y = targetCopy[i] - break - } - } - - if (x < y) return -1 - if (y < x) return 1 - return 0 -} - -// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`, -// OR the last index of `val` in `buffer` at offset <= `byteOffset`. -// -// Arguments: -// - buffer - a Buffer to search -// - val - a string, Buffer, or number -// - byteOffset - an index into `buffer`; will be clamped to an int32 -// - encoding - an optional encoding, relevant is val is a string -// - dir - true for indexOf, false for lastIndexOf -function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) { - // Empty buffer means no match - if (buffer.length === 0) return -1 - - // Normalize byteOffset - if (typeof byteOffset === 'string') { - encoding = byteOffset - byteOffset = 0 - } else if (byteOffset > 0x7fffffff) { - byteOffset = 0x7fffffff - } else if (byteOffset < -0x80000000) { - byteOffset = -0x80000000 - } - byteOffset = +byteOffset // Coerce to Number. - if (numberIsNaN(byteOffset)) { - // byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer - byteOffset = dir ? 0 : (buffer.length - 1) - } - - // Normalize byteOffset: negative offsets start from the end of the buffer - if (byteOffset < 0) byteOffset = buffer.length + byteOffset - if (byteOffset >= buffer.length) { - if (dir) return -1 - else byteOffset = buffer.length - 1 - } else if (byteOffset < 0) { - if (dir) byteOffset = 0 - else return -1 - } - - // Normalize val - if (typeof val === 'string') { - val = Buffer.from(val, encoding) - } - - // Finally, search either indexOf (if dir is true) or lastIndexOf - if (Buffer.isBuffer(val)) { - // Special case: looking for empty string/buffer always fails - if (val.length === 0) { - return -1 - } - return arrayIndexOf(buffer, val, byteOffset, encoding, dir) - } else if (typeof val === 'number') { - val = val & 0xFF // Search for a byte value [0-255] - if (typeof Uint8Array.prototype.indexOf === 'function') { - if (dir) { - return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset) - } else { - return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset) - } - } - return arrayIndexOf(buffer, [ val ], byteOffset, encoding, dir) - } - - throw new TypeError('val must be string, number or Buffer') -} - -function arrayIndexOf (arr, val, byteOffset, encoding, dir) { - var indexSize = 1 - var arrLength = arr.length - var valLength = val.length - - if (encoding !== undefined) { - encoding = String(encoding).toLowerCase() - if (encoding === 'ucs2' || encoding === 'ucs-2' || - encoding === 'utf16le' || encoding === 'utf-16le') { - if (arr.length < 2 || val.length < 2) { - return -1 - } - indexSize = 2 - arrLength /= 2 - valLength /= 2 - byteOffset /= 2 - } - } - - function read (buf, i) { - if (indexSize === 1) { - return buf[i] - } else { - return buf.readUInt16BE(i * indexSize) - } - } - - var i - if (dir) { - var foundIndex = -1 - for (i = byteOffset; i < arrLength; i++) { - if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) { - if (foundIndex === -1) foundIndex = i - if (i - foundIndex + 1 === valLength) return foundIndex * indexSize - } else { - if (foundIndex !== -1) i -= i - foundIndex - foundIndex = -1 - } - } - } else { - if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength - for (i = byteOffset; i >= 0; i--) { - var found = true - for (var j = 0; j < valLength; j++) { - if (read(arr, i + j) !== read(val, j)) { - found = false - break - } - } - if (found) return i - } - } - - return -1 -} - -Buffer.prototype.includes = function includes (val, byteOffset, encoding) { - return this.indexOf(val, byteOffset, encoding) !== -1 -} - -Buffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) { - return bidirectionalIndexOf(this, val, byteOffset, encoding, true) -} - -Buffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) { - return bidirectionalIndexOf(this, val, byteOffset, encoding, false) -} - -function hexWrite (buf, string, offset, length) { - offset = Number(offset) || 0 - var remaining = buf.length - offset - if (!length) { - length = remaining - } else { - length = Number(length) - if (length > remaining) { - length = remaining - } - } - - var strLen = string.length - - if (length > strLen / 2) { - length = strLen / 2 - } - for (var i = 0; i < length; ++i) { - var parsed = parseInt(string.substr(i * 2, 2), 16) - if (numberIsNaN(parsed)) return i - buf[offset + i] = parsed - } - return i -} - -function utf8Write (buf, string, offset, length) { - return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length) -} - -function asciiWrite (buf, string, offset, length) { - return blitBuffer(asciiToBytes(string), buf, offset, length) -} - -function latin1Write (buf, string, offset, length) { - return asciiWrite(buf, string, offset, length) -} - -function base64Write (buf, string, offset, length) { - return blitBuffer(base64ToBytes(string), buf, offset, length) -} - -function ucs2Write (buf, string, offset, length) { - return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length) -} - -Buffer.prototype.write = function write (string, offset, length, encoding) { - // Buffer#write(string) - if (offset === undefined) { - encoding = 'utf8' - length = this.length - offset = 0 - // Buffer#write(string, encoding) - } else if (length === undefined && typeof offset === 'string') { - encoding = offset - length = this.length - offset = 0 - // Buffer#write(string, offset[, length][, encoding]) - } else if (isFinite(offset)) { - offset = offset >>> 0 - if (isFinite(length)) { - length = length >>> 0 - if (encoding === undefined) encoding = 'utf8' - } else { - encoding = length - length = undefined - } - } else { - throw new Error( - 'Buffer.write(string, encoding, offset[, length]) is no longer supported' - ) - } - - var remaining = this.length - offset - if (length === undefined || length > remaining) length = remaining - - if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) { - throw new RangeError('Attempt to write outside buffer bounds') - } - - if (!encoding) encoding = 'utf8' - - var loweredCase = false - for (;;) { - switch (encoding) { - case 'hex': - return hexWrite(this, string, offset, length) - - case 'utf8': - case 'utf-8': - return utf8Write(this, string, offset, length) - - case 'ascii': - return asciiWrite(this, string, offset, length) - - case 'latin1': - case 'binary': - return latin1Write(this, string, offset, length) - - case 'base64': - // Warning: maxLength not taken into account in base64Write - return base64Write(this, string, offset, length) - - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return ucs2Write(this, string, offset, length) - - default: - if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) - encoding = ('' + encoding).toLowerCase() - loweredCase = true - } - } -} - -Buffer.prototype.toJSON = function toJSON () { - return { - type: 'Buffer', - data: Array.prototype.slice.call(this._arr || this, 0) - } -} - -function base64Slice (buf, start, end) { - if (start === 0 && end === buf.length) { - return base64.fromByteArray(buf) - } else { - return base64.fromByteArray(buf.slice(start, end)) - } -} - -function utf8Slice (buf, start, end) { - end = Math.min(buf.length, end) - var res = [] - - var i = start - while (i < end) { - var firstByte = buf[i] - var codePoint = null - var bytesPerSequence = (firstByte > 0xEF) ? 4 - : (firstByte > 0xDF) ? 3 - : (firstByte > 0xBF) ? 2 - : 1 - - if (i + bytesPerSequence <= end) { - var secondByte, thirdByte, fourthByte, tempCodePoint - - switch (bytesPerSequence) { - case 1: - if (firstByte < 0x80) { - codePoint = firstByte - } - break - case 2: - secondByte = buf[i + 1] - if ((secondByte & 0xC0) === 0x80) { - tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F) - if (tempCodePoint > 0x7F) { - codePoint = tempCodePoint - } - } - break - case 3: - secondByte = buf[i + 1] - thirdByte = buf[i + 2] - if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) { - tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F) - if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) { - codePoint = tempCodePoint - } - } - break - case 4: - secondByte = buf[i + 1] - thirdByte = buf[i + 2] - fourthByte = buf[i + 3] - if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) { - tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F) - if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) { - codePoint = tempCodePoint - } - } - } - } - - if (codePoint === null) { - // we did not generate a valid codePoint so insert a - // replacement char (U+FFFD) and advance only 1 byte - codePoint = 0xFFFD - bytesPerSequence = 1 - } else if (codePoint > 0xFFFF) { - // encode to utf16 (surrogate pair dance) - codePoint -= 0x10000 - res.push(codePoint >>> 10 & 0x3FF | 0xD800) - codePoint = 0xDC00 | codePoint & 0x3FF - } - - res.push(codePoint) - i += bytesPerSequence - } - - return decodeCodePointsArray(res) -} - -// Based on http://stackoverflow.com/a/22747272/680742, the browser with -// the lowest limit is Chrome, with 0x10000 args. -// We go 1 magnitude less, for safety -var MAX_ARGUMENTS_LENGTH = 0x1000 - -function decodeCodePointsArray (codePoints) { - var len = codePoints.length - if (len <= MAX_ARGUMENTS_LENGTH) { - return String.fromCharCode.apply(String, codePoints) // avoid extra slice() - } - - // Decode in chunks to avoid "call stack size exceeded". - var res = '' - var i = 0 - while (i < len) { - res += String.fromCharCode.apply( - String, - codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH) - ) - } - return res -} - -function asciiSlice (buf, start, end) { - var ret = '' - end = Math.min(buf.length, end) - - for (var i = start; i < end; ++i) { - ret += String.fromCharCode(buf[i] & 0x7F) - } - return ret -} - -function latin1Slice (buf, start, end) { - var ret = '' - end = Math.min(buf.length, end) - - for (var i = start; i < end; ++i) { - ret += String.fromCharCode(buf[i]) - } - return ret -} - -function hexSlice (buf, start, end) { - var len = buf.length - - if (!start || start < 0) start = 0 - if (!end || end < 0 || end > len) end = len - - var out = '' - for (var i = start; i < end; ++i) { - out += toHex(buf[i]) - } - return out -} - -function utf16leSlice (buf, start, end) { - var bytes = buf.slice(start, end) - var res = '' - for (var i = 0; i < bytes.length; i += 2) { - res += String.fromCharCode(bytes[i] + (bytes[i + 1] * 256)) - } - return res -} - -Buffer.prototype.slice = function slice (start, end) { - var len = this.length - start = ~~start - end = end === undefined ? len : ~~end - - if (start < 0) { - start += len - if (start < 0) start = 0 - } else if (start > len) { - start = len - } - - if (end < 0) { - end += len - if (end < 0) end = 0 - } else if (end > len) { - end = len - } - - if (end < start) end = start - - var newBuf = this.subarray(start, end) - // Return an augmented `Uint8Array` instance - newBuf.__proto__ = Buffer.prototype - return newBuf -} - -/* - * Need to make sure that buffer isn't trying to write out of bounds. - */ -function checkOffset (offset, ext, length) { - if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint') - if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length') -} - -Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) { - offset = offset >>> 0 - byteLength = byteLength >>> 0 - if (!noAssert) checkOffset(offset, byteLength, this.length) - - var val = this[offset] - var mul = 1 - var i = 0 - while (++i < byteLength && (mul *= 0x100)) { - val += this[offset + i] * mul - } - - return val -} - -Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) { - offset = offset >>> 0 - byteLength = byteLength >>> 0 - if (!noAssert) { - checkOffset(offset, byteLength, this.length) - } - - var val = this[offset + --byteLength] - var mul = 1 - while (byteLength > 0 && (mul *= 0x100)) { - val += this[offset + --byteLength] * mul - } - - return val -} - -Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 1, this.length) - return this[offset] -} - -Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 2, this.length) - return this[offset] | (this[offset + 1] << 8) -} - -Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 2, this.length) - return (this[offset] << 8) | this[offset + 1] -} - -Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 4, this.length) - - return ((this[offset]) | - (this[offset + 1] << 8) | - (this[offset + 2] << 16)) + - (this[offset + 3] * 0x1000000) -} - -Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 4, this.length) - - return (this[offset] * 0x1000000) + - ((this[offset + 1] << 16) | - (this[offset + 2] << 8) | - this[offset + 3]) -} - -Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) { - offset = offset >>> 0 - byteLength = byteLength >>> 0 - if (!noAssert) checkOffset(offset, byteLength, this.length) - - var val = this[offset] - var mul = 1 - var i = 0 - while (++i < byteLength && (mul *= 0x100)) { - val += this[offset + i] * mul - } - mul *= 0x80 - - if (val >= mul) val -= Math.pow(2, 8 * byteLength) - - return val -} - -Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) { - offset = offset >>> 0 - byteLength = byteLength >>> 0 - if (!noAssert) checkOffset(offset, byteLength, this.length) - - var i = byteLength - var mul = 1 - var val = this[offset + --i] - while (i > 0 && (mul *= 0x100)) { - val += this[offset + --i] * mul - } - mul *= 0x80 - - if (val >= mul) val -= Math.pow(2, 8 * byteLength) - - return val -} - -Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 1, this.length) - if (!(this[offset] & 0x80)) return (this[offset]) - return ((0xff - this[offset] + 1) * -1) -} - -Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 2, this.length) - var val = this[offset] | (this[offset + 1] << 8) - return (val & 0x8000) ? val | 0xFFFF0000 : val -} - -Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 2, this.length) - var val = this[offset + 1] | (this[offset] << 8) - return (val & 0x8000) ? val | 0xFFFF0000 : val -} - -Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 4, this.length) - - return (this[offset]) | - (this[offset + 1] << 8) | - (this[offset + 2] << 16) | - (this[offset + 3] << 24) -} - -Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 4, this.length) - - return (this[offset] << 24) | - (this[offset + 1] << 16) | - (this[offset + 2] << 8) | - (this[offset + 3]) -} - -Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 4, this.length) - return ieee754.read(this, offset, true, 23, 4) -} - -Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 4, this.length) - return ieee754.read(this, offset, false, 23, 4) -} - -Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 8, this.length) - return ieee754.read(this, offset, true, 52, 8) -} - -Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 8, this.length) - return ieee754.read(this, offset, false, 52, 8) -} - -function checkInt (buf, value, offset, ext, max, min) { - if (!Buffer.isBuffer(buf)) throw new TypeError('"buffer" argument must be a Buffer instance') - if (value > max || value < min) throw new RangeError('"value" argument is out of bounds') - if (offset + ext > buf.length) throw new RangeError('Index out of range') -} - -Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) { - value = +value - offset = offset >>> 0 - byteLength = byteLength >>> 0 - if (!noAssert) { - var maxBytes = Math.pow(2, 8 * byteLength) - 1 - checkInt(this, value, offset, byteLength, maxBytes, 0) - } - - var mul = 1 - var i = 0 - this[offset] = value & 0xFF - while (++i < byteLength && (mul *= 0x100)) { - this[offset + i] = (value / mul) & 0xFF - } - - return offset + byteLength -} - -Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) { - value = +value - offset = offset >>> 0 - byteLength = byteLength >>> 0 - if (!noAssert) { - var maxBytes = Math.pow(2, 8 * byteLength) - 1 - checkInt(this, value, offset, byteLength, maxBytes, 0) - } - - var i = byteLength - 1 - var mul = 1 - this[offset + i] = value & 0xFF - while (--i >= 0 && (mul *= 0x100)) { - this[offset + i] = (value / mul) & 0xFF - } - - return offset + byteLength -} - -Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0) - this[offset] = (value & 0xff) - return offset + 1 -} - -Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) - this[offset] = (value & 0xff) - this[offset + 1] = (value >>> 8) - return offset + 2 -} - -Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) - this[offset] = (value >>> 8) - this[offset + 1] = (value & 0xff) - return offset + 2 -} - -Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) - this[offset + 3] = (value >>> 24) - this[offset + 2] = (value >>> 16) - this[offset + 1] = (value >>> 8) - this[offset] = (value & 0xff) - return offset + 4 -} - -Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) - this[offset] = (value >>> 24) - this[offset + 1] = (value >>> 16) - this[offset + 2] = (value >>> 8) - this[offset + 3] = (value & 0xff) - return offset + 4 -} - -Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) { - var limit = Math.pow(2, (8 * byteLength) - 1) - - checkInt(this, value, offset, byteLength, limit - 1, -limit) - } - - var i = 0 - var mul = 1 - var sub = 0 - this[offset] = value & 0xFF - while (++i < byteLength && (mul *= 0x100)) { - if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) { - sub = 1 - } - this[offset + i] = ((value / mul) >> 0) - sub & 0xFF - } - - return offset + byteLength -} - -Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) { - var limit = Math.pow(2, (8 * byteLength) - 1) - - checkInt(this, value, offset, byteLength, limit - 1, -limit) - } - - var i = byteLength - 1 - var mul = 1 - var sub = 0 - this[offset + i] = value & 0xFF - while (--i >= 0 && (mul *= 0x100)) { - if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) { - sub = 1 - } - this[offset + i] = ((value / mul) >> 0) - sub & 0xFF - } - - return offset + byteLength -} - -Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80) - if (value < 0) value = 0xff + value + 1 - this[offset] = (value & 0xff) - return offset + 1 -} - -Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) - this[offset] = (value & 0xff) - this[offset + 1] = (value >>> 8) - return offset + 2 -} - -Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) - this[offset] = (value >>> 8) - this[offset + 1] = (value & 0xff) - return offset + 2 -} - -Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) - this[offset] = (value & 0xff) - this[offset + 1] = (value >>> 8) - this[offset + 2] = (value >>> 16) - this[offset + 3] = (value >>> 24) - return offset + 4 -} - -Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) - if (value < 0) value = 0xffffffff + value + 1 - this[offset] = (value >>> 24) - this[offset + 1] = (value >>> 16) - this[offset + 2] = (value >>> 8) - this[offset + 3] = (value & 0xff) - return offset + 4 -} - -function checkIEEE754 (buf, value, offset, ext, max, min) { - if (offset + ext > buf.length) throw new RangeError('Index out of range') - if (offset < 0) throw new RangeError('Index out of range') -} - -function writeFloat (buf, value, offset, littleEndian, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) { - checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38) - } - ieee754.write(buf, value, offset, littleEndian, 23, 4) - return offset + 4 -} - -Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) { - return writeFloat(this, value, offset, true, noAssert) -} - -Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) { - return writeFloat(this, value, offset, false, noAssert) -} - -function writeDouble (buf, value, offset, littleEndian, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) { - checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308) - } - ieee754.write(buf, value, offset, littleEndian, 52, 8) - return offset + 8 -} - -Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) { - return writeDouble(this, value, offset, true, noAssert) -} - -Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) { - return writeDouble(this, value, offset, false, noAssert) -} - -// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) -Buffer.prototype.copy = function copy (target, targetStart, start, end) { - if (!Buffer.isBuffer(target)) throw new TypeError('argument should be a Buffer') - if (!start) start = 0 - if (!end && end !== 0) end = this.length - if (targetStart >= target.length) targetStart = target.length - if (!targetStart) targetStart = 0 - if (end > 0 && end < start) end = start - - // Copy 0 bytes; we're done - if (end === start) return 0 - if (target.length === 0 || this.length === 0) return 0 - - // Fatal error conditions - if (targetStart < 0) { - throw new RangeError('targetStart out of bounds') - } - if (start < 0 || start >= this.length) throw new RangeError('Index out of range') - if (end < 0) throw new RangeError('sourceEnd out of bounds') - - // Are we oob? - if (end > this.length) end = this.length - if (target.length - targetStart < end - start) { - end = target.length - targetStart + start - } - - var len = end - start - - if (this === target && typeof Uint8Array.prototype.copyWithin === 'function') { - // Use built-in when available, missing from IE11 - this.copyWithin(targetStart, start, end) - } else if (this === target && start < targetStart && targetStart < end) { - // descending copy from end - for (var i = len - 1; i >= 0; --i) { - target[i + targetStart] = this[i + start] - } - } else { - Uint8Array.prototype.set.call( - target, - this.subarray(start, end), - targetStart - ) - } - - return len -} - -// Usage: -// buffer.fill(number[, offset[, end]]) -// buffer.fill(buffer[, offset[, end]]) -// buffer.fill(string[, offset[, end]][, encoding]) -Buffer.prototype.fill = function fill (val, start, end, encoding) { - // Handle string cases: - if (typeof val === 'string') { - if (typeof start === 'string') { - encoding = start - start = 0 - end = this.length - } else if (typeof end === 'string') { - encoding = end - end = this.length - } - if (encoding !== undefined && typeof encoding !== 'string') { - throw new TypeError('encoding must be a string') - } - if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) { - throw new TypeError('Unknown encoding: ' + encoding) - } - if (val.length === 1) { - var code = val.charCodeAt(0) - if ((encoding === 'utf8' && code < 128) || - encoding === 'latin1') { - // Fast path: If `val` fits into a single byte, use that numeric value. - val = code - } - } - } else if (typeof val === 'number') { - val = val & 255 - } - - // Invalid ranges are not set to a default, so can range check early. - if (start < 0 || this.length < start || this.length < end) { - throw new RangeError('Out of range index') - } - - if (end <= start) { - return this - } - - start = start >>> 0 - end = end === undefined ? this.length : end >>> 0 - - if (!val) val = 0 - - var i - if (typeof val === 'number') { - for (i = start; i < end; ++i) { - this[i] = val - } - } else { - var bytes = Buffer.isBuffer(val) - ? val - : Buffer.from(val, encoding) - var len = bytes.length - if (len === 0) { - throw new TypeError('The value "' + val + - '" is invalid for argument "value"') - } - for (i = 0; i < end - start; ++i) { - this[i + start] = bytes[i % len] - } - } - - return this -} - -// HELPER FUNCTIONS -// ================ - -var INVALID_BASE64_RE = /[^+/0-9A-Za-z-_]/g - -function base64clean (str) { - // Node takes equal signs as end of the Base64 encoding - str = str.split('=')[0] - // Node strips out invalid characters like \n and \t from the string, base64-js does not - str = str.trim().replace(INVALID_BASE64_RE, '') - // Node converts strings with length < 2 to '' - if (str.length < 2) return '' - // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not - while (str.length % 4 !== 0) { - str = str + '=' - } - return str -} - -function toHex (n) { - if (n < 16) return '0' + n.toString(16) - return n.toString(16) -} - -function utf8ToBytes (string, units) { - units = units || Infinity - var codePoint - var length = string.length - var leadSurrogate = null - var bytes = [] - - for (var i = 0; i < length; ++i) { - codePoint = string.charCodeAt(i) - - // is surrogate component - if (codePoint > 0xD7FF && codePoint < 0xE000) { - // last char was a lead - if (!leadSurrogate) { - // no lead yet - if (codePoint > 0xDBFF) { - // unexpected trail - if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) - continue - } else if (i + 1 === length) { - // unpaired lead - if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) - continue - } - - // valid lead - leadSurrogate = codePoint - - continue - } - - // 2 leads in a row - if (codePoint < 0xDC00) { - if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) - leadSurrogate = codePoint - continue - } - - // valid surrogate pair - codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000 - } else if (leadSurrogate) { - // valid bmp char, but last char was a lead - if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) - } - - leadSurrogate = null - - // encode utf8 - if (codePoint < 0x80) { - if ((units -= 1) < 0) break - bytes.push(codePoint) - } else if (codePoint < 0x800) { - if ((units -= 2) < 0) break - bytes.push( - codePoint >> 0x6 | 0xC0, - codePoint & 0x3F | 0x80 - ) - } else if (codePoint < 0x10000) { - if ((units -= 3) < 0) break - bytes.push( - codePoint >> 0xC | 0xE0, - codePoint >> 0x6 & 0x3F | 0x80, - codePoint & 0x3F | 0x80 - ) - } else if (codePoint < 0x110000) { - if ((units -= 4) < 0) break - bytes.push( - codePoint >> 0x12 | 0xF0, - codePoint >> 0xC & 0x3F | 0x80, - codePoint >> 0x6 & 0x3F | 0x80, - codePoint & 0x3F | 0x80 - ) - } else { - throw new Error('Invalid code point') - } - } - - return bytes -} - -function asciiToBytes (str) { - var byteArray = [] - for (var i = 0; i < str.length; ++i) { - // Node's code seems to be doing this and not & 0x7F.. - byteArray.push(str.charCodeAt(i) & 0xFF) - } - return byteArray -} - -function utf16leToBytes (str, units) { - var c, hi, lo - var byteArray = [] - for (var i = 0; i < str.length; ++i) { - if ((units -= 2) < 0) break - - c = str.charCodeAt(i) - hi = c >> 8 - lo = c % 256 - byteArray.push(lo) - byteArray.push(hi) - } - - return byteArray -} - -function base64ToBytes (str) { - return base64.toByteArray(base64clean(str)) -} - -function blitBuffer (src, dst, offset, length) { - for (var i = 0; i < length; ++i) { - if ((i + offset >= dst.length) || (i >= src.length)) break - dst[i + offset] = src[i] - } - return i -} - -// ArrayBuffer or Uint8Array objects from other contexts (i.e. iframes) do not pass -// the `instanceof` check but they should be treated as of that type. -// See: https://github.com/feross/buffer/issues/166 -function isInstance (obj, type) { - return obj instanceof type || - (obj != null && obj.constructor != null && obj.constructor.name != null && - obj.constructor.name === type.name) -} -function numberIsNaN (obj) { - // For IE11 support - return obj !== obj // eslint-disable-line no-self-compare -} - -}).call(this)}).call(this,require("buffer").Buffer) -},{"base64-js":31,"buffer":63,"ieee754":154}],64:[function(require,module,exports){ -'use strict'; - -var GetIntrinsic = require('get-intrinsic'); - -var callBind = require('./'); - -var $indexOf = callBind(GetIntrinsic('String.prototype.indexOf')); - -module.exports = function callBoundIntrinsic(name, allowMissing) { - var intrinsic = GetIntrinsic(name, !!allowMissing); - if (typeof intrinsic === 'function' && $indexOf(name, '.prototype.') > -1) { - return callBind(intrinsic); - } - return intrinsic; -}; - -},{"./":65,"get-intrinsic":133}],65:[function(require,module,exports){ -'use strict'; - -var bind = require('function-bind'); -var GetIntrinsic = require('get-intrinsic'); -var setFunctionLength = require('set-function-length'); - -var $TypeError = require('es-errors/type'); -var $apply = GetIntrinsic('%Function.prototype.apply%'); -var $call = GetIntrinsic('%Function.prototype.call%'); -var $reflectApply = GetIntrinsic('%Reflect.apply%', true) || bind.call($call, $apply); - -var $defineProperty = require('es-define-property'); -var $max = GetIntrinsic('%Math.max%'); - -module.exports = function callBind(originalFunction) { - if (typeof originalFunction !== 'function') { - throw new $TypeError('a function is required'); - } - var func = $reflectApply(bind, $call, arguments); - return setFunctionLength( - func, - 1 + $max(0, originalFunction.length - (arguments.length - 1)), - true - ); -}; - -var applyBind = function applyBind() { - return $reflectApply(bind, $apply, arguments); -}; - -if ($defineProperty) { - $defineProperty(module.exports, 'apply', { value: applyBind }); -} else { - module.exports.apply = applyBind; -} - -},{"es-define-property":121,"es-errors/type":127,"function-bind":132,"get-intrinsic":133,"set-function-length":223}],66:[function(require,module,exports){ -'use strict' - -exports.Commented = require('./commented') -exports.Diagnose = require('./diagnose') -exports.Decoder = require('./decoder') -exports.Encoder = require('./encoder') -exports.Simple = require('./simple') -exports.Tagged = require('./tagged') -exports.Map = require('./map') - -/** - * Convenience name for {@linkcode Commented.comment}. - */ -exports.comment = exports.Commented.comment - -/** - * Convenience name for {@linkcode Decoder.decodeAll}. - */ -exports.decodeAll = exports.Decoder.decodeAll - -/** - * Convenience name for {@linkcode Decoder.decodeFirst}. - */ -exports.decodeFirst = exports.Decoder.decodeFirst - -/** - * Convenience name for {@linkcode Decoder.decodeAllSync}. - */ -exports.decodeAllSync = exports.Decoder.decodeAllSync - -/** - * Convenience name for {@linkcode Decoder.decodeFirstSync}. - */ -exports.decodeFirstSync = exports.Decoder.decodeFirstSync - -/** - * Convenience name for {@linkcode Diagnose.diagnose}. - */ -exports.diagnose = exports.Diagnose.diagnose - -/** - * Convenience name for {@linkcode Encoder.encode}. - */ -exports.encode = exports.Encoder.encode - -/** - * Convenience name for {@linkcode Encoder.encodeCanonical}. - */ -exports.encodeCanonical = exports.Encoder.encodeCanonical - -/** - * Convenience name for {@linkcode Encoder.encodeOne}. - */ -exports.encodeOne = exports.Encoder.encodeOne - -/** - * Convenience name for {@linkcode Encoder.encodeAsync}. - */ -exports.encodeAsync = exports.Encoder.encodeAsync - -/** - * Convenience name for {@linkcode Decoder.decodeFirstSync}. - */ -exports.decode = exports.Decoder.decodeFirstSync - -/** - * The codec information for - * {@link https://github.com/Level/encoding-down encoding-down}, which is a - * codec framework for leveldb. CBOR is a particularly convenient format for - * both keys and values, as it can deal with a lot of types that JSON can't - * handle without losing type information. - * - * @example - * const level = require('level') - * const cbor = require('cbor') - * - * async function putget() { - * const db = level('./db', { - * keyEncoding: cbor.leveldb, - * valueEncoding: cbor.leveldb, - * }) - * - * await db.put({a: 1}, 9857298342094820394820394820398234092834n) - * const val = await db.get({a: 1}) - * } - */ -exports.leveldb = { - decode: exports.Decoder.decodeFirstSync, - encode: exports.Encoder.encode, - buffer: true, - name: 'cbor', -} - -/** - * Reset everything that we can predict a plugin might have altered in good - * faith. For now that includes the default set of tags that decoding and - * encoding will use. - */ -exports.reset = function reset() { - exports.Encoder.reset() - exports.Tagged.reset() -} - -},{"./commented":67,"./decoder":69,"./diagnose":70,"./encoder":71,"./map":72,"./simple":73,"./tagged":74}],67:[function(require,module,exports){ -'use strict' - -const stream = require('stream') -const utils = require('./utils') -const Decoder = require('./decoder') -const NoFilter = require('nofilter') -const {MT, NUMBYTES, SYMS} = require('./constants') -const {Buffer} = require('buffer') - -function plural(c) { - if (c > 1) { - return 's' - } - return '' -} - -/** - * @typedef CommentOptions - * @property {number} [max_depth=10] How many times to indent - * the dashes. - * @property {number} [depth=1] Initial indentation depth. - * @property {boolean} [no_summary=false] If true, omit the summary - * of the full bytes read at the end. - * @property {object} [tags] Mapping from tag number to function(v), - * where v is the decoded value that comes after the tag, and where the - * function returns the correctly-created value for that tag. - * @property {boolean} [preferWeb=false] If true, prefer Uint8Arrays to - * be generated instead of node Buffers. This might turn on some more - * changes in the future, so forward-compatibility is not guaranteed yet. - * @property {BufferEncoding} [encoding='hex'] Encoding to use for input, if it - * is a string. - */ -/** - * @callback commentCallback - * @param {Error} [error] If one was generated. - * @param {string} [commented] The comment string. - * @returns {void} - */ -/** - * Normalize inputs to the static functions. - * - * @param {CommentOptions|commentCallback|string|number} opts Encoding, - * max_depth, or callback. - * @param {commentCallback} [cb] Called on completion. - * @returns {{options: CommentOptions, cb: commentCallback}} Normalized value. - * @throws {TypeError} Unknown option type. - * @private - */ -function normalizeOptions(opts, cb) { - switch (typeof opts) { - case 'function': - return {options: {}, cb: /** @type {commentCallback} */ (opts)} - case 'string': - return {options: {encoding: /** @type {BufferEncoding} */ (opts)}, cb} - case 'number': - return {options: {max_depth: opts}, cb} - case 'object': - return {options: opts || {}, cb} - default: - throw new TypeError('Unknown option type') - } -} - -/** - * Generate the expanded format of RFC 8949, section 3.2.2. - * - * @extends stream.Transform - */ -class Commented extends stream.Transform { - /** - * Create a CBOR commenter. - * - * @param {CommentOptions} [options={}] Stream options. - */ - constructor(options = {}) { - const { - depth = 1, - max_depth = 10, - no_summary = false, - // Decoder options - tags = {}, - preferWeb, - encoding, - // Stream.Transform options - ...superOpts - } = options - - super({ - ...superOpts, - readableObjectMode: false, - writableObjectMode: false, - }) - - this.depth = depth - this.max_depth = max_depth - this.all = new NoFilter() - - if (!tags[24]) { - tags[24] = this._tag_24.bind(this) - } - this.parser = new Decoder({ - tags, - max_depth, - preferWeb, - encoding, - }) - this.parser.on('value', this._on_value.bind(this)) - this.parser.on('start', this._on_start.bind(this)) - this.parser.on('start-string', this._on_start_string.bind(this)) - this.parser.on('stop', this._on_stop.bind(this)) - this.parser.on('more-bytes', this._on_more.bind(this)) - this.parser.on('error', this._on_error.bind(this)) - if (!no_summary) { - this.parser.on('data', this._on_data.bind(this)) - } - this.parser.bs.on('read', this._on_read.bind(this)) - } - - /** - * @param {Buffer} v Descend into embedded CBOR. - * @private - */ - _tag_24(v) { - const c = new Commented({depth: this.depth + 1, no_summary: true}) - - c.on('data', b => this.push(b)) - c.on('error', er => this.emit('error', er)) - c.end(v) - } - - _transform(fresh, encoding, cb) { - this.parser.write(fresh, encoding, cb) - } - - _flush(cb) { - // TODO: find the test that covers this, and look at the return value - return this.parser._flush(cb) - } - - /** - * Comment on an input Buffer or string, creating a string passed to the - * callback. If callback not specified, a promise is returned. - * - * @static - * @param {string|Buffer|ArrayBuffer|Uint8Array|Uint8ClampedArray - * |DataView|stream.Readable} input Something to parse. - * @param {CommentOptions|commentCallback|string|number} [options={}] - * Encoding, max_depth, or callback. - * @param {commentCallback} [cb] If specified, called on completion. - * @returns {Promise} If cb not specified. - * @throws {Error} Input required. - */ - static comment(input, options = {}, cb = null) { - if (input == null) { - throw new Error('input required') - } - ({options, cb} = normalizeOptions(options, cb)) - const bs = new NoFilter() - const {encoding = 'hex', ...opts} = options - const d = new Commented(opts) - let p = null - - if (typeof cb === 'function') { - d.on('end', () => { - cb(null, bs.toString('utf8')) - }) - d.on('error', cb) - } else { - p = new Promise((resolve, reject) => { - d.on('end', () => { - resolve(bs.toString('utf8')) - }) - d.on('error', reject) - }) - } - d.pipe(bs) - utils.guessEncoding(input, encoding).pipe(d) - return p - } - - /** - * @ignore - */ - _on_error(er) { - this.push('ERROR: ') - this.push(er.toString()) - this.push('\n') - } - - /** - * @ignore - */ - _on_read(buf) { - this.all.write(buf) - const hex = buf.toString('hex') - - this.push(new Array(this.depth + 1).join(' ')) - this.push(hex) - - let ind = ((this.max_depth - this.depth) * 2) - hex.length - if (ind < 1) { - ind = 1 - } - this.push(new Array(ind + 1).join(' ')) - this.push('-- ') - } - - /** - * @ignore - */ - _on_more(mt, len, parent_mt, pos) { - let desc = '' - - this.depth++ - switch (mt) { - case MT.POS_INT: - desc = 'Positive number,' - break - case MT.NEG_INT: - desc = 'Negative number,' - break - case MT.ARRAY: - desc = 'Array, length' - break - case MT.MAP: - desc = 'Map, count' - break - case MT.BYTE_STRING: - desc = 'Bytes, length' - break - case MT.UTF8_STRING: - desc = 'String, length' - break - case MT.SIMPLE_FLOAT: - if (len === 1) { - desc = 'Simple value,' - } else { - desc = 'Float,' - } - break - } - this.push(`${desc} next ${len} byte${plural(len)}\n`) - } - - /** - * @ignore - */ - _on_start_string(mt, len, parent_mt, pos) { - let desc = '' - - this.depth++ - switch (mt) { - case MT.BYTE_STRING: - desc = `Bytes, length: ${len}` - break - case MT.UTF8_STRING: - desc = `String, length: ${len.toString()}` - break - } - this.push(`${desc}\n`) - } - - /** - * @ignore - */ - _on_start(mt, tag, parent_mt, pos) { - this.depth++ - switch (parent_mt) { - case MT.ARRAY: - this.push(`[${pos}], `) - break - case MT.MAP: - if (pos % 2) { - this.push(`{Val:${Math.floor(pos / 2)}}, `) - } else { - this.push(`{Key:${Math.floor(pos / 2)}}, `) - } - break - } - switch (mt) { - case MT.TAG: - this.push(`Tag #${tag}`) - if (tag === 24) { - this.push(' Encoded CBOR data item') - } - break - case MT.ARRAY: - if (tag === SYMS.STREAM) { - this.push('Array (streaming)') - } else { - this.push(`Array, ${tag} item${plural(tag)}`) - } - break - case MT.MAP: - if (tag === SYMS.STREAM) { - this.push('Map (streaming)') - } else { - this.push(`Map, ${tag} pair${plural(tag)}`) - } - break - case MT.BYTE_STRING: - this.push('Bytes (streaming)') - break - case MT.UTF8_STRING: - this.push('String (streaming)') - break - } - this.push('\n') - } - - /** - * @ignore - */ - _on_stop(mt) { - this.depth-- - } - - /** - * @private - */ - _on_value(val, parent_mt, pos, ai) { - if (val !== SYMS.BREAK) { - switch (parent_mt) { - case MT.ARRAY: - this.push(`[${pos}], `) - break - case MT.MAP: - if (pos % 2) { - this.push(`{Val:${Math.floor(pos / 2)}}, `) - } else { - this.push(`{Key:${Math.floor(pos / 2)}}, `) - } - break - } - } - const str = utils.cborValueToString(val, -Infinity) - - if ((typeof val === 'string') || - (Buffer.isBuffer(val))) { - if (val.length > 0) { - this.push(str) - this.push('\n') - } - this.depth-- - } else { - this.push(str) - this.push('\n') - } - - switch (ai) { - case NUMBYTES.ONE: - case NUMBYTES.TWO: - case NUMBYTES.FOUR: - case NUMBYTES.EIGHT: - this.depth-- - } - } - - /** - * @ignore - */ - _on_data() { - this.push('0x') - this.push(this.all.read().toString('hex')) - this.push('\n') - } -} - -module.exports = Commented - -},{"./constants":68,"./decoder":69,"./utils":75,"buffer":63,"nofilter":181,"stream":232}],68:[function(require,module,exports){ -'use strict' - -/** - * @enum {number} - */ -exports.MT = { - POS_INT: 0, - NEG_INT: 1, - BYTE_STRING: 2, - UTF8_STRING: 3, - ARRAY: 4, - MAP: 5, - TAG: 6, - SIMPLE_FLOAT: 7, -} - -/** - * @enum {number} - */ -exports.TAG = { - DATE_STRING: 0, - DATE_EPOCH: 1, - POS_BIGINT: 2, - NEG_BIGINT: 3, - DECIMAL_FRAC: 4, - BIGFLOAT: 5, - BASE64URL_EXPECTED: 21, - BASE64_EXPECTED: 22, - BASE16_EXPECTED: 23, - CBOR: 24, - URI: 32, - BASE64URL: 33, - BASE64: 34, - REGEXP: 35, - MIME: 36, - // https://github.com/input-output-hk/cbor-sets-spec/blob/master/CBOR_SETS.md - SET: 258, -} - -/** - * @enum {number} - */ -exports.NUMBYTES = { - ZERO: 0, - ONE: 24, - TWO: 25, - FOUR: 26, - EIGHT: 27, - INDEFINITE: 31, -} - -/** - * @enum {number} - */ -exports.SIMPLE = { - FALSE: 20, - TRUE: 21, - NULL: 22, - UNDEFINED: 23, -} - -exports.SYMS = { - NULL: Symbol.for('github.com/hildjj/node-cbor/null'), - UNDEFINED: Symbol.for('github.com/hildjj/node-cbor/undef'), - PARENT: Symbol.for('github.com/hildjj/node-cbor/parent'), - BREAK: Symbol.for('github.com/hildjj/node-cbor/break'), - STREAM: Symbol.for('github.com/hildjj/node-cbor/stream'), -} - -exports.SHIFT32 = 0x100000000 - -exports.BI = { - MINUS_ONE: BigInt(-1), - NEG_MAX: BigInt(-1) - BigInt(Number.MAX_SAFE_INTEGER), - MAXINT32: BigInt('0xffffffff'), - MAXINT64: BigInt('0xffffffffffffffff'), - SHIFT32: BigInt(exports.SHIFT32), -} - - -},{}],69:[function(require,module,exports){ -'use strict' - -const BinaryParseStream = require('../vendor/binary-parse-stream') -const Tagged = require('./tagged') -const Simple = require('./simple') -const utils = require('./utils') -const NoFilter = require('nofilter') -const stream = require('stream') -const constants = require('./constants') -const {MT, NUMBYTES, SYMS, BI} = constants -const {Buffer} = require('buffer') - -const COUNT = Symbol('count') -const MAJOR = Symbol('major type') -const ERROR = Symbol('error') -const NOT_FOUND = Symbol('not found') - -function parentArray(parent, typ, count) { - const a = [] - - a[COUNT] = count - a[SYMS.PARENT] = parent - a[MAJOR] = typ - return a -} - -function parentBufferStream(parent, typ) { - const b = new NoFilter() - - b[COUNT] = -1 - b[SYMS.PARENT] = parent - b[MAJOR] = typ - return b -} - -class UnexpectedDataError extends Error { - constructor(byte, value) { - super(`Unexpected data: 0x${byte.toString(16)}`) - this.name = 'UnexpectedDataError' - this.byte = byte - this.value = value - } -} - -/** - * Things that can act as inputs, from which a NoFilter can be created. - * - * @typedef {string|Buffer|ArrayBuffer|Uint8Array|Uint8ClampedArray - * |DataView|stream.Readable} BufferLike - */ -/** - * @typedef ExtendedResults - * @property {any} value The value that was found. - * @property {number} length The number of bytes of the original input that - * were read. - * @property {Buffer} bytes The bytes of the original input that were used - * to produce the value. - * @property {Buffer} [unused] The bytes that were left over from the original - * input. This property only exists if {@linkcode Decoder.decodeFirst} or - * {@linkcode Decoder.decodeFirstSync} was called. - */ -/** - * @typedef DecoderOptions - * @property {number} [max_depth=-1] The maximum depth to parse. - * Use -1 for "until you run out of memory". Set this to a finite - * positive number for un-trusted inputs. Most standard inputs won't nest - * more than 100 or so levels; I've tested into the millions before - * running out of memory. - * @property {Tagged.TagMap} [tags] Mapping from tag number to function(v), - * where v is the decoded value that comes after the tag, and where the - * function returns the correctly-created value for that tag. - * @property {boolean} [preferWeb=false] If true, prefer Uint8Arrays to - * be generated instead of node Buffers. This might turn on some more - * changes in the future, so forward-compatibility is not guaranteed yet. - * @property {BufferEncoding} [encoding='hex'] The encoding of the input. - * Ignored if input is a Buffer. - * @property {boolean} [required=false] Should an error be thrown when no - * data is in the input? - * @property {boolean} [extendedResults=false] If true, emit extended - * results, which will be an object with shape {@link ExtendedResults}. - * The value will already have been null-checked. - * @property {boolean} [preventDuplicateKeys=false] If true, error is - * thrown if a map has duplicate keys. - */ -/** - * @callback decodeCallback - * @param {Error} [error] If one was generated. - * @param {any} [value] The decoded value. - * @returns {void} - */ -/** - * @param {DecoderOptions|decodeCallback|string} opts Options, - * the callback, or input incoding. - * @param {decodeCallback} [cb] Called on completion. - * @returns {{options: DecoderOptions, cb: decodeCallback}} Normalized. - * @throws {TypeError} On unknown option type. - * @private - */ -function normalizeOptions(opts, cb) { - switch (typeof opts) { - case 'function': - return {options: {}, cb: /** @type {decodeCallback} */ (opts)} - case 'string': - return {options: {encoding: /** @type {BufferEncoding} */ (opts)}, cb} - case 'object': - return {options: opts || {}, cb} - default: - throw new TypeError('Unknown option type') - } -} - -/** - * Decode a stream of CBOR bytes by transforming them into equivalent - * JavaScript data. Because of the limitations of Node object streams, - * special symbols are emitted instead of NULL or UNDEFINED. Fix those - * up by calling {@link Decoder.nullcheck}. - * - * @extends BinaryParseStream - */ -class Decoder extends BinaryParseStream { - /** - * Create a parsing stream. - * - * @param {DecoderOptions} [options={}] Options. - */ - constructor(options = {}) { - const { - tags = {}, - max_depth = -1, - preferWeb = false, - required = false, - encoding = 'hex', - extendedResults = false, - preventDuplicateKeys = false, - ...superOpts - } = options - - super({defaultEncoding: encoding, ...superOpts}) - - this.running = true - this.max_depth = max_depth - this.tags = tags - this.preferWeb = preferWeb - this.extendedResults = extendedResults - this.required = required - this.preventDuplicateKeys = preventDuplicateKeys - - if (extendedResults) { - this.bs.on('read', this._onRead.bind(this)) - this.valueBytes = /** @type {NoFilter} */ (new NoFilter()) - } - } - - /** - * Check the given value for a symbol encoding a NULL or UNDEFINED value in - * the CBOR stream. - * - * @static - * @param {any} val The value to check. - * @returns {any} The corrected value. - * @throws {Error} Nothing was found. - * @example - * myDecoder.on('data', val => { - * val = Decoder.nullcheck(val) - * // ... - * }) - */ - static nullcheck(val) { - switch (val) { - case SYMS.NULL: - return null - case SYMS.UNDEFINED: - return undefined - // Leaving this in for now as belt-and-suspenders, but I'm pretty sure - // it can't happen. - /* istanbul ignore next */ - case NOT_FOUND: - /* istanbul ignore next */ - throw new Error('Value not found') - default: - return val - } - } - - /** - * Decode the first CBOR item in the input, synchronously. This will throw - * an exception if the input is not valid CBOR, or if there are more bytes - * left over at the end (if options.extendedResults is not true). - * - * @static - * @param {BufferLike} input If a Readable stream, must have - * received the `readable` event already, or you will get an error - * claiming "Insufficient data". - * @param {DecoderOptions|string} [options={}] Options or encoding for input. - * @returns {ExtendedResults|any} The decoded value. - * @throws {UnexpectedDataError} Data is left over after decoding. - * @throws {Error} Insufficient data. - */ - static decodeFirstSync(input, options = {}) { - if (input == null) { - throw new TypeError('input required') - } - ({options} = normalizeOptions(options)) - const {encoding = 'hex', ...opts} = options - const c = new Decoder(opts) - const s = utils.guessEncoding(input, encoding) - - // For/of doesn't work when you need to call next() with a value - // generator created by parser will be "done" after each CBOR entity - // parser will yield numbers of bytes that it wants - const parser = c._parse() - let state = parser.next() - - while (!state.done) { - const b = s.read(state.value) - - if ((b == null) || (b.length !== state.value)) { - throw new Error('Insufficient data') - } - if (c.extendedResults) { - c.valueBytes.write(b) - } - state = parser.next(b) - } - - let val = null - if (c.extendedResults) { - val = state.value - val.unused = s.read() - } else { - val = Decoder.nullcheck(state.value) - if (s.length > 0) { - const nextByte = s.read(1) - - s.unshift(nextByte) - throw new UnexpectedDataError(nextByte[0], val) - } - } - return val - } - - /** - * Decode all of the CBOR items in the input into an array. This will throw - * an exception if the input is not valid CBOR; a zero-length input will - * return an empty array. - * - * @static - * @param {BufferLike} input What to parse? - * @param {DecoderOptions|string} [options={}] Options or encoding - * for input. - * @returns {Array|Array} Array of all found items. - * @throws {TypeError} No input provided. - * @throws {Error} Insufficient data provided. - */ - static decodeAllSync(input, options = {}) { - if (input == null) { - throw new TypeError('input required') - } - ({options} = normalizeOptions(options)) - const {encoding = 'hex', ...opts} = options - const c = new Decoder(opts) - const s = utils.guessEncoding(input, encoding) - const res = [] - - while (s.length > 0) { - const parser = c._parse() - let state = parser.next() - - while (!state.done) { - const b = s.read(state.value) - - if ((b == null) || (b.length !== state.value)) { - throw new Error('Insufficient data') - } - if (c.extendedResults) { - c.valueBytes.write(b) - } - state = parser.next(b) - } - res.push(Decoder.nullcheck(state.value)) - } - return res - } - - /** - * Decode the first CBOR item in the input. This will error if there are - * more bytes left over at the end (if options.extendedResults is not true), - * and optionally if there were no valid CBOR bytes in the input. Emits the - * {Decoder.NOT_FOUND} Symbol in the callback if no data was found and the - * `required` option is false. - * - * @static - * @param {BufferLike} input What to parse? - * @param {DecoderOptions|decodeCallback|string} [options={}] Options, the - * callback, or input encoding. - * @param {decodeCallback} [cb] Callback. - * @returns {Promise} Returned even if callback is - * specified. - * @throws {TypeError} No input provided. - */ - static decodeFirst(input, options = {}, cb = null) { - if (input == null) { - throw new TypeError('input required') - } - ({options, cb} = normalizeOptions(options, cb)) - const {encoding = 'hex', required = false, ...opts} = options - - const c = new Decoder(opts) - let v = /** @type {any} */ (NOT_FOUND) - const s = utils.guessEncoding(input, encoding) - const p = new Promise((resolve, reject) => { - c.on('data', val => { - v = Decoder.nullcheck(val) - c.close() - }) - c.once('error', er => { - if (c.extendedResults && (er instanceof UnexpectedDataError)) { - v.unused = c.bs.slice() - return resolve(v) - } - if (v !== NOT_FOUND) { - // Typescript work-around - // eslint-disable-next-line dot-notation - er['value'] = v - } - v = ERROR - c.close() - return reject(er) - }) - c.once('end', () => { - switch (v) { - case NOT_FOUND: - if (required) { - return reject(new Error('No CBOR found')) - } - return resolve(v) - // Pretty sure this can't happen, but not *certain*. - /* istanbul ignore next */ - case ERROR: - /* istanbul ignore next */ - return undefined - default: - return resolve(v) - } - }) - }) - - if (typeof cb === 'function') { - p.then(val => cb(null, val), cb) - } - s.pipe(c) - return p - } - - /** - * @callback decodeAllCallback - * @param {Error} error If one was generated. - * @param {Array|Array} value All of the decoded - * values, wrapped in an Array. - */ - - /** - * Decode all of the CBOR items in the input. This will error if there are - * more bytes left over at the end. - * - * @static - * @param {BufferLike} input What to parse? - * @param {DecoderOptions|decodeAllCallback|string} [options={}] - * Decoding options, the callback, or the input encoding. - * @param {decodeAllCallback} [cb] Callback. - * @returns {Promise|Array>} Even if callback - * is specified. - * @throws {TypeError} No input specified. - */ - static decodeAll(input, options = {}, cb = null) { - if (input == null) { - throw new TypeError('input required') - } - ({options, cb} = normalizeOptions(options, cb)) - const {encoding = 'hex', ...opts} = options - - const c = new Decoder(opts) - const vals = [] - - c.on('data', val => vals.push(Decoder.nullcheck(val))) - - const p = new Promise((resolve, reject) => { - c.on('error', reject) - c.on('end', () => resolve(vals)) - }) - - if (typeof cb === 'function') { - p.then(v => cb(undefined, v), er => cb(er, undefined)) - } - utils.guessEncoding(input, encoding).pipe(c) - return p - } - - /** - * Stop processing. - */ - close() { - this.running = false - this.__fresh = true - } - - /** - * Only called if extendedResults is true. - * - * @ignore - */ - _onRead(data) { - this.valueBytes.write(data) - } - - /** - * @yields {number} Number of bytes to read. - * @returns {Generator} Yields a number of bytes, - * returns anything, next returns a Buffer. - * @throws {Error} Maximum depth exceeded. - * @ignore - */ - *_parse() { - let parent = null - let depth = 0 - let val = null - - while (true) { - if ((this.max_depth >= 0) && (depth > this.max_depth)) { - throw new Error(`Maximum depth ${this.max_depth} exceeded`) - } - - const [octet] = yield 1 - if (!this.running) { - this.bs.unshift(Buffer.from([octet])) - throw new UnexpectedDataError(octet) - } - const mt = octet >> 5 - const ai = octet & 0x1f - const parent_major = (parent == null) ? undefined : parent[MAJOR] - const parent_length = (parent == null) ? undefined : parent.length - - switch (ai) { - case NUMBYTES.ONE: - this.emit('more-bytes', mt, 1, parent_major, parent_length) - ;[val] = yield 1 - break - case NUMBYTES.TWO: - case NUMBYTES.FOUR: - case NUMBYTES.EIGHT: { - const numbytes = 1 << (ai - 24) - - this.emit('more-bytes', mt, numbytes, parent_major, parent_length) - const buf = yield numbytes - val = (mt === MT.SIMPLE_FLOAT) ? - buf : - utils.parseCBORint(ai, buf) - break - } - case 28: - case 29: - case 30: - this.running = false - throw new Error(`Additional info not implemented: ${ai}`) - case NUMBYTES.INDEFINITE: - switch (mt) { - case MT.POS_INT: - case MT.NEG_INT: - case MT.TAG: - throw new Error(`Invalid indefinite encoding for MT ${mt}`) - } - val = -1 - break - default: - val = ai - } - switch (mt) { - case MT.POS_INT: - // Val already decoded - break - case MT.NEG_INT: - if (val === Number.MAX_SAFE_INTEGER) { - val = BI.NEG_MAX - } else { - val = (typeof val === 'bigint') ? BI.MINUS_ONE - val : -1 - val - } - break - case MT.BYTE_STRING: - case MT.UTF8_STRING: - switch (val) { - case 0: - this.emit('start-string', mt, val, parent_major, parent_length) - if (mt === MT.UTF8_STRING) { - val = '' - } else { - val = this.preferWeb ? new Uint8Array(0) : Buffer.allocUnsafe(0) - } - break - case -1: - this.emit('start', mt, SYMS.STREAM, parent_major, parent_length) - parent = parentBufferStream(parent, mt) - depth++ - continue - default: - this.emit('start-string', mt, val, parent_major, parent_length) - val = yield val - if (mt === MT.UTF8_STRING) { - val = utils.utf8(val) - } else if (this.preferWeb) { - val = new Uint8Array(val.buffer, val.byteOffset, val.length) - } - } - break - case MT.ARRAY: - case MT.MAP: - switch (val) { - case 0: - val = (mt === MT.MAP) ? {} : [] - break - case -1: - this.emit('start', mt, SYMS.STREAM, parent_major, parent_length) - parent = parentArray(parent, mt, -1) - depth++ - continue - default: - this.emit('start', mt, val, parent_major, parent_length) - parent = parentArray(parent, mt, val * (mt - 3)) - depth++ - continue - } - break - case MT.TAG: - this.emit('start', mt, val, parent_major, parent_length) - parent = parentArray(parent, mt, 1) - parent.push(val) - depth++ - continue - case MT.SIMPLE_FLOAT: - if (typeof val === 'number') { - if ((ai === NUMBYTES.ONE) && (val < 32)) { - throw new Error( - `Invalid two-byte encoding of simple value ${val}` - ) - } - const hasParent = (parent != null) - val = Simple.decode( - val, - hasParent, - hasParent && (parent[COUNT] < 0) - ) - } else { - val = utils.parseCBORfloat(val) - } - } - this.emit('value', val, parent_major, parent_length, ai) - let again = false - while (parent != null) { - if (val === SYMS.BREAK) { - parent[COUNT] = 1 - } else if (Array.isArray(parent)) { - parent.push(val) - } else { - // Assert: parent instanceof NoFilter - const pm = parent[MAJOR] - - if ((pm != null) && (pm !== mt)) { - this.running = false - throw new Error('Invalid major type in indefinite encoding') - } - parent.write(val) - } - - if ((--parent[COUNT]) !== 0) { - again = true - break - } - --depth - delete parent[COUNT] - - if (Array.isArray(parent)) { - switch (parent[MAJOR]) { - case MT.ARRAY: - val = parent - break - case MT.MAP: { - let allstrings = true - - if ((parent.length % 2) !== 0) { - throw new Error(`Invalid map length: ${parent.length}`) - } - for (let i = 0, len = parent.length; i < len; i += 2) { - if ((typeof parent[i] !== 'string') || - (parent[i] === '__proto__')) { - allstrings = false - break - } - } - if (allstrings) { - val = {} - for (let i = 0, len = parent.length; i < len; i += 2) { - if (this.preventDuplicateKeys && - Object.prototype.hasOwnProperty.call(val, parent[i])) { - throw new Error('Duplicate keys in a map') - } - val[parent[i]] = parent[i + 1] - } - } else { - val = new Map() - for (let i = 0, len = parent.length; i < len; i += 2) { - if (this.preventDuplicateKeys && val.has(parent[i])) { - throw new Error('Duplicate keys in a map') - } - val.set(parent[i], parent[i + 1]) - } - } - break - } - case MT.TAG: { - const t = new Tagged(parent[0], parent[1]) - - val = t.convert(this.tags) - break - } - } - } else /* istanbul ignore else */ if (parent instanceof NoFilter) { - // Only parent types are Array and NoFilter for (Array/Map) and - // (bytes/string) respectively. - switch (parent[MAJOR]) { - case MT.BYTE_STRING: - val = parent.slice() - if (this.preferWeb) { - val = new Uint8Array( - /** @type {Buffer} */ (val).buffer, - /** @type {Buffer} */ (val).byteOffset, - /** @type {Buffer} */ (val).length - ) - } - break - case MT.UTF8_STRING: - val = parent.toString('utf-8') - break - } - } - this.emit('stop', parent[MAJOR]) - - const old = parent - parent = parent[SYMS.PARENT] - delete old[SYMS.PARENT] - delete old[MAJOR] - } - if (!again) { - if (this.extendedResults) { - const bytes = this.valueBytes.slice() - const ret = { - value: Decoder.nullcheck(val), - bytes, - length: bytes.length, - } - - this.valueBytes = new NoFilter() - return ret - } - return val - } - } - } -} - -Decoder.NOT_FOUND = NOT_FOUND -module.exports = Decoder - -},{"../vendor/binary-parse-stream":76,"./constants":68,"./simple":73,"./tagged":74,"./utils":75,"buffer":63,"nofilter":181,"stream":232}],70:[function(require,module,exports){ -'use strict' - -const stream = require('stream') -const Decoder = require('./decoder') -const utils = require('./utils') -const NoFilter = require('nofilter') -const {MT, SYMS} = require('./constants') - -/** - * Things that can act as inputs, from which a NoFilter can be created. - * - * @typedef {string|Buffer|ArrayBuffer|Uint8Array|Uint8ClampedArray - * |DataView|stream.Readable} BufferLike - */ - -/** - * @typedef DiagnoseOptions - * @property {string} [separator='\n'] Output between detected objects. - * @property {boolean} [stream_errors=false] Put error info into the - * output stream. - * @property {number} [max_depth=-1] The maximum depth to parse. - * Use -1 for "until you run out of memory". Set this to a finite - * positive number for un-trusted inputs. Most standard inputs won't nest - * more than 100 or so levels; I've tested into the millions before - * running out of memory. - * @property {object} [tags] Mapping from tag number to function(v), - * where v is the decoded value that comes after the tag, and where the - * function returns the correctly-created value for that tag. - * @property {boolean} [preferWeb=false] If true, prefer Uint8Arrays to - * be generated instead of node Buffers. This might turn on some more - * changes in the future, so forward-compatibility is not guaranteed yet. - * @property {BufferEncoding} [encoding='hex'] The encoding of input, ignored if - * input is not string. - */ -/** - * @callback diagnoseCallback - * @param {Error} [error] If one was generated. - * @param {string} [value] The diagnostic value. - * @returns {void} - */ -/** - * @param {DiagnoseOptions|diagnoseCallback|string} opts Options, - * the callback, or input incoding. - * @param {diagnoseCallback} [cb] Called on completion. - * @returns {{options: DiagnoseOptions, cb: diagnoseCallback}} Normalized. - * @throws {TypeError} Unknown option type. - * @private - */ -function normalizeOptions(opts, cb) { - switch (typeof opts) { - case 'function': - return {options: {}, cb: /** @type {diagnoseCallback} */ (opts)} - case 'string': - return {options: {encoding: /** @type {BufferEncoding} */ (opts)}, cb} - case 'object': - return {options: opts || {}, cb} - default: - throw new TypeError('Unknown option type') - } -} - -/** - * Output the diagnostic format from a stream of CBOR bytes. - * - * @extends stream.Transform - */ -class Diagnose extends stream.Transform { - /** - * Creates an instance of Diagnose. - * - * @param {DiagnoseOptions} [options={}] Options for creation. - */ - constructor(options = {}) { - const { - separator = '\n', - stream_errors = false, - // Decoder options - tags, - max_depth, - preferWeb, - encoding, - // Stream.Transform options - ...superOpts - } = options - super({ - ...superOpts, - readableObjectMode: false, - writableObjectMode: false, - }) - - this.float_bytes = -1 - this.separator = separator - this.stream_errors = stream_errors - this.parser = new Decoder({ - tags, - max_depth, - preferWeb, - encoding, - }) - this.parser.on('more-bytes', this._on_more.bind(this)) - this.parser.on('value', this._on_value.bind(this)) - this.parser.on('start', this._on_start.bind(this)) - this.parser.on('stop', this._on_stop.bind(this)) - this.parser.on('data', this._on_data.bind(this)) - this.parser.on('error', this._on_error.bind(this)) - } - - _transform(fresh, encoding, cb) { - return this.parser.write(fresh, encoding, cb) - } - - _flush(cb) { - return this.parser._flush(er => { - if (this.stream_errors) { - if (er) { - this._on_error(er) - } - return cb() - } - return cb(er) - }) - } - - /** - * Convenience function to return a string in diagnostic format. - * - * @param {BufferLike} input The CBOR bytes to format. - * @param {DiagnoseOptions |diagnoseCallback|string} [options={}] - * Options, the callback, or the input encoding. - * @param {diagnoseCallback} [cb] Callback. - * @throws {TypeError} Input not provided. - * @returns {Promise} If callback not specified. - */ - static diagnose(input, options = {}, cb = null) { - if (input == null) { - throw new TypeError('input required') - } - ({options, cb} = normalizeOptions(options, cb)) - const {encoding = 'hex', ...opts} = options - - const bs = new NoFilter() - const d = new Diagnose(opts) - let p = null - if (typeof cb === 'function') { - d.on('end', () => cb(null, bs.toString('utf8'))) - d.on('error', cb) - } else { - p = new Promise((resolve, reject) => { - d.on('end', () => resolve(bs.toString('utf8'))) - d.on('error', reject) - }) - } - d.pipe(bs) - utils.guessEncoding(input, encoding).pipe(d) - return p - } - - /** - * @ignore - */ - _on_error(er) { - if (this.stream_errors) { - this.push(er.toString()) - } else { - this.emit('error', er) - } - } - - /** @private */ - _on_more(mt, len, parent_mt, pos) { - if (mt === MT.SIMPLE_FLOAT) { - this.float_bytes = { - 2: 1, - 4: 2, - 8: 3, - }[len] - } - } - - /** @private */ - _fore(parent_mt, pos) { - switch (parent_mt) { - case MT.BYTE_STRING: - case MT.UTF8_STRING: - case MT.ARRAY: - if (pos > 0) { - this.push(', ') - } - break - case MT.MAP: - if (pos > 0) { - if (pos % 2) { - this.push(': ') - } else { - this.push(', ') - } - } - } - } - - /** @private */ - _on_value(val, parent_mt, pos) { - if (val === SYMS.BREAK) { - return - } - this._fore(parent_mt, pos) - const fb = this.float_bytes - this.float_bytes = -1 - this.push(utils.cborValueToString(val, fb)) - } - - /** @private */ - _on_start(mt, tag, parent_mt, pos) { - this._fore(parent_mt, pos) - switch (mt) { - case MT.TAG: - this.push(`${tag}(`) - break - case MT.ARRAY: - this.push('[') - break - case MT.MAP: - this.push('{') - break - case MT.BYTE_STRING: - case MT.UTF8_STRING: - this.push('(') - break - } - if (tag === SYMS.STREAM) { - this.push('_ ') - } - } - - /** @private */ - _on_stop(mt) { - switch (mt) { - case MT.TAG: - this.push(')') - break - case MT.ARRAY: - this.push(']') - break - case MT.MAP: - this.push('}') - break - case MT.BYTE_STRING: - case MT.UTF8_STRING: - this.push(')') - break - } - } - - /** @private */ - _on_data() { - this.push(this.separator) - } -} - -module.exports = Diagnose - -},{"./constants":68,"./decoder":69,"./utils":75,"nofilter":181,"stream":232}],71:[function(require,module,exports){ -'use strict' - -const stream = require('stream') -const NoFilter = require('nofilter') -const utils = require('./utils') -const constants = require('./constants') -const { - MT, NUMBYTES, SHIFT32, SIMPLE, SYMS, TAG, BI, -} = constants -const {Buffer} = require('buffer') - -const HALF = (MT.SIMPLE_FLOAT << 5) | NUMBYTES.TWO -const FLOAT = (MT.SIMPLE_FLOAT << 5) | NUMBYTES.FOUR -const DOUBLE = (MT.SIMPLE_FLOAT << 5) | NUMBYTES.EIGHT -const TRUE = (MT.SIMPLE_FLOAT << 5) | SIMPLE.TRUE -const FALSE = (MT.SIMPLE_FLOAT << 5) | SIMPLE.FALSE -const UNDEFINED = (MT.SIMPLE_FLOAT << 5) | SIMPLE.UNDEFINED -const NULL = (MT.SIMPLE_FLOAT << 5) | SIMPLE.NULL - -const BREAK = Buffer.from([0xff]) -const BUF_NAN = Buffer.from('f97e00', 'hex') -const BUF_INF_NEG = Buffer.from('f9fc00', 'hex') -const BUF_INF_POS = Buffer.from('f97c00', 'hex') -const BUF_NEG_ZERO = Buffer.from('f98000', 'hex') - -/** - * Generate the CBOR for a value. If you are using this, you'll either need - * to call {@link Encoder.write} with a Buffer, or look into the internals of - * Encoder to reuse existing non-documented behavior. - * - * @callback EncodeFunction - * @param {Encoder} enc The encoder to use. - * @param {any} val The value to encode. - * @returns {boolean} True on success. - */ - -/* eslint-disable jsdoc/check-types */ -/** - * A mapping from tag number to a tag decoding function. - * - * @typedef {Object.} SemanticMap - */ -/* eslint-enable jsdoc/check-types */ - -/** - * @type {SemanticMap} - * @private - */ -const SEMANTIC_TYPES = {} - -/** - * @type {SemanticMap} - * @private - */ -let current_SEMANTIC_TYPES = {} - -/** - * @param {string} str String to normalize. - * @returns {"number"|"float"|"int"|"string"} Normalized. - * @throws {TypeError} Invalid input. - * @private - */ -function parseDateType(str) { - if (!str) { - return 'number' - } - switch (str.toLowerCase()) { - case 'number': - return 'number' - case 'float': - return 'float' - case 'int': - case 'integer': - return 'int' - case 'string': - return 'string' - } - throw new TypeError(`dateType invalid, got "${str}"`) -} - -/** - * @typedef EncodingOptions - * @property {any[]|object} [genTypes=[]] Array of pairs of - * `type`, `function(Encoder)` for semantic types to be encoded. Not - * needed for Array, Date, Buffer, Map, RegExp, Set, or URL. - * If an object, the keys are the constructor names for the types. - * @property {boolean} [canonical=false] Should the output be - * canonicalized. - * @property {boolean|WeakSet} [detectLoops=false] Should object loops - * be detected? This will currently add memory to track every part of the - * object being encoded in a WeakSet. Do not encode - * the same object twice on the same encoder, without calling - * `removeLoopDetectors` in between, which will clear the WeakSet. - * You may pass in your own WeakSet to be used; this is useful in some - * recursive scenarios. - * @property {("number"|"float"|"int"|"string")} [dateType="number"] - - * how should dates be encoded? "number" means float or int, if no - * fractional seconds. - * @property {any} [encodeUndefined=undefined] How should an - * "undefined" in the input be encoded. By default, just encode a CBOR - * undefined. If this is a buffer, use those bytes without re-encoding - * them. If this is a function, the function will be called (which is a - * good time to throw an exception, if that's what you want), and the - * return value will be used according to these rules. Anything else will - * be encoded as CBOR. - * @property {boolean} [disallowUndefinedKeys=false] Should - * "undefined" be disallowed as a key in a Map that is serialized? If - * this is true, encode(new Map([[undefined, 1]])) will throw an - * exception. Note that it is impossible to get a key of undefined in a - * normal JS object. - * @property {boolean} [collapseBigIntegers=false] Should integers - * that come in as ECMAscript bigint's be encoded - * as normal CBOR integers if they fit, discarding type information? - * @property {number} [chunkSize=4096] Number of characters or bytes - * for each chunk, if obj is a string or Buffer, when indefinite encoding. - * @property {boolean} [omitUndefinedProperties=false] When encoding - * objects or Maps, do not include a key if its corresponding value is - * `undefined`. - */ - -/** - * Transform JavaScript values into CBOR bytes. The `Writable` side of - * the stream is in object mode. - * - * @extends stream.Transform - */ -class Encoder extends stream.Transform { - /** - * Creates an instance of Encoder. - * - * @param {EncodingOptions} [options={}] Options for the encoder. - */ - constructor(options = {}) { - const { - canonical = false, - encodeUndefined, - disallowUndefinedKeys = false, - dateType = 'number', - collapseBigIntegers = false, - detectLoops = false, - omitUndefinedProperties = false, - genTypes = [], - ...superOpts - } = options - - super({ - ...superOpts, - readableObjectMode: false, - writableObjectMode: true, - }) - - this.canonical = canonical - this.encodeUndefined = encodeUndefined - this.disallowUndefinedKeys = disallowUndefinedKeys - this.dateType = parseDateType(dateType) - this.collapseBigIntegers = this.canonical ? true : collapseBigIntegers - - /** @type {WeakSet?} */ - this.detectLoops = undefined - if (typeof detectLoops === 'boolean') { - if (detectLoops) { - this.detectLoops = new WeakSet() - } - } else if (detectLoops instanceof WeakSet) { - this.detectLoops = detectLoops - } else { - throw new TypeError('detectLoops must be boolean or WeakSet') - } - this.omitUndefinedProperties = omitUndefinedProperties - - this.semanticTypes = {...Encoder.SEMANTIC_TYPES} - - if (Array.isArray(genTypes)) { - for (let i = 0, len = genTypes.length; i < len; i += 2) { - this.addSemanticType(genTypes[i], genTypes[i + 1]) - } - } else { - for (const [k, v] of Object.entries(genTypes)) { - this.addSemanticType(k, v) - } - } - } - - _transform(fresh, encoding, cb) { - const ret = this.pushAny(fresh) - // Old transformers might not return bool. undefined !== false - return cb((ret === false) ? new Error('Push Error') : undefined) - } - - // eslint-disable-next-line class-methods-use-this - _flush(cb) { - return cb() - } - - /** - * @param {number} val Number(0-255) to encode. - * @returns {boolean} True on success. - * @ignore - */ - _pushUInt8(val) { - const b = Buffer.allocUnsafe(1) - b.writeUInt8(val, 0) - return this.push(b) - } - - /** - * @param {number} val Number(0-65535) to encode. - * @returns {boolean} True on success. - * @ignore - */ - _pushUInt16BE(val) { - const b = Buffer.allocUnsafe(2) - b.writeUInt16BE(val, 0) - return this.push(b) - } - - /** - * @param {number} val Number(0..2**32-1) to encode. - * @returns {boolean} True on success. - * @ignore - */ - _pushUInt32BE(val) { - const b = Buffer.allocUnsafe(4) - b.writeUInt32BE(val, 0) - return this.push(b) - } - - /** - * @param {number} val Number to encode as 4-byte float. - * @returns {boolean} True on success. - * @ignore - */ - _pushFloatBE(val) { - const b = Buffer.allocUnsafe(4) - b.writeFloatBE(val, 0) - return this.push(b) - } - - /** - * @param {number} val Number to encode as 8-byte double. - * @returns {boolean} True on success. - * @ignore - */ - _pushDoubleBE(val) { - const b = Buffer.allocUnsafe(8) - b.writeDoubleBE(val, 0) - return this.push(b) - } - - /** - * @returns {boolean} True on success. - * @ignore - */ - _pushNaN() { - return this.push(BUF_NAN) - } - - /** - * @param {number} obj Positive or negative infinity. - * @returns {boolean} True on success. - * @ignore - */ - _pushInfinity(obj) { - const half = (obj < 0) ? BUF_INF_NEG : BUF_INF_POS - return this.push(half) - } - - /** - * Choose the best float representation for a number and encode it. - * - * @param {number} obj A number that is known to be not-integer, but not - * how many bytes of precision it needs. - * @returns {boolean} True on success. - * @ignore - */ - _pushFloat(obj) { - if (this.canonical) { - // TODO: is this enough slower to hide behind canonical? - // It's certainly enough of a hack (see utils.parseHalf) - - // From section 3.9: - // If a protocol allows for IEEE floats, then additional canonicalization - // rules might need to be added. One example rule might be to have all - // floats start as a 64-bit float, then do a test conversion to a 32-bit - // float; if the result is the same numeric value, use the shorter value - // and repeat the process with a test conversion to a 16-bit float. (This - // rule selects 16-bit float for positive and negative Infinity as well.) - - // which seems pretty much backwards to me. - const b2 = Buffer.allocUnsafe(2) - if (utils.writeHalf(b2, obj)) { - // I have convinced myself that there are no cases where writeHalf - // will return true but `utils.parseHalf(b2) !== obj)` - return this._pushUInt8(HALF) && this.push(b2) - } - } - if (Math.fround(obj) === obj) { - return this._pushUInt8(FLOAT) && this._pushFloatBE(obj) - } - - return this._pushUInt8(DOUBLE) && this._pushDoubleBE(obj) - } - - /** - * Choose the best integer representation for a postive number and encode - * it. If the number is over MAX_SAFE_INTEGER, fall back on float (but I - * don't remember why). - * - * @param {number} obj A positive number that is known to be an integer, - * but not how many bytes of precision it needs. - * @param {number} mt The Major Type number to combine with the integer. - * Not yet shifted. - * @param {number} [orig] The number before it was transformed to positive. - * If the mt is NEG_INT, and the positive number is over MAX_SAFE_INT, - * then we'll encode this as a float rather than making the number - * negative again and losing precision. - * @returns {boolean} True on success. - * @ignore - */ - _pushInt(obj, mt, orig) { - const m = mt << 5 - - if (obj < 24) { - return this._pushUInt8(m | obj) - } - if (obj <= 0xff) { - return this._pushUInt8(m | NUMBYTES.ONE) && this._pushUInt8(obj) - } - if (obj <= 0xffff) { - return this._pushUInt8(m | NUMBYTES.TWO) && this._pushUInt16BE(obj) - } - if (obj <= 0xffffffff) { - return this._pushUInt8(m | NUMBYTES.FOUR) && this._pushUInt32BE(obj) - } - let max = Number.MAX_SAFE_INTEGER - if (mt === MT.NEG_INT) { - // Special case for Number.MIN_SAFE_INTEGER - 1 - max-- - } - if (obj <= max) { - return this._pushUInt8(m | NUMBYTES.EIGHT) && - this._pushUInt32BE(Math.floor(obj / SHIFT32)) && - this._pushUInt32BE(obj % SHIFT32) - } - if (mt === MT.NEG_INT) { - return this._pushFloat(orig) - } - return this._pushFloat(obj) - } - - /** - * Choose the best integer representation for a number and encode it. - * - * @param {number} obj A number that is known to be an integer, - * but not how many bytes of precision it needs. - * @returns {boolean} True on success. - * @ignore - */ - _pushIntNum(obj) { - if (Object.is(obj, -0)) { - return this.push(BUF_NEG_ZERO) - } - - if (obj < 0) { - return this._pushInt(-obj - 1, MT.NEG_INT, obj) - } - return this._pushInt(obj, MT.POS_INT) - } - - /** - * @param {number} obj Plain JS number to encode. - * @returns {boolean} True on success. - * @ignore - */ - _pushNumber(obj) { - if (isNaN(obj)) { - return this._pushNaN() - } - if (!isFinite(obj)) { - return this._pushInfinity(obj) - } - if (Math.round(obj) === obj) { - return this._pushIntNum(obj) - } - return this._pushFloat(obj) - } - - /** - * @param {string} obj String to encode. - * @returns {boolean} True on success. - * @ignore - */ - _pushString(obj) { - const len = Buffer.byteLength(obj, 'utf8') - return this._pushInt(len, MT.UTF8_STRING) && this.push(obj, 'utf8') - } - - /** - * @param {boolean} obj Bool to encode. - * @returns {boolean} True on success. - * @ignore - */ - _pushBoolean(obj) { - return this._pushUInt8(obj ? TRUE : FALSE) - } - - /** - * @param {undefined} obj Ignored. - * @returns {boolean} True on success. - * @ignore - */ - _pushUndefined(obj) { - switch (typeof this.encodeUndefined) { - case 'undefined': - return this._pushUInt8(UNDEFINED) - case 'function': - return this.pushAny(this.encodeUndefined(obj)) - case 'object': { - const buf = utils.bufferishToBuffer(this.encodeUndefined) - if (buf) { - return this.push(buf) - } - } - } - return this.pushAny(this.encodeUndefined) - } - - /** - * @param {null} obj Ignored. - * @returns {boolean} True on success. - * @ignore - */ - _pushNull(obj) { - return this._pushUInt8(NULL) - } - - /** - * @param {number} tag Tag number to encode. - * @returns {boolean} True on success. - * @ignore - */ - _pushTag(tag) { - return this._pushInt(tag, MT.TAG) - } - - /** - * @param {bigint} obj BigInt to encode. - * @returns {boolean} True on success. - * @ignore - */ - _pushJSBigint(obj) { - let m = MT.POS_INT - let tag = TAG.POS_BIGINT - // BigInt doesn't have -0 - if (obj < 0) { - obj = -obj + BI.MINUS_ONE - m = MT.NEG_INT - tag = TAG.NEG_BIGINT - } - - if (this.collapseBigIntegers && - (obj <= BI.MAXINT64)) { - // Special handiling for 64bits - if (obj <= 0xffffffff) { - return this._pushInt(Number(obj), m) - } - return this._pushUInt8((m << 5) | NUMBYTES.EIGHT) && - this._pushUInt32BE(Number(obj / BI.SHIFT32)) && - this._pushUInt32BE(Number(obj % BI.SHIFT32)) - } - - let str = obj.toString(16) - if (str.length % 2) { - str = `0${str}` - } - const buf = Buffer.from(str, 'hex') - return this._pushTag(tag) && Encoder._pushBuffer(this, buf) - } - - /** - * @param {object} obj Object to encode. - * @returns {boolean} True on success. - * @throws {Error} Loop detected. - * @ignore - */ - _pushObject(obj, opts) { - if (!obj) { - return this._pushNull(obj) - } - opts = { - indefinite: false, - skipTypes: false, - ...opts, - } - if (!opts.indefinite) { - // This will only happen the first time through for indefinite encoding - if (this.detectLoops) { - if (this.detectLoops.has(obj)) { - throw new Error(`\ -Loop detected while CBOR encoding. -Call removeLoopDetectors before resuming.`) - } else { - this.detectLoops.add(obj) - } - } - } - if (!opts.skipTypes) { - const f = obj.encodeCBOR - if (typeof f === 'function') { - return f.call(obj, this) - } - const converter = this.semanticTypes[obj.constructor.name] - if (converter) { - return converter.call(obj, this, obj) - } - } - const keys = Object.keys(obj).filter(k => { - const tv = typeof obj[k] - return (tv !== 'function') && - (!this.omitUndefinedProperties || (tv !== 'undefined')) - }) - const cbor_keys = {} - if (this.canonical) { - // Note: this can't be a normal sort, because 'b' needs to sort before - // 'aa' - keys.sort((a, b) => { - // Always strings, so don't bother to pass options. - // hold on to the cbor versions, since there's no need - // to encode more than once - const a_cbor = cbor_keys[a] || (cbor_keys[a] = Encoder.encode(a)) - const b_cbor = cbor_keys[b] || (cbor_keys[b] = Encoder.encode(b)) - - return a_cbor.compare(b_cbor) - }) - } - if (opts.indefinite) { - if (!this._pushUInt8((MT.MAP << 5) | NUMBYTES.INDEFINITE)) { - return false - } - } else if (!this._pushInt(keys.length, MT.MAP)) { - return false - } - let ck = null - for (let j = 0, len2 = keys.length; j < len2; j++) { - const k = keys[j] - if (this.canonical && ((ck = cbor_keys[k]))) { - if (!this.push(ck)) { // Already a Buffer - return false - } - } else if (!this._pushString(k)) { - return false - } - if (!this.pushAny(obj[k])) { - return false - } - } - if (opts.indefinite) { - if (!this.push(BREAK)) { - return false - } - } else if (this.detectLoops) { - this.detectLoops.delete(obj) - } - return true - } - - /** - * @param {any[]} objs Array of supported things. - * @returns {Buffer} Concatenation of encodings for the supported things. - * @ignore - */ - _encodeAll(objs) { - const bs = new NoFilter({highWaterMark: this.readableHighWaterMark}) - this.pipe(bs) - for (const o of objs) { - this.pushAny(o) - } - this.end() - return bs.read() - } - - /** - * Add an encoding function to the list of supported semantic types. This - * is useful for objects for which you can't add an encodeCBOR method. - * - * @param {string|Function} type The type to encode. - * @param {EncodeFunction} fun The encoder to use. - * @returns {EncodeFunction?} The previous encoder or undefined if there - * wasn't one. - * @throws {TypeError} Invalid function. - */ - addSemanticType(type, fun) { - const typeName = (typeof type === 'string') ? type : type.name - const old = this.semanticTypes[typeName] - - if (fun) { - if (typeof fun !== 'function') { - throw new TypeError('fun must be of type function') - } - this.semanticTypes[typeName] = fun - } else if (old) { - delete this.semanticTypes[typeName] - } - return old - } - - /** - * Push any supported type onto the encoded stream. - * - * @param {any} obj The thing to encode. - * @returns {boolean} True on success. - * @throws {TypeError} Unknown type for obj. - */ - pushAny(obj) { - switch (typeof obj) { - case 'number': - return this._pushNumber(obj) - case 'bigint': - return this._pushJSBigint(obj) - case 'string': - return this._pushString(obj) - case 'boolean': - return this._pushBoolean(obj) - case 'undefined': - return this._pushUndefined(obj) - case 'object': - return this._pushObject(obj) - case 'symbol': - switch (obj) { - case SYMS.NULL: - return this._pushNull(null) - case SYMS.UNDEFINED: - return this._pushUndefined(undefined) - // TODO: Add pluggable support for other symbols - default: - throw new TypeError(`Unknown symbol: ${obj.toString()}`) - } - default: - throw new TypeError( - `Unknown type: ${typeof obj}, ${(typeof obj.toString === 'function') ? obj.toString() : ''}` - ) - } - } - - /** - * Encode an array and all of its elements. - * - * @param {Encoder} gen Encoder to use. - * @param {any[]} obj Array to encode. - * @param {object} [opts] Options. - * @param {boolean} [opts.indefinite=false] Use indefinite encoding? - * @returns {boolean} True on success. - */ - static pushArray(gen, obj, opts) { - opts = { - indefinite: false, - ...opts, - } - const len = obj.length - if (opts.indefinite) { - if (!gen._pushUInt8((MT.ARRAY << 5) | NUMBYTES.INDEFINITE)) { - return false - } - } else if (!gen._pushInt(len, MT.ARRAY)) { - return false - } - for (let j = 0; j < len; j++) { - if (!gen.pushAny(obj[j])) { - return false - } - } - if (opts.indefinite) { - if (!gen.push(BREAK)) { - return false - } - } - return true - } - - /** - * Remove the loop detector WeakSet for this Encoder. - * - * @returns {boolean} True when the Encoder was reset, else false. - */ - removeLoopDetectors() { - if (!this.detectLoops) { - return false - } - this.detectLoops = new WeakSet() - return true - } - - /** - * @param {Encoder} gen Encoder. - * @param {Date} obj Date to encode. - * @returns {boolean} True on success. - * @ignore - */ - static _pushDate(gen, obj) { - switch (gen.dateType) { - case 'string': - return gen._pushTag(TAG.DATE_STRING) && - gen._pushString(obj.toISOString()) - case 'int': - return gen._pushTag(TAG.DATE_EPOCH) && - gen._pushIntNum(Math.round(obj.getTime() / 1000)) - case 'float': - // Force float - return gen._pushTag(TAG.DATE_EPOCH) && - gen._pushFloat(obj.getTime() / 1000) - case 'number': - default: - // If we happen to have an integral number of seconds, - // use integer. Otherwise, use float. - return gen._pushTag(TAG.DATE_EPOCH) && - gen.pushAny(obj.getTime() / 1000) - } - } - - /** - * @param {Encoder} gen Encoder. - * @param {Buffer} obj Buffer to encode. - * @returns {boolean} True on success. - * @ignore - */ - static _pushBuffer(gen, obj) { - return gen._pushInt(obj.length, MT.BYTE_STRING) && gen.push(obj) - } - - /** - * @param {Encoder} gen Encoder. - * @param {NoFilter} obj Buffer to encode. - * @returns {boolean} True on success. - * @ignore - */ - static _pushNoFilter(gen, obj) { - return Encoder._pushBuffer(gen, /** @type {Buffer} */ (obj.slice())) - } - - /** - * @param {Encoder} gen Encoder. - * @param {RegExp} obj RegExp to encode. - * @returns {boolean} True on success. - * @ignore - */ - static _pushRegexp(gen, obj) { - return gen._pushTag(TAG.REGEXP) && gen.pushAny(obj.source) - } - - /** - * @param {Encoder} gen Encoder. - * @param {Set} obj Set to encode. - * @returns {boolean} True on success. - * @ignore - */ - static _pushSet(gen, obj) { - if (!gen._pushTag(TAG.SET)) { - return false - } - if (!gen._pushInt(obj.size, MT.ARRAY)) { - return false - } - for (const x of obj) { - if (!gen.pushAny(x)) { - return false - } - } - return true - } - - /** - * @param {Encoder} gen Encoder. - * @param {URL} obj URL to encode. - * @returns {boolean} True on success. - * @ignore - */ - static _pushURL(gen, obj) { - return gen._pushTag(TAG.URI) && gen.pushAny(obj.toString()) - } - - /** - * @param {Encoder} gen Encoder. - * @param {object} obj Boxed String, Number, or Boolean object to encode. - * @returns {boolean} True on success. - * @ignore - */ - static _pushBoxed(gen, obj) { - return gen.pushAny(obj.valueOf()) - } - - /** - * @param {Encoder} gen Encoder. - * @param {Map} obj Map to encode. - * @returns {boolean} True on success. - * @throws {Error} Map key that is undefined. - * @ignore - */ - static _pushMap(gen, obj, opts) { - opts = { - indefinite: false, - ...opts, - } - let entries = [...obj.entries()] - if (gen.omitUndefinedProperties) { - entries = entries.filter(([k, v]) => v !== undefined) - } - if (opts.indefinite) { - if (!gen._pushUInt8((MT.MAP << 5) | NUMBYTES.INDEFINITE)) { - return false - } - } else if (!gen._pushInt(entries.length, MT.MAP)) { - return false - } - // Memoizing the cbor only helps in certain cases, and hurts in most - // others. Just avoid it. - if (gen.canonical) { - // Keep the key/value pairs together, so we don't have to do odd - // gets with object keys later - const enc = new Encoder({ - genTypes: gen.semanticTypes, - canonical: gen.canonical, - detectLoops: Boolean(gen.detectLoops), // Give enc its own loop detector - dateType: gen.dateType, - disallowUndefinedKeys: gen.disallowUndefinedKeys, - collapseBigIntegers: gen.collapseBigIntegers, - }) - const bs = new NoFilter({highWaterMark: gen.readableHighWaterMark}) - enc.pipe(bs) - entries.sort(([a], [b]) => { - // Both a and b are the keys - enc.pushAny(a) - const a_cbor = bs.read() - enc.pushAny(b) - const b_cbor = bs.read() - return a_cbor.compare(b_cbor) - }) - for (const [k, v] of entries) { - if (gen.disallowUndefinedKeys && (typeof k === 'undefined')) { - throw new Error('Invalid Map key: undefined') - } - if (!(gen.pushAny(k) && gen.pushAny(v))) { - return false - } - } - } else { - for (const [k, v] of entries) { - if (gen.disallowUndefinedKeys && (typeof k === 'undefined')) { - throw new Error('Invalid Map key: undefined') - } - if (!(gen.pushAny(k) && gen.pushAny(v))) { - return false - } - } - } - if (opts.indefinite) { - if (!gen.push(BREAK)) { - return false - } - } - return true - } - - /** - * @param {Encoder} gen Encoder. - * @param {NodeJS.TypedArray} obj Array to encode. - * @returns {boolean} True on success. - * @ignore - */ - static _pushTypedArray(gen, obj) { - // See https://tools.ietf.org/html/rfc8746 - - let typ = 0b01000000 - let sz = obj.BYTES_PER_ELEMENT - const {name} = obj.constructor - - if (name.startsWith('Float')) { - typ |= 0b00010000 - sz /= 2 - } else if (!name.includes('U')) { - typ |= 0b00001000 - } - if (name.includes('Clamped') || ((sz !== 1) && !utils.isBigEndian())) { - typ |= 0b00000100 - } - typ |= { - 1: 0b00, - 2: 0b01, - 4: 0b10, - 8: 0b11, - }[sz] - if (!gen._pushTag(typ)) { - return false - } - return Encoder._pushBuffer( - gen, - Buffer.from(obj.buffer, obj.byteOffset, obj.byteLength) - ) - } - - /** - * @param {Encoder} gen Encoder. - * @param { ArrayBuffer } obj Array to encode. - * @returns {boolean} True on success. - * @ignore - */ - static _pushArrayBuffer(gen, obj) { - return Encoder._pushBuffer(gen, Buffer.from(obj)) - } - - /** - * Encode the given object with indefinite length. There are apparently - * some (IMO) broken implementations of poorly-specified protocols that - * REQUIRE indefinite-encoding. See the example for how to add this as an - * `encodeCBOR` function to an object or class to get indefinite encoding. - * - * @param {Encoder} gen The encoder to use. - * @param {string|Buffer|Array|Map|object} [obj] The object to encode. If - * null, use "this" instead. - * @param {EncodingOptions} [options={}] Options for encoding. - * @returns {boolean} True on success. - * @throws {Error} No object to encode or invalid indefinite encoding. - * @example Force indefinite encoding: - * const o = { - * a: true, - * encodeCBOR: cbor.Encoder.encodeIndefinite, - * } - * const m = [] - * m.encodeCBOR = cbor.Encoder.encodeIndefinite - * cbor.encodeOne([o, m]) - */ - static encodeIndefinite(gen, obj, options = {}) { - if (obj == null) { - if (this == null) { - throw new Error('No object to encode') - } - obj = this - } - - // TODO: consider other options - const {chunkSize = 4096} = options - - let ret = true - const objType = typeof obj - let buf = null - if (objType === 'string') { - // TODO: make sure not to split surrogate pairs at the edges of chunks, - // since such half-surrogates cannot be legally encoded as UTF-8. - ret = ret && gen._pushUInt8((MT.UTF8_STRING << 5) | NUMBYTES.INDEFINITE) - let offset = 0 - while (offset < obj.length) { - const endIndex = offset + chunkSize - ret = ret && gen._pushString(obj.slice(offset, endIndex)) - offset = endIndex - } - ret = ret && gen.push(BREAK) - } else if ((buf = utils.bufferishToBuffer(obj))) { - ret = ret && gen._pushUInt8((MT.BYTE_STRING << 5) | NUMBYTES.INDEFINITE) - let offset = 0 - while (offset < buf.length) { - const endIndex = offset + chunkSize - ret = ret && Encoder._pushBuffer(gen, buf.slice(offset, endIndex)) - offset = endIndex - } - ret = ret && gen.push(BREAK) - } else if (Array.isArray(obj)) { - ret = ret && Encoder.pushArray(gen, obj, { - indefinite: true, - }) - } else if (obj instanceof Map) { - ret = ret && Encoder._pushMap(gen, obj, { - indefinite: true, - }) - } else { - if (objType !== 'object') { - throw new Error('Invalid indefinite encoding') - } - ret = ret && gen._pushObject(obj, { - indefinite: true, - skipTypes: true, - }) - } - return ret - } - - /** - * Encode one or more JavaScript objects, and return a Buffer containing the - * CBOR bytes. - * - * @param {...any} objs The objects to encode. - * @returns {Buffer} The encoded objects. - */ - static encode(...objs) { - return new Encoder()._encodeAll(objs) - } - - /** - * Encode one or more JavaScript objects canonically (slower!), and return - * a Buffer containing the CBOR bytes. - * - * @param {...any} objs The objects to encode. - * @returns {Buffer} The encoded objects. - */ - static encodeCanonical(...objs) { - return new Encoder({ - canonical: true, - })._encodeAll(objs) - } - - /** - * Encode one JavaScript object using the given options. - * - * @static - * @param {any} obj The object to encode. - * @param {EncodingOptions} [options={}] Passed to the Encoder constructor. - * @returns {Buffer} The encoded objects. - */ - static encodeOne(obj, options) { - return new Encoder(options)._encodeAll([obj]) - } - - /** - * Encode one JavaScript object using the given options in a way that - * is more resilient to objects being larger than the highWaterMark - * number of bytes. As with the other static encode functions, this - * will still use a large amount of memory. Use a stream-based approach - * directly if you need to process large and complicated inputs. - * - * @param {any} obj The object to encode. - * @param {EncodingOptions} [options={}] Passed to the Encoder constructor. - * @returns {Promise} A promise for the encoded buffer. - */ - static encodeAsync(obj, options) { - return new Promise((resolve, reject) => { - const bufs = [] - const enc = new Encoder(options) - enc.on('data', buf => bufs.push(buf)) - enc.on('error', reject) - enc.on('finish', () => resolve(Buffer.concat(bufs))) - enc.pushAny(obj) - enc.end() - }) - } - - /** - * The currently supported set of semantic types. May be modified by plugins. - * - * @type {SemanticMap} - */ - static get SEMANTIC_TYPES() { - return current_SEMANTIC_TYPES - } - - static set SEMANTIC_TYPES(val) { - current_SEMANTIC_TYPES = val - } - - /** - * Reset the supported semantic types to the original set, before any - * plugins modified the list. - */ - static reset() { - Encoder.SEMANTIC_TYPES = {...SEMANTIC_TYPES} - } -} - -Object.assign(SEMANTIC_TYPES, { - Array: Encoder.pushArray, - Date: Encoder._pushDate, - Buffer: Encoder._pushBuffer, - [Buffer.name]: Encoder._pushBuffer, // Might be mangled - Map: Encoder._pushMap, - NoFilter: Encoder._pushNoFilter, - [NoFilter.name]: Encoder._pushNoFilter, // Mßight be mangled - RegExp: Encoder._pushRegexp, - Set: Encoder._pushSet, - ArrayBuffer: Encoder._pushArrayBuffer, - Uint8ClampedArray: Encoder._pushTypedArray, - Uint8Array: Encoder._pushTypedArray, - Uint16Array: Encoder._pushTypedArray, - Uint32Array: Encoder._pushTypedArray, - Int8Array: Encoder._pushTypedArray, - Int16Array: Encoder._pushTypedArray, - Int32Array: Encoder._pushTypedArray, - Float32Array: Encoder._pushTypedArray, - Float64Array: Encoder._pushTypedArray, - URL: Encoder._pushURL, - Boolean: Encoder._pushBoxed, - Number: Encoder._pushBoxed, - String: Encoder._pushBoxed, -}) - -// Safari needs to get better. -if (typeof BigUint64Array !== 'undefined') { - SEMANTIC_TYPES[BigUint64Array.name] = Encoder._pushTypedArray -} -if (typeof BigInt64Array !== 'undefined') { - SEMANTIC_TYPES[BigInt64Array.name] = Encoder._pushTypedArray -} - -Encoder.reset() -module.exports = Encoder - -},{"./constants":68,"./utils":75,"buffer":63,"nofilter":181,"stream":232}],72:[function(require,module,exports){ -'use strict' - -const {Buffer} = require('buffer') -const encoder = require('./encoder') -const decoder = require('./decoder') -const {MT} = require('./constants') - -/** - * Wrapper around a JavaScript Map object that allows the keys to be - * any complex type. The base Map object allows this, but will only - * compare the keys by identity, not by value. CborMap translates keys - * to CBOR first (and base64's them to ensure by-value comparison). - * - * This is not a subclass of Object, because it would be tough to get - * the semantics to be an exact match. - * - * @extends Map - */ -class CborMap extends Map { - /** - * Creates an instance of CborMap. - * - * @param {Iterable} [iterable] An Array or other iterable - * object whose elements are key-value pairs (arrays with two elements, e.g. - * [[ 1, 'one' ],[ 2, 'two' ]]). Each key-value pair is added - * to the new CborMap; null values are treated as undefined. - */ - constructor(iterable) { - super(iterable) - } - - /** - * @ignore - */ - static _encode(key) { - return encoder.encodeCanonical(key).toString('base64') - } - - /** - * @ignore - */ - static _decode(key) { - return decoder.decodeFirstSync(key, 'base64') - } - - /** - * Retrieve a specified element. - * - * @param {any} key The key identifying the element to retrieve. - * Can be any type, which will be serialized into CBOR and compared by - * value. - * @returns {any} The element if it exists, or undefined. - */ - get(key) { - return super.get(CborMap._encode(key)) - } - - /** - * Adds or updates an element with a specified key and value. - * - * @param {any} key The key identifying the element to store. - * Can be any type, which will be serialized into CBOR and compared by - * value. - * @param {any} val The element to store. - * @returns {this} This object. - */ - set(key, val) { - return super.set(CborMap._encode(key), val) - } - - /** - * Removes the specified element. - * - * @param {any} key The key identifying the element to delete. Can be any - * type, which will be serialized into CBOR and compared by value. - * @returns {boolean} True if an element in the Map object existed and has - * been removed, or false if the element does not exist. - */ - delete(key) { - return super.delete(CborMap._encode(key)) - } - - /** - * Does an element with the specified key exist? - * - * @param {any} key The key identifying the element to check. - * Can be any type, which will be serialized into CBOR and compared by - * value. - * @returns {boolean} True if an element with the specified key exists in - * the Map object; otherwise false. - */ - has(key) { - return super.has(CborMap._encode(key)) - } - - /** - * Returns a new Iterator object that contains the keys for each element - * in the Map object in insertion order. The keys are decoded into their - * original format. - * - * @yields {any} The keys of the map. - */ - *keys() { - for (const k of super.keys()) { - yield CborMap._decode(k) - } - } - - /* eslint-disable jsdoc/require-returns-check */ - /** - * Returns a new Iterator object that contains the [key, value] pairs for - * each element in the Map object in insertion order. - * - * @yields {any[]} Key value pairs. - * @returns {IterableIterator} Key value pairs. - */ - *entries() { - for (const kv of super.entries()) { - yield [CborMap._decode(kv[0]), kv[1]] - } - } - /* eslint-enable jsdoc/require-returns-check */ - - /** - * Returns a new Iterator object that contains the [key, value] pairs for - * each element in the Map object in insertion order. - * - * @returns {IterableIterator} Key value pairs. - */ - [Symbol.iterator]() { - return this.entries() - } - - /** - * Executes a provided function once per each key/value pair in the Map - * object, in insertion order. - * - * @param {function(any, any, Map): undefined} fun Function to execute for - * each element, which takes a value, a key, and the Map being traversed. - * @param {any} thisArg Value to use as this when executing callback. - * @throws {TypeError} Invalid function. - */ - forEach(fun, thisArg) { - if (typeof fun !== 'function') { - throw new TypeError('Must be function') - } - for (const kv of super.entries()) { - fun.call(this, kv[1], CborMap._decode(kv[0]), this) - } - } - - /** - * Push the simple value onto the CBOR stream. - * - * @param {object} gen The generator to push onto. - * @returns {boolean} True on success. - */ - encodeCBOR(gen) { - if (!gen._pushInt(this.size, MT.MAP)) { - return false - } - if (gen.canonical) { - const entries = Array.from(super.entries()) - .map(kv => [Buffer.from(kv[0], 'base64'), kv[1]]) - entries.sort((a, b) => a[0].compare(b[0])) - for (const kv of entries) { - if (!(gen.push(kv[0]) && gen.pushAny(kv[1]))) { - return false - } - } - } else { - for (const kv of super.entries()) { - if (!(gen.push(Buffer.from(kv[0], 'base64')) && gen.pushAny(kv[1]))) { - return false - } - } - } - return true - } -} - -module.exports = CborMap - -},{"./constants":68,"./decoder":69,"./encoder":71,"buffer":63}],73:[function(require,module,exports){ -'use strict' - -const {MT, SIMPLE, SYMS} = require('./constants') - -/** - * A CBOR Simple Value that does not map onto a known constant. - */ -class Simple { - /** - * Creates an instance of Simple. - * - * @param {number} value The simple value's integer value. - */ - constructor(value) { - if (typeof value !== 'number') { - throw new Error(`Invalid Simple type: ${typeof value}`) - } - if ((value < 0) || (value > 255) || ((value | 0) !== value)) { - throw new Error(`value must be a small positive integer: ${value}`) - } - this.value = value - } - - /** - * Debug string for simple value. - * - * @returns {string} Formated string of `simple(value)`. - */ - toString() { - return `simple(${this.value})` - } - - /** - * Debug string for simple value. - * - * @param {number} depth How deep are we? - * @param {object} opts Options. - * @returns {string} Formatted string of `simple(value)`. - */ - [Symbol.for('nodejs.util.inspect.custom')](depth, opts) { - return `simple(${this.value})` - } - - /** - * Push the simple value onto the CBOR stream. - * - * @param {object} gen The generator to push onto. - * @returns {boolean} True on success. - */ - encodeCBOR(gen) { - return gen._pushInt(this.value, MT.SIMPLE_FLOAT) - } - - /** - * Is the given object a Simple? - * - * @param {any} obj Object to test. - * @returns {boolean} Is it Simple? - */ - static isSimple(obj) { - return obj instanceof Simple - } - - /** - * Decode from the CBOR additional information into a JavaScript value. - * If the CBOR item has no parent, return a "safe" symbol instead of - * `null` or `undefined`, so that the value can be passed through a - * stream in object mode. - * - * @param {number} val The CBOR additional info to convert. - * @param {boolean} [has_parent=true] Does the CBOR item have a parent? - * @param {boolean} [parent_indefinite=false] Is the parent element - * indefinitely encoded? - * @returns {(null|undefined|boolean|symbol|Simple)} The decoded value. - * @throws {Error} Invalid BREAK. - */ - static decode(val, has_parent = true, parent_indefinite = false) { - switch (val) { - case SIMPLE.FALSE: - return false - case SIMPLE.TRUE: - return true - case SIMPLE.NULL: - if (has_parent) { - return null - } - return SYMS.NULL - case SIMPLE.UNDEFINED: - if (has_parent) { - return undefined - } - return SYMS.UNDEFINED - case -1: - if (!has_parent || !parent_indefinite) { - throw new Error('Invalid BREAK') - } - return SYMS.BREAK - default: - return new Simple(val) - } - } -} - -module.exports = Simple - -},{"./constants":68}],74:[function(require,module,exports){ -'use strict' - -const constants = require('./constants') -const utils = require('./utils') -const INTERNAL_JSON = Symbol('INTERNAL_JSON') - -function setBuffersToJSON(obj, fn) { - // The data item tagged can be a byte string or any other data item. In the - // latter case, the tag applies to all of the byte string data items - // contained in the data item, except for those contained in a nested data - // item tagged with an expected conversion. - if (utils.isBufferish(obj)) { - obj.toJSON = fn - } else if (Array.isArray(obj)) { - for (const v of obj) { - setBuffersToJSON(v, fn) - } - } else if (obj && (typeof obj === 'object')) { - // FFS, complexity in the protocol. - - // There's some circular dependency in here. - // eslint-disable-next-line no-use-before-define - if (!(obj instanceof Tagged) || (obj.tag < 21) || (obj.tag > 23)) { - for (const v of Object.values(obj)) { - setBuffersToJSON(v, fn) - } - } - } -} - -function b64this() { - // eslint-disable-next-line no-invalid-this - return utils.base64(this) -} - -function b64urlThis() { - // eslint-disable-next-line no-invalid-this - return utils.base64url(this) -} - -function hexThis() { - // eslint-disable-next-line no-invalid-this - return this.toString('hex') -} - -function swapEndian(ab, size, byteOffset, byteLength) { - const dv = new DataView(ab) - const [getter, setter] = { - 2: [dv.getUint16, dv.setUint16], - 4: [dv.getUint32, dv.setUint32], - 8: [dv.getBigUint64, dv.setBigUint64], - }[size] - - const end = byteOffset + byteLength - for (let offset = byteOffset; offset < end; offset += size) { - setter.call(dv, offset, getter.call(dv, offset, true)) - } -} - -/** - * Convert a tagged value to a more interesting JavaScript type. Errors - * thrown in this function will be captured into the "err" property of the - * original Tagged instance. - * - * @callback TagFunction - * @param {any} value The value inside the tag. - * @param {Tagged} tag The enclosing Tagged instance; useful if you want to - * modify it and return it. Also available as "this". - * @returns {any} The transformed value. - */ - -/* eslint-disable jsdoc/check-types */ -/** - * A mapping from tag number to a tag decoding function. - * - * @typedef {Object.} TagMap - */ -/* eslint-enable jsdoc/check-types */ - -/** - * @type {TagMap} - * @private - */ -const TAGS = { - // Standard date/time string; see Section 3.4.1 - 0: v => new Date(v), - // Epoch-based date/time; see Section 3.4.2 - 1: v => new Date(v * 1000), - // Positive bignum; see Section 3.4.3 - 2: v => utils.bufferToBigInt(v), - // Negative bignum; see Section 3.4.3 - 3: v => constants.BI.MINUS_ONE - utils.bufferToBigInt(v), - // Expected conversion to base64url encoding; see Section 3.4.5.2 - 21: (v, tag) => { - if (utils.isBufferish(v)) { - tag[INTERNAL_JSON] = b64urlThis - } else { - setBuffersToJSON(v, b64urlThis) - } - return tag - }, - // Expected conversion to base64 encoding; see Section 3.4.5.2 - 22: (v, tag) => { - if (utils.isBufferish(v)) { - tag[INTERNAL_JSON] = b64this - } else { - setBuffersToJSON(v, b64this) - } - return tag - }, - // Expected conversion to base16 encoding; see Section Section 3.4.5.2 - 23: (v, tag) => { - if (utils.isBufferish(v)) { - tag[INTERNAL_JSON] = hexThis - } else { - setBuffersToJSON(v, hexThis) - } - return tag - }, - // URI; see Section 3.4.5.3 - 32: v => new URL(v), - // Base64url; see Section 3.4.5.3 - 33: (v, tag) => { - // If any of the following apply: - // - the encoded text string contains non-alphabet characters or - // only 1 alphabet character in the last block of 4 (where - // alphabet is defined by Section 5 of [RFC4648] for tag number 33 - // and Section 4 of [RFC4648] for tag number 34), or - if (!v.match(/^[a-zA-Z0-9_-]+$/)) { - throw new Error('Invalid base64url characters') - } - const last = v.length % 4 - if (last === 1) { - throw new Error('Invalid base64url length') - } - // - the padding bits in a 2- or 3-character block are not 0, or - if (last === 2) { - // The last 4 bits of the last character need to be zero. - if ('AQgw'.indexOf(v[v.length - 1]) === -1) { - throw new Error('Invalid base64 padding') - } - } else if (last === 3) { - // The last 2 bits of the last character need to be zero. - if ('AEIMQUYcgkosw048'.indexOf(v[v.length - 1]) === -1) { - throw new Error('Invalid base64 padding') - } - } - - // Or - // - the base64url encoding has padding characters, - // (caught above) - - // the string is invalid. - return tag - }, - // Base64; see Section 3.4.5.3 - 34: (v, tag) => { - // If any of the following apply: - // - the encoded text string contains non-alphabet characters or - // only 1 alphabet character in the last block of 4 (where - // alphabet is defined by Section 5 of [RFC4648] for tag number 33 - // and Section 4 of [RFC4648] for tag number 34), or - const m = v.match(/^[a-zA-Z0-9+/]+(?={0,2})$/) - if (!m) { - throw new Error('Invalid base64 characters') - } - if ((v.length % 4) !== 0) { - throw new Error('Invalid base64 length') - } - // - the padding bits in a 2- or 3-character block are not 0, or - if (m.groups.padding === '=') { - // The last 4 bits of the last character need to be zero. - if ('AQgw'.indexOf(v[v.length - 2]) === -1) { - throw new Error('Invalid base64 padding') - } - } else if (m.groups.padding === '==') { - // The last 2 bits of the last character need to be zero. - if ('AEIMQUYcgkosw048'.indexOf(v[v.length - 3]) === -1) { - throw new Error('Invalid base64 padding') - } - } - - // - the base64 encoding has the wrong number of padding characters, - // (caught above) - // the string is invalid. - return tag - }, - // Regular expression; see Section 2.4.4.3 - 35: v => new RegExp(v), - // https://github.com/input-output-hk/cbor-sets-spec/blob/master/CBOR_SETS.md - 258: v => new Set(v), -} - -const TYPED_ARRAY_TAGS = { - 64: Uint8Array, - 65: Uint16Array, - 66: Uint32Array, - // 67: BigUint64Array, Safari doesn't implement - 68: Uint8ClampedArray, - 69: Uint16Array, - 70: Uint32Array, - // 71: BigUint64Array, Safari doesn't implement - 72: Int8Array, - 73: Int16Array, - 74: Int32Array, - // 75: BigInt64Array, Safari doesn't implement - // 76: reserved - 77: Int16Array, - 78: Int32Array, - // 79: BigInt64Array, Safari doesn't implement - // 80: not implemented, float16 array - 81: Float32Array, - 82: Float64Array, - // 83: not implemented, float128 array - // 84: not implemented, float16 array - 85: Float32Array, - 86: Float64Array, - // 87: not implemented, float128 array -} - -// Safari -if (typeof BigUint64Array !== 'undefined') { - TYPED_ARRAY_TAGS[67] = BigUint64Array - TYPED_ARRAY_TAGS[71] = BigUint64Array -} -if (typeof BigInt64Array !== 'undefined') { - TYPED_ARRAY_TAGS[75] = BigInt64Array - TYPED_ARRAY_TAGS[79] = BigInt64Array -} - -function _toTypedArray(val, tagged) { - if (!utils.isBufferish(val)) { - throw new TypeError('val not a buffer') - } - const {tag} = tagged - // See https://tools.ietf.org/html/rfc8746 - const TypedClass = TYPED_ARRAY_TAGS[tag] - if (!TypedClass) { - throw new Error(`Invalid typed array tag: ${tag}`) - } - const little = tag & 0b00000100 - const float = (tag & 0b00010000) >> 4 - const sz = 2 ** (float + (tag & 0b00000011)) - - if ((!little !== utils.isBigEndian()) && (sz > 1)) { - swapEndian(val.buffer, sz, val.byteOffset, val.byteLength) - } - - const ab = val.buffer.slice(val.byteOffset, val.byteOffset + val.byteLength) - return new TypedClass(ab) -} - -for (const n of Object.keys(TYPED_ARRAY_TAGS)) { - TAGS[n] = _toTypedArray -} - -/** - * @type {TagMap} - * @private - */ -let current_TAGS = {} - -/** - * A CBOR tagged item, where the tag does not have semantics specified at the - * moment, or those semantics threw an error during parsing. Typically this will - * be an extension point you're not yet expecting. - */ -class Tagged { - /** - * Creates an instance of Tagged. - * - * @param {number} tag The number of the tag. - * @param {any} value The value inside the tag. - * @param {Error} [err] The error that was thrown parsing the tag, or null. - */ - constructor(tag, value, err) { - this.tag = tag - this.value = value - this.err = err - if (typeof this.tag !== 'number') { - throw new Error(`Invalid tag type (${typeof this.tag})`) - } - if ((this.tag < 0) || ((this.tag | 0) !== this.tag)) { - throw new Error(`Tag must be a positive integer: ${this.tag}`) - } - } - - toJSON() { - if (this[INTERNAL_JSON]) { - return this[INTERNAL_JSON].call(this.value) - } - const ret = { - tag: this.tag, - value: this.value, - } - if (this.err) { - ret.err = this.err - } - return ret - } - - /** - * Convert to a String. - * - * @returns {string} String of the form '1(2)'. - */ - toString() { - return `${this.tag}(${JSON.stringify(this.value)})` - } - - /** - * Push the simple value onto the CBOR stream. - * - * @param {object} gen The generator to push onto. - * @returns {boolean} True on success. - */ - encodeCBOR(gen) { - gen._pushTag(this.tag) - return gen.pushAny(this.value) - } - - /** - * If we have a converter for this type, do the conversion. Some converters - * are built-in. Additional ones can be passed in. If you want to remove - * a built-in converter, pass a converter in whose value is 'null' instead - * of a function. - * - * @param {object} converters Keys in the object are a tag number, the value - * is a function that takes the decoded CBOR and returns a JavaScript value - * of the appropriate type. Throw an exception in the function on errors. - * @returns {any} The converted item. - */ - convert(converters) { - let f = (converters == null) ? undefined : converters[this.tag] - if (typeof f !== 'function') { - f = Tagged.TAGS[this.tag] - if (typeof f !== 'function') { - return this - } - } - try { - return f.call(this, this.value, this) - } catch (error) { - if (error && error.message && (error.message.length > 0)) { - this.err = error.message - } else { - this.err = error - } - return this - } - } - - /** - * The current set of supported tags. May be modified by plugins. - * - * @type {TagMap} - * @static - */ - static get TAGS() { - return current_TAGS - } - - static set TAGS(val) { - current_TAGS = val - } - - /** - * Reset the supported tags to the original set, before any plugins modified - * the list. - */ - static reset() { - Tagged.TAGS = {...TAGS} - } -} -Tagged.INTERNAL_JSON = INTERNAL_JSON -Tagged.reset() -module.exports = Tagged - -},{"./constants":68,"./utils":75}],75:[function(require,module,exports){ -'use strict' - -const {Buffer} = require('buffer') -const NoFilter = require('nofilter') -const stream = require('stream') -const constants = require('./constants') -const {NUMBYTES, SHIFT32, BI, SYMS} = constants -const MAX_SAFE_HIGH = 0x1fffff - -/** - * Convert a UTF8-encoded Buffer to a JS string. If possible, throw an error - * on invalid UTF8. Byte Order Marks are not looked at or stripped. - * - * @private - */ -const td = new TextDecoder('utf8', {fatal: true, ignoreBOM: true}) -exports.utf8 = buf => td.decode(buf) -exports.utf8.checksUTF8 = true - -function isReadable(s) { - // Is this a readable stream? In the webpack version, instanceof isn't - // working correctly. - if (s instanceof stream.Readable) { - return true - } - return ['read', 'on', 'pipe'].every(f => typeof s[f] === 'function') -} - -exports.isBufferish = function isBufferish(b) { - return b && - (typeof b === 'object') && - ((Buffer.isBuffer(b)) || - (b instanceof Uint8Array) || - (b instanceof Uint8ClampedArray) || - (b instanceof ArrayBuffer) || - (b instanceof DataView)) -} - -exports.bufferishToBuffer = function bufferishToBuffer(b) { - if (Buffer.isBuffer(b)) { - return b - } else if (ArrayBuffer.isView(b)) { - return Buffer.from(b.buffer, b.byteOffset, b.byteLength) - } else if (b instanceof ArrayBuffer) { - return Buffer.from(b) - } - return null -} - -exports.parseCBORint = function parseCBORint(ai, buf) { - switch (ai) { - case NUMBYTES.ONE: - return buf.readUInt8(0) - case NUMBYTES.TWO: - return buf.readUInt16BE(0) - case NUMBYTES.FOUR: - return buf.readUInt32BE(0) - case NUMBYTES.EIGHT: { - const f = buf.readUInt32BE(0) - const g = buf.readUInt32BE(4) - if (f > MAX_SAFE_HIGH) { - return (BigInt(f) * BI.SHIFT32) + BigInt(g) - } - return (f * SHIFT32) + g - } - default: - throw new Error(`Invalid additional info for int: ${ai}`) - } -} - -exports.writeHalf = function writeHalf(buf, half) { - // Assume 0, -0, NaN, Infinity, and -Infinity have already been caught - - // HACK: everyone settle in. This isn't going to be pretty. - // Translate cn-cbor's C code (from Carsten Borman): - - // uint32_t be32; - // uint16_t be16, u16; - // union { - // float f; - // uint32_t u; - // } u32; - // u32.f = float_val; - - const u32 = Buffer.allocUnsafe(4) - u32.writeFloatBE(half, 0) - const u = u32.readUInt32BE(0) - - // If ((u32.u & 0x1FFF) == 0) { /* worth trying half */ - - // hildjj: If the lower 13 bits aren't 0, - // we will lose precision in the conversion. - // mant32 = 24bits, mant16 = 11bits, 24-11 = 13 - if ((u & 0x1FFF) !== 0) { - return false - } - - // Sign, exponent, mantissa - // int s16 = (u32.u >> 16) & 0x8000; - // int exp = (u32.u >> 23) & 0xff; - // int mant = u32.u & 0x7fffff; - - let s16 = (u >> 16) & 0x8000 // Top bit is sign - const exp = (u >> 23) & 0xff // Then 5 bits of exponent - const mant = u & 0x7fffff - - // Hildjj: zeros already handled. Assert if you don't believe me. - // if (exp == 0 && mant == 0) - // ; /* 0.0, -0.0 */ - - // else if (exp >= 113 && exp <= 142) /* normalized */ - // s16 += ((exp - 112) << 10) + (mant >> 13); - - if ((exp >= 113) && (exp <= 142)) { - s16 += ((exp - 112) << 10) + (mant >> 13) - } else if ((exp >= 103) && (exp < 113)) { - // Denormalized numbers - // else if (exp >= 103 && exp < 113) { /* denorm, exp16 = 0 */ - // if (mant & ((1 << (126 - exp)) - 1)) - // goto float32; /* loss of precision */ - // s16 += ((mant + 0x800000) >> (126 - exp)); - - if (mant & ((1 << (126 - exp)) - 1)) { - return false - } - s16 += ((mant + 0x800000) >> (126 - exp)) - } else { - // } else if (exp == 255 && mant == 0) { /* Inf */ - // s16 += 0x7c00; - - // hildjj: Infinity already handled - - // } else - // goto float32; /* loss of range */ - - return false - } - - // Done - // ensure_writable(3); - // u16 = s16; - // be16 = hton16p((const uint8_t*)&u16); - buf.writeUInt16BE(s16) - return true -} - -exports.parseHalf = function parseHalf(buf) { - const sign = buf[0] & 0x80 ? -1 : 1 - const exp = (buf[0] & 0x7C) >> 2 - const mant = ((buf[0] & 0x03) << 8) | buf[1] - if (!exp) { - return sign * 5.9604644775390625e-8 * mant - } else if (exp === 0x1f) { - return sign * (mant ? NaN : Infinity) - } - return sign * (2 ** (exp - 25)) * (1024 + mant) -} - -exports.parseCBORfloat = function parseCBORfloat(buf) { - switch (buf.length) { - case 2: - return exports.parseHalf(buf) - case 4: - return buf.readFloatBE(0) - case 8: - return buf.readDoubleBE(0) - default: - throw new Error(`Invalid float size: ${buf.length}`) - } -} - -exports.hex = function hex(s) { - return Buffer.from(s.replace(/^0x/, ''), 'hex') -} - -exports.bin = function bin(s) { - s = s.replace(/\s/g, '') - let start = 0 - let end = (s.length % 8) || 8 - const chunks = [] - while (end <= s.length) { - chunks.push(parseInt(s.slice(start, end), 2)) - start = end - end += 8 - } - return Buffer.from(chunks) -} - -exports.arrayEqual = function arrayEqual(a, b) { - if ((a == null) && (b == null)) { - return true - } - if ((a == null) || (b == null)) { - return false - } - return (a.length === b.length) && a.every((elem, i) => elem === b[i]) -} - -exports.bufferToBigInt = function bufferToBigInt(buf) { - return BigInt(`0x${buf.toString('hex')}`) -} - -exports.cborValueToString = function cborValueToString(val, float_bytes = -1) { - switch (typeof val) { - case 'symbol': { - switch (val) { - case SYMS.NULL: - return 'null' - case SYMS.UNDEFINED: - return 'undefined' - case SYMS.BREAK: - return 'BREAK' - } - // Impossible in node 10 - /* istanbul ignore if */ - if (val.description) { - return val.description - } - // On node10, Symbol doesn't have description. Parse it out of the - // toString value, which looks like `Symbol(foo)`. - const s = val.toString() - const m = s.match(/^Symbol\((?.*)\)/) - /* istanbul ignore if */ - if (m && m.groups.name) { - // Impossible in node 12+ - /* istanbul ignore next */ - return m.groups.name - } - return 'Symbol' - } - case 'string': - return JSON.stringify(val) - case 'bigint': - return val.toString() - case 'number': { - const s = Object.is(val, -0) ? '-0' : String(val) - return (float_bytes > 0) ? `${s}_${float_bytes}` : s - } - case 'object': { - // A null should be caught above - const buf = exports.bufferishToBuffer(val) - if (buf) { - const hex = buf.toString('hex') - return (float_bytes === -Infinity) ? hex : `h'${hex}'` - } - if (typeof val[Symbol.for('nodejs.util.inspect.custom')] === 'function') { - return val[Symbol.for('nodejs.util.inspect.custom')]() - } - // Shouldn't get non-empty arrays here - if (Array.isArray(val)) { - return '[]' - } - // This should be all that is left - return '{}' - } - } - return String(val) -} - -exports.guessEncoding = function guessEncoding(input, encoding) { - if (typeof input === 'string') { - return new NoFilter(input, (encoding == null) ? 'hex' : encoding) - } - const buf = exports.bufferishToBuffer(input) - if (buf) { - return new NoFilter(buf) - } - if (isReadable(input)) { - return input - } - throw new Error('Unknown input type') -} - -const B64URL_SWAPS = { - '=': '', - '+': '-', - '/': '_', -} - -/** - * @param {Buffer|Uint8Array|Uint8ClampedArray|ArrayBuffer|DataView} buf - * Buffer to convert. - * @returns {string} Base64url string. - * @private - */ -exports.base64url = function base64url(buf) { - return exports.bufferishToBuffer(buf) - .toString('base64') - .replace(/[=+/]/g, c => B64URL_SWAPS[c]) -} - -/** - * @param {Buffer|Uint8Array|Uint8ClampedArray|ArrayBuffer|DataView} buf - * Buffer to convert. - * @returns {string} Base64 string. - * @private - */ -exports.base64 = function base64(buf) { - return exports.bufferishToBuffer(buf).toString('base64') -} - -exports.isBigEndian = function isBigEndian() { - const array = new Uint8Array(4) - const view = new Uint32Array(array.buffer) - return !((view[0] = 1) & array[0]) -} - -},{"./constants":68,"buffer":63,"nofilter":181,"stream":232}],76:[function(require,module,exports){ -// Tweaked version of nathan7's binary-parse-stream -// (see https://github.com/nathan7/binary-parse-stream) -// Uses NoFilter instead of the readable in the original. Removes -// the ability to read -1, which was odd and un-needed. -// License for binary-parse-stream: MIT - -// binary-parse-stream is now unmaintained, so I have rewritten it as -// more modern JS so I can get tsc to help check types. - -'use strict' -const stream = require('stream') -const NoFilter = require('nofilter') - -/** - * BinaryParseStream is a TransformStream that consumes buffers and outputs - * objects on the other end. It expects your subclass to implement a `_parse` - * method that is a generator. When your generator yields a number, it'll be - * fed a buffer of that length from the input. When your generator returns, - * the return value will be pushed to the output side. - * - * @extends stream.Transform - */ -class BinaryParseStream extends stream.Transform { - /** - * Creates an instance of BinaryParseStream. - * - * @param {stream.TransformOptions} options Stream options. - * @memberof BinaryParseStream - */ - constructor(options) { - super(options) - // Doesn't work to pass these in as opts, for some reason - // also, work around typescript not knowing TransformStream internals - // eslint-disable-next-line dot-notation - this['_writableState'].objectMode = false - // eslint-disable-next-line dot-notation - this['_readableState'].objectMode = true - - this.bs = new NoFilter() - this.__restart() - } - - _transform(fresh, encoding, cb) { - this.bs.write(fresh) - - while (this.bs.length >= this.__needed) { - let ret = null - const chunk = (this.__needed === null) ? - undefined : - this.bs.read(this.__needed) - - try { - ret = this.__parser.next(chunk) - } catch (e) { - return cb(e) - } - - if (this.__needed) { - this.__fresh = false - } - - if (ret.done) { - this.push(ret.value) - this.__restart() - } else { - this.__needed = ret.value || Infinity - } - } - - return cb() - } - - /** - * Subclasses must override this to set their parsing behavior. Yield a - * number to receive a Buffer of that many bytes. - * - * @abstract - * @returns {Generator} - */ - /* istanbul ignore next */ - *_parse() { // eslint-disable-line class-methods-use-this, require-yield - throw new Error('Must be implemented in subclass') - } - - __restart() { - this.__needed = null - this.__parser = this._parse() - this.__fresh = true - } - - _flush(cb) { - cb(this.__fresh ? null : new Error('unexpected end of input')) - } -} - -module.exports = BinaryParseStream - -},{"nofilter":181,"stream":232}],77:[function(require,module,exports){ -var Buffer = require('safe-buffer').Buffer -var Transform = require('stream').Transform -var StringDecoder = require('string_decoder').StringDecoder -var inherits = require('inherits') - -function CipherBase (hashMode) { - Transform.call(this) - this.hashMode = typeof hashMode === 'string' - if (this.hashMode) { - this[hashMode] = this._finalOrDigest - } else { - this.final = this._finalOrDigest - } - if (this._final) { - this.__final = this._final - this._final = null - } - this._decoder = null - this._encoding = null -} -inherits(CipherBase, Transform) - -CipherBase.prototype.update = function (data, inputEnc, outputEnc) { - if (typeof data === 'string') { - data = Buffer.from(data, inputEnc) - } - - var outData = this._update(data) - if (this.hashMode) return this - - if (outputEnc) { - outData = this._toString(outData, outputEnc) - } - - return outData -} - -CipherBase.prototype.setAutoPadding = function () {} -CipherBase.prototype.getAuthTag = function () { - throw new Error('trying to get auth tag in unsupported state') -} - -CipherBase.prototype.setAuthTag = function () { - throw new Error('trying to set auth tag in unsupported state') -} - -CipherBase.prototype.setAAD = function () { - throw new Error('trying to set aad in unsupported state') -} - -CipherBase.prototype._transform = function (data, _, next) { - var err - try { - if (this.hashMode) { - this._update(data) - } else { - this.push(this._update(data)) - } - } catch (e) { - err = e - } finally { - next(err) - } -} -CipherBase.prototype._flush = function (done) { - var err - try { - this.push(this.__final()) - } catch (e) { - err = e - } - - done(err) -} -CipherBase.prototype._finalOrDigest = function (outputEnc) { - var outData = this.__final() || Buffer.alloc(0) - if (outputEnc) { - outData = this._toString(outData, outputEnc, true) - } - return outData -} - -CipherBase.prototype._toString = function (value, enc, fin) { - if (!this._decoder) { - this._decoder = new StringDecoder(enc) - this._encoding = enc - } - - if (this._encoding !== enc) throw new Error('can\'t switch encodings') - - var out = this._decoder.write(value) - if (fin) { - out += this._decoder.end() - } - - return out -} - -module.exports = CipherBase - -},{"inherits":155,"safe-buffer":221,"stream":232,"string_decoder":247}],78:[function(require,module,exports){ -module.exports={ - "O_RDONLY": 0, - "O_WRONLY": 1, - "O_RDWR": 2, - "S_IFMT": 61440, - "S_IFREG": 32768, - "S_IFDIR": 16384, - "S_IFCHR": 8192, - "S_IFBLK": 24576, - "S_IFIFO": 4096, - "S_IFLNK": 40960, - "S_IFSOCK": 49152, - "O_CREAT": 512, - "O_EXCL": 2048, - "O_NOCTTY": 131072, - "O_TRUNC": 1024, - "O_APPEND": 8, - "O_DIRECTORY": 1048576, - "O_NOFOLLOW": 256, - "O_SYNC": 128, - "O_SYMLINK": 2097152, - "O_NONBLOCK": 4, - "S_IRWXU": 448, - "S_IRUSR": 256, - "S_IWUSR": 128, - "S_IXUSR": 64, - "S_IRWXG": 56, - "S_IRGRP": 32, - "S_IWGRP": 16, - "S_IXGRP": 8, - "S_IRWXO": 7, - "S_IROTH": 4, - "S_IWOTH": 2, - "S_IXOTH": 1, - "E2BIG": 7, - "EACCES": 13, - "EADDRINUSE": 48, - "EADDRNOTAVAIL": 49, - "EAFNOSUPPORT": 47, - "EAGAIN": 35, - "EALREADY": 37, - "EBADF": 9, - "EBADMSG": 94, - "EBUSY": 16, - "ECANCELED": 89, - "ECHILD": 10, - "ECONNABORTED": 53, - "ECONNREFUSED": 61, - "ECONNRESET": 54, - "EDEADLK": 11, - "EDESTADDRREQ": 39, - "EDOM": 33, - "EDQUOT": 69, - "EEXIST": 17, - "EFAULT": 14, - "EFBIG": 27, - "EHOSTUNREACH": 65, - "EIDRM": 90, - "EILSEQ": 92, - "EINPROGRESS": 36, - "EINTR": 4, - "EINVAL": 22, - "EIO": 5, - "EISCONN": 56, - "EISDIR": 21, - "ELOOP": 62, - "EMFILE": 24, - "EMLINK": 31, - "EMSGSIZE": 40, - "EMULTIHOP": 95, - "ENAMETOOLONG": 63, - "ENETDOWN": 50, - "ENETRESET": 52, - "ENETUNREACH": 51, - "ENFILE": 23, - "ENOBUFS": 55, - "ENODATA": 96, - "ENODEV": 19, - "ENOENT": 2, - "ENOEXEC": 8, - "ENOLCK": 77, - "ENOLINK": 97, - "ENOMEM": 12, - "ENOMSG": 91, - "ENOPROTOOPT": 42, - "ENOSPC": 28, - "ENOSR": 98, - "ENOSTR": 99, - "ENOSYS": 78, - "ENOTCONN": 57, - "ENOTDIR": 20, - "ENOTEMPTY": 66, - "ENOTSOCK": 38, - "ENOTSUP": 45, - "ENOTTY": 25, - "ENXIO": 6, - "EOPNOTSUPP": 102, - "EOVERFLOW": 84, - "EPERM": 1, - "EPIPE": 32, - "EPROTO": 100, - "EPROTONOSUPPORT": 43, - "EPROTOTYPE": 41, - "ERANGE": 34, - "EROFS": 30, - "ESPIPE": 29, - "ESRCH": 3, - "ESTALE": 70, - "ETIME": 101, - "ETIMEDOUT": 60, - "ETXTBSY": 26, - "EWOULDBLOCK": 35, - "EXDEV": 18, - "SIGHUP": 1, - "SIGINT": 2, - "SIGQUIT": 3, - "SIGILL": 4, - "SIGTRAP": 5, - "SIGABRT": 6, - "SIGIOT": 6, - "SIGBUS": 10, - "SIGFPE": 8, - "SIGKILL": 9, - "SIGUSR1": 30, - "SIGSEGV": 11, - "SIGUSR2": 31, - "SIGPIPE": 13, - "SIGALRM": 14, - "SIGTERM": 15, - "SIGCHLD": 20, - "SIGCONT": 19, - "SIGSTOP": 17, - "SIGTSTP": 18, - "SIGTTIN": 21, - "SIGTTOU": 22, - "SIGURG": 16, - "SIGXCPU": 24, - "SIGXFSZ": 25, - "SIGVTALRM": 26, - "SIGPROF": 27, - "SIGWINCH": 28, - "SIGIO": 23, - "SIGSYS": 12, - "SSL_OP_ALL": 2147486719, - "SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION": 262144, - "SSL_OP_CIPHER_SERVER_PREFERENCE": 4194304, - "SSL_OP_CISCO_ANYCONNECT": 32768, - "SSL_OP_COOKIE_EXCHANGE": 8192, - "SSL_OP_CRYPTOPRO_TLSEXT_BUG": 2147483648, - "SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS": 2048, - "SSL_OP_EPHEMERAL_RSA": 0, - "SSL_OP_LEGACY_SERVER_CONNECT": 4, - "SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER": 32, - "SSL_OP_MICROSOFT_SESS_ID_BUG": 1, - "SSL_OP_MSIE_SSLV2_RSA_PADDING": 0, - "SSL_OP_NETSCAPE_CA_DN_BUG": 536870912, - "SSL_OP_NETSCAPE_CHALLENGE_BUG": 2, - "SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG": 1073741824, - "SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG": 8, - "SSL_OP_NO_COMPRESSION": 131072, - "SSL_OP_NO_QUERY_MTU": 4096, - "SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION": 65536, - "SSL_OP_NO_SSLv2": 16777216, - "SSL_OP_NO_SSLv3": 33554432, - "SSL_OP_NO_TICKET": 16384, - "SSL_OP_NO_TLSv1": 67108864, - "SSL_OP_NO_TLSv1_1": 268435456, - "SSL_OP_NO_TLSv1_2": 134217728, - "SSL_OP_PKCS1_CHECK_1": 0, - "SSL_OP_PKCS1_CHECK_2": 0, - "SSL_OP_SINGLE_DH_USE": 1048576, - "SSL_OP_SINGLE_ECDH_USE": 524288, - "SSL_OP_SSLEAY_080_CLIENT_DH_BUG": 128, - "SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG": 0, - "SSL_OP_TLS_BLOCK_PADDING_BUG": 512, - "SSL_OP_TLS_D5_BUG": 256, - "SSL_OP_TLS_ROLLBACK_BUG": 8388608, - "ENGINE_METHOD_DSA": 2, - "ENGINE_METHOD_DH": 4, - "ENGINE_METHOD_RAND": 8, - "ENGINE_METHOD_ECDH": 16, - "ENGINE_METHOD_ECDSA": 32, - "ENGINE_METHOD_CIPHERS": 64, - "ENGINE_METHOD_DIGESTS": 128, - "ENGINE_METHOD_STORE": 256, - "ENGINE_METHOD_PKEY_METHS": 512, - "ENGINE_METHOD_PKEY_ASN1_METHS": 1024, - "ENGINE_METHOD_ALL": 65535, - "ENGINE_METHOD_NONE": 0, - "DH_CHECK_P_NOT_SAFE_PRIME": 2, - "DH_CHECK_P_NOT_PRIME": 1, - "DH_UNABLE_TO_CHECK_GENERATOR": 4, - "DH_NOT_SUITABLE_GENERATOR": 8, - "NPN_ENABLED": 1, - "RSA_PKCS1_PADDING": 1, - "RSA_SSLV23_PADDING": 2, - "RSA_NO_PADDING": 3, - "RSA_PKCS1_OAEP_PADDING": 4, - "RSA_X931_PADDING": 5, - "RSA_PKCS1_PSS_PADDING": 6, - "POINT_CONVERSION_COMPRESSED": 2, - "POINT_CONVERSION_UNCOMPRESSED": 4, - "POINT_CONVERSION_HYBRID": 6, - "F_OK": 0, - "R_OK": 4, - "W_OK": 2, - "X_OK": 1, - "UV_UDP_REUSEADDR": 4 -} - -},{}],79:[function(require,module,exports){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// NOTE: These type checking functions intentionally don't use `instanceof` -// because it is fragile and can be easily faked with `Object.create()`. - -function isArray(arg) { - if (Array.isArray) { - return Array.isArray(arg); - } - return objectToString(arg) === '[object Array]'; -} -exports.isArray = isArray; - -function isBoolean(arg) { - return typeof arg === 'boolean'; -} -exports.isBoolean = isBoolean; - -function isNull(arg) { - return arg === null; -} -exports.isNull = isNull; - -function isNullOrUndefined(arg) { - return arg == null; -} -exports.isNullOrUndefined = isNullOrUndefined; - -function isNumber(arg) { - return typeof arg === 'number'; -} -exports.isNumber = isNumber; - -function isString(arg) { - return typeof arg === 'string'; -} -exports.isString = isString; - -function isSymbol(arg) { - return typeof arg === 'symbol'; -} -exports.isSymbol = isSymbol; - -function isUndefined(arg) { - return arg === void 0; -} -exports.isUndefined = isUndefined; - -function isRegExp(re) { - return objectToString(re) === '[object RegExp]'; -} -exports.isRegExp = isRegExp; - -function isObject(arg) { - return typeof arg === 'object' && arg !== null; -} -exports.isObject = isObject; - -function isDate(d) { - return objectToString(d) === '[object Date]'; -} -exports.isDate = isDate; - -function isError(e) { - return (objectToString(e) === '[object Error]' || e instanceof Error); -} -exports.isError = isError; - -function isFunction(arg) { - return typeof arg === 'function'; -} -exports.isFunction = isFunction; - -function isPrimitive(arg) { - return arg === null || - typeof arg === 'boolean' || - typeof arg === 'number' || - typeof arg === 'string' || - typeof arg === 'symbol' || // ES6 symbol - typeof arg === 'undefined'; -} -exports.isPrimitive = isPrimitive; - -exports.isBuffer = require('buffer').Buffer.isBuffer; - -function objectToString(o) { - return Object.prototype.toString.call(o); -} - -},{"buffer":63}],80:[function(require,module,exports){ -(function (global,Buffer){(function (){ -/* jshint esversion: 6 */ -/* jslint node: true */ -'use strict'; - -const AlgToTags = { - PS512: -39, - PS384: -38, - PS256: -37, - RS512: -259, - RS384: -258, - RS256: -257, - 'ECDH-SS-512': -28, - 'ECDH-SS': -27, - 'ECDH-ES-512': -26, - 'ECDH-ES': -25, - ES256: -7, - ES384: -35, - ES512: -36, - direct: -6, - A128GCM: 1, - A192GCM: 2, - A256GCM: 3, - 'SHA-256_64': 4, - 'SHA-256-64': 4, - 'HS256/64': 4, - 'SHA-256': 5, - HS256: 5, - 'SHA-384': 6, - HS384: 6, - 'SHA-512': 7, - HS512: 7, - 'AES-CCM-16-64-128': 10, - 'AES-CCM-16-128/64': 10, - 'AES-CCM-16-64-256': 11, - 'AES-CCM-16-256/64': 11, - 'AES-CCM-64-64-128': 12, - 'AES-CCM-64-128/64': 12, - 'AES-CCM-64-64-256': 13, - 'AES-CCM-64-256/64': 13, - 'AES-MAC-128/64': 14, - 'AES-MAC-256/64': 15, - 'AES-MAC-128/128': 25, - 'AES-MAC-256/128': 26, - 'AES-CCM-16-128-128': 30, - 'AES-CCM-16-128/128': 30, - 'AES-CCM-16-128-256': 31, - 'AES-CCM-16-256/128': 31, - 'AES-CCM-64-128-128': 32, - 'AES-CCM-64-128/128': 32, - 'AES-CCM-64-128-256': 33, - 'AES-CCM-64-256/128': 33 -}; - -const Translators = { - kid: (value) => { - return Buffer.from(value, 'utf8'); - }, - alg: (value) => { - if (!(AlgToTags[value])) { - throw new Error('Unknown \'alg\' parameter, ' + value); - } - return AlgToTags[value]; - } -}; - -const HeaderParameters = { - partyUNonce: -22, - static_key_id: -3, - static_key: -2, - ephemeral_key: -1, - alg: 1, - crit: 2, - content_type: 3, - ctyp: 3, // one could question this but it makes testing easier - kid: 4, - IV: 5, - Partial_IV: 6, - counter_signature: 7, - x5chain: 33 -}; - -exports.EMPTY_BUFFER = Buffer.alloc(0); - -exports.TranslateHeaders = function (header) { - const result = new Map(); - for (const param in header) { - if (!HeaderParameters[param]) { - throw new Error('Unknown parameter, \'' + param + '\''); - } - let value = header[param]; - if (Translators[param]) { - value = Translators[param](header[param]); - } - if (value !== undefined && value !== null) { - result.set(HeaderParameters[param], value); - } - } - return result; -}; - -const KeyParameters = { - crv: -1, - k: -1, - x: -2, - y: -3, - d: -4, - kty: 1 -}; - -const KeyTypes = { - OKP: 1, - EC2: 2, - RSA: 3, - Symmetric: 4 -}; - -const KeyCrv = { - 'P-256': 1, - 'P-384': 2, - 'P-521': 3, - X25519: 4, - X448: 5, - Ed25519: 6, - Ed448: 7 -}; - -const KeyTranslators = { - kty: (value) => { - if (!(KeyTypes[value])) { - throw new Error('Unknown \'kty\' parameter, ' + value); - } - return KeyTypes[value]; - }, - crv: (value) => { - if (!(KeyCrv[value])) { - throw new Error('Unknown \'crv\' parameter, ' + value); - } - return KeyCrv[value]; - } -}; - -exports.TranslateKey = function (key) { - const result = new Map(); - for (const param in key) { - if (!KeyParameters[param]) { - throw new Error('Unknown parameter, \'' + param + '\''); - } - let value = key[param]; - if (KeyTranslators[param]) { - value = KeyTranslators[param](value); - } - result.set(KeyParameters[param], value); - } - return result; -}; - -module.exports.xor = function (a, b) { - const buffer = Buffer.alloc(Math.max(a.length, b.length)); - for (let i = 1; i <= buffer.length; ++i) { - const av = (a.length - i) < 0 ? 0 : a[a.length - i]; - const bv = (b.length - i) < 0 ? 0 : b[b.length - i]; - buffer[buffer.length - i] = av ^ bv; - } - return buffer; -}; - -exports.HeaderParameters = HeaderParameters; - -exports.runningInNode = function () { - return Object.prototype.toString.call(global.process) === '[object process]'; -}; - -}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer) -},{"buffer":63}],81:[function(require,module,exports){ -(function (Buffer){(function (){ -/* jshint esversion: 6 */ -/* jslint node: true */ -'use strict'; - -const cbor = require('cbor'); -const crypto = require('crypto'); -const Promise = require('any-promise'); -const common = require('./common'); -const HKDF = require('node-hkdf-sync'); - -const Tagged = cbor.Tagged; - -const EMPTY_BUFFER = common.EMPTY_BUFFER; -const EncryptTag = exports.EncryptTag = 96; -const Encrypt0Tag = exports.Encrypt0Tag = 16; - -const runningInNode = common.runningInNode; - -const TagToAlg = { - 1: 'A128GCM', - 2: 'A192GCM', - 3: 'A256GCM', - 10: 'AES-CCM-16-64-128', - 11: 'AES-CCM-16-64-256', - 12: 'AES-CCM-64-64-128', - 13: 'AES-CCM-64-64-256', - 30: 'AES-CCM-16-128-128', - 31: 'AES-CCM-16-128-256', - 32: 'AES-CCM-64-128-128', - 33: 'AES-CCM-64-128-256' -}; - -const COSEAlgToNodeAlg = { - A128GCM: 'aes-128-gcm', - A192GCM: 'aes-192-gcm', - A256GCM: 'aes-256-gcm', - - 'AES-CCM-16-64-128': 'aes-128-ccm', - 'AES-CCM-16-64-256': 'aes-256-ccm', - 'AES-CCM-64-64-128': 'aes-128-ccm', - 'AES-CCM-64-64-256': 'aes-256-ccm', - 'AES-CCM-16-128-128': 'aes-128-ccm', - 'AES-CCM-16-128-256': 'aes-256-ccm', - 'AES-CCM-64-128-128': 'aes-128-ccm', - 'AES-CCM-64-128-256': 'aes-256-ccm' -}; - -const isNodeAlg = { - 1: true, // A128GCM - 2: true, // A192GCM - 3: true // A256GCM -}; - -const isCCMAlg = { - 10: true, // AES-CCM-16-64-128 - 11: true, // AES-CCM-16-64-256 - 12: true, // AES-CCM-64-64-128 - 13: true, // AES-CCM-64-64-256 - 30: true, // AES-CCM-16-128-128 - 31: true, // AES-CCM-16-128-256 - 32: true, // AES-CCM-64-128-128 - 33: true // AES-CCM-64-128-256 -}; - -const authTagLength = { - 1: 16, - 2: 16, - 3: 16, - 10: 8, // AES-CCM-16-64-128 - 11: 8, // AES-CCM-16-64-256 - 12: 8, // AES-CCM-64-64-128 - 13: 8, // AES-CCM-64-64-256 - 30: 16, // AES-CCM-16-128-128 - 31: 16, // AES-CCM-16-128-256 - 32: 16, // AES-CCM-64-128-128 - 33: 16 // AES-CCM-64-128-256 -}; - -const ivLenght = { - 1: 12, // A128GCM - 2: 12, // A192GCM - 3: 12, // A256GCM - 10: 13, // AES-CCM-16-64-128 - 11: 13, // AES-CCM-16-64-256 - 12: 7, // AES-CCM-64-64-128 - 13: 7, // AES-CCM-64-64-256 - 30: 13, // AES-CCM-16-128-128 - 31: 13, // AES-CCM-16-128-256 - 32: 7, // AES-CCM-64-128-128 - 33: 7 // AES-CCM-64-128-256 -}; - -const keyLength = { - 1: 16, // A128GCM - 2: 24, // A192GCM - 3: 32, // A256GCM - 10: 16, // AES-CCM-16-64-128 - 11: 32, // AES-CCM-16-64-256 - 12: 16, // AES-CCM-64-64-128 - 13: 32, // AES-CCM-64-64-256 - 30: 16, // AES-CCM-16-128-128 - 31: 32, // AES-CCM-16-128-256 - 32: 16, // AES-CCM-64-128-128 - 33: 32, // AES-CCM-64-128-256 - 'P-521': 66, - 'P-256': 32 -}; - -const HKDFAlg = { - 'ECDH-ES': 'sha256', - 'ECDH-ES-512': 'sha512', - 'ECDH-SS': 'sha256', - 'ECDH-SS-512': 'sha512' -}; - -const nodeCRV = { - 'P-521': 'secp521r1', - 'P-256': 'prime256v1' -}; - -function createAAD (p, context, externalAAD) { - p = (!p.size) ? EMPTY_BUFFER : cbor.encode(p); - const encStructure = [ - context, - p, - externalAAD - ]; - return cbor.encode(encStructure); -} - -function _randomSource (bytes) { - return crypto.randomBytes(bytes); -} - -function nodeEncrypt (payload, key, alg, iv, aad, ccm = false) { - const nodeAlg = COSEAlgToNodeAlg[TagToAlg[alg]]; - const chiperOptions = ccm ? { authTagLength: authTagLength[alg] } : null; - const aadOptions = ccm ? { plaintextLength: Buffer.byteLength(payload) } : null; - const cipher = crypto.createCipheriv(nodeAlg, key, iv, chiperOptions); - cipher.setAAD(aad, aadOptions); - return Buffer.concat([ - cipher.update(payload), - cipher.final(), - cipher.getAuthTag() - ]); -} - -function createContext (rp, alg, partyUNonce) { - return cbor.encode([ - alg, // AlgorithmID - [ // PartyUInfo - null, // identity - (partyUNonce || null), // nonce - null // other - ], - [ // PartyVInfo - null, // identity - null, // nonce - null // other - ], - [ - keyLength[alg] * 8, // keyDataLength - rp // protected - ] - ]); -} - -exports.create = function (headers, payload, recipients, options) { - return new Promise((resolve, reject) => { - options = options || {}; - const externalAAD = options.externalAAD || EMPTY_BUFFER; - const randomSource = options.randomSource || _randomSource; - let u = headers.u || {}; - let p = headers.p || {}; - - p = common.TranslateHeaders(p); - u = common.TranslateHeaders(u); - - const alg = p.get(common.HeaderParameters.alg) || u.get(common.HeaderParameters.alg); - - if (!alg) { - throw new Error('Missing mandatory parameter \'alg\''); - } - - if (Array.isArray(recipients)) { - if (recipients.length === 0) { - throw new Error('There has to be at least one recipent'); - } - if (recipients.length > 1) { - throw new Error('Encrypting with multiple recipents is not implemented'); - } - - let iv; - if (options.contextIv) { - const partialIv = randomSource(2); - iv = common.xor(partialIv, options.contextIv); - u.set(common.HeaderParameters.Partial_IV, partialIv); - } else { - iv = randomSource(ivLenght[alg]); - u.set(common.HeaderParameters.IV, iv); - } - - const aad = createAAD(p, 'Encrypt', externalAAD); - - let key; - let recipientStruct; - // TODO do a more accurate check - if (recipients[0] && recipients[0].p && - (recipients[0].p.alg === 'ECDH-ES' || - recipients[0].p.alg === 'ECDH-ES-512' || - recipients[0].p.alg === 'ECDH-SS' || - recipients[0].p.alg === 'ECDH-SS-512')) { - const recipient = crypto.createECDH(nodeCRV[recipients[0].key.crv]); - const generated = crypto.createECDH(nodeCRV[recipients[0].key.crv]); - recipient.setPrivateKey(recipients[0].key.d); - let pk = randomSource(keyLength[recipients[0].key.crv]); - if (recipients[0].p.alg === 'ECDH-ES' || - recipients[0].p.alg === 'ECDH-ES-512') { - pk = randomSource(keyLength[recipients[0].key.crv]); - pk[0] = (recipients[0].key.crv !== 'P-521' || pk[0] === 1) ? pk[0] : 0; - } else { - pk = recipients[0].sender.d; - } - - generated.setPrivateKey(pk); - const senderPublicKey = generated.getPublicKey(); - const recipientPublicKey = Buffer.concat([ - Buffer.from('04', 'hex'), - recipients[0].key.x, - recipients[0].key.y - ]); - - const generatedKey = common.TranslateKey({ - crv: recipients[0].key.crv, - x: senderPublicKey.slice(1, keyLength[recipients[0].key.crv] + 1), // TODO slice based on key length - y: senderPublicKey.slice(keyLength[recipients[0].key.crv] + 1), - kty: 'EC2' // TODO use real value - }); - const rp = cbor.encode(common.TranslateHeaders(recipients[0].p)); - const ikm = generated.computeSecret(recipientPublicKey); - let partyUNonce = null; - if (recipients[0].p.alg === 'ECDH-SS' || recipients[0].p.alg === 'ECDH-SS-512') { - partyUNonce = randomSource(64); // TODO use real value - } - const context = createContext(rp, alg, partyUNonce); - const nrBytes = keyLength[alg]; - const hkdf = new HKDF(HKDFAlg[recipients[0].p.alg], undefined, ikm); - key = hkdf.derive(context, nrBytes); - let ru = recipients[0].u; - - if (recipients[0].p.alg === 'ECDH-ES' || - recipients[0].p.alg === 'ECDH-ES-512') { - ru.ephemeral_key = generatedKey; - } else { - ru.static_key = generatedKey; - } - - ru.partyUNonce = partyUNonce; - ru = common.TranslateHeaders(ru); - - recipientStruct = [[rp, ru, EMPTY_BUFFER]]; - } else { - key = recipients[0].key; - const ru = common.TranslateHeaders(recipients[0].u); - recipientStruct = [[EMPTY_BUFFER, ru, EMPTY_BUFFER]]; - } - - let ciphertext; - if (isNodeAlg[alg]) { - ciphertext = nodeEncrypt(payload, key, alg, iv, aad); - } else if (isCCMAlg[alg] && runningInNode()) { - ciphertext = nodeEncrypt(payload, key, alg, iv, aad, true); - } else { - throw new Error('No implementation for algorithm, ' + alg); - } - - if (p.size === 0 && options.encodep === 'empty') { - p = EMPTY_BUFFER; - } else { - p = cbor.encode(p); - } - - const encrypted = [p, u, ciphertext, recipientStruct]; - resolve(cbor.encode(options.excludetag ? encrypted : new Tagged(EncryptTag, encrypted))); - } else { - let iv; - if (options.contextIv) { - const partialIv = randomSource(2); - iv = common.xor(partialIv, options.contextIv); - u.set(common.HeaderParameters.Partial_IV, partialIv); - } else { - iv = randomSource(ivLenght[alg]); - u.set(common.HeaderParameters.IV, iv); - } - - const key = recipients.key; - - const aad = createAAD(p, 'Encrypt0', externalAAD); - let ciphertext; - if (isNodeAlg[alg]) { - ciphertext = nodeEncrypt(payload, key, alg, iv, aad); - } else if (isCCMAlg[alg] && runningInNode()) { - ciphertext = nodeEncrypt(payload, key, alg, iv, aad, true); - } else { - throw new Error('No implementation for algorithm, ' + alg); - } - - if (p.size === 0 && options.encodep === 'empty') { - p = EMPTY_BUFFER; - } else { - p = cbor.encode(p); - } - const encrypted = [p, u, ciphertext]; - resolve(cbor.encode(options.excludetag ? encrypted : new Tagged(Encrypt0Tag, encrypted))); - } - }); -}; - -function nodeDecrypt (ciphertext, key, alg, iv, tag, aad, ccm = false) { - const nodeAlg = COSEAlgToNodeAlg[TagToAlg[alg]]; - const chiperOptions = ccm ? { authTagLength: authTagLength[alg] } : null; - const aadOptions = ccm ? { plaintextLength: Buffer.byteLength(ciphertext) } : null; - const decipher = crypto.createDecipheriv(nodeAlg, key, iv, chiperOptions); - decipher.setAuthTag(tag); - decipher.setAAD(aad, aadOptions); - return Buffer.concat([decipher.update(ciphertext), decipher.final()]); -} - -exports.read = async function (data, key, options) { - options = options || {}; - const externalAAD = options.externalAAD || EMPTY_BUFFER; - let obj = await cbor.decodeFirst(data); - let msgTag = options.defaultType ? options.defaultType : EncryptTag; - if (obj instanceof Tagged) { - if (obj.tag !== EncryptTag && obj.tag !== Encrypt0Tag) { - throw new Error('Unknown tag, ' + obj.tag); - } - msgTag = obj.tag; - obj = obj.value; - } - - if (!Array.isArray(obj)) { - throw new Error('Expecting Array'); - } - - if (msgTag === EncryptTag && obj.length !== 4) { - throw new Error('Expecting Array of lenght 4 for COSE Encrypt message'); - } - - if (msgTag === Encrypt0Tag && obj.length !== 3) { - throw new Error('Expecting Array of lenght 4 for COSE Encrypt0 message'); - } - - let [p, u, ciphertext] = obj; - - p = (p.length === 0) ? EMPTY_BUFFER : cbor.decodeFirstSync(p); - p = (!p.size) ? EMPTY_BUFFER : p; - u = (!u.size) ? EMPTY_BUFFER : u; - - const alg = (p !== EMPTY_BUFFER) ? p.get(common.HeaderParameters.alg) : (u !== EMPTY_BUFFER) ? u.get(common.HeaderParameters.alg) : undefined; - if (!TagToAlg[alg]) { - throw new Error('Unknown or unsupported algorithm ' + alg); - } - - let iv = u.get(common.HeaderParameters.IV); - const partialIv = u.get(common.HeaderParameters.Partial_IV); - if (iv && partialIv) { - throw new Error('IV and Partial IV parameters MUST NOT both be present in the same security layer'); - } - if (partialIv && !options.contextIv) { - throw new Error('Context IV must be provided when Partial IV is used'); - } - if (partialIv && options.contextIv) { - iv = common.xor(partialIv, options.contextIv); - } - - const tagLength = authTagLength[alg]; - const tag = ciphertext.slice(ciphertext.length - tagLength, ciphertext.length); - ciphertext = ciphertext.slice(0, ciphertext.length - tagLength); - - const aad = createAAD(p, (msgTag === EncryptTag ? 'Encrypt' : 'Encrypt0'), externalAAD); - if (isNodeAlg[alg]) { - return nodeDecrypt(ciphertext, key, alg, iv, tag, aad); - } else if (isCCMAlg[alg] && runningInNode()) { - return nodeDecrypt(ciphertext, key, alg, iv, tag, aad, true); - } else { - throw new Error('No implementation for algorithm, ' + alg); - } -}; - -}).call(this)}).call(this,require("buffer").Buffer) -},{"./common":80,"any-promise":3,"buffer":63,"cbor":66,"crypto":91,"node-hkdf-sync":162}],82:[function(require,module,exports){ -/* jshint esversion: 6 */ -/* jslint node: true */ -'use strict'; - -exports.common = require('./common'); -exports.mac = require('./mac'); -exports.sign = require('./sign'); -exports.encrypt = require('./encrypt'); - -},{"./common":80,"./encrypt":81,"./mac":83,"./sign":84}],83:[function(require,module,exports){ -/* jshint esversion: 6 */ -/* jslint node: true */ -'use strict'; - -const cbor = require('cbor'); -const aesCbcMac = require('aes-cbc-mac'); -const crypto = require('crypto'); -const Promise = require('any-promise'); -const common = require('./common'); -const Tagged = cbor.Tagged; -const EMPTY_BUFFER = common.EMPTY_BUFFER; - -const MAC0Tag = exports.MAC0Tag = 17; -const MACTag = exports.MACTag = 97; - -const AlgFromTags = { - 4: 'SHA-256_64', - 5: 'SHA-256', - 6: 'SHA-384', - 7: 'SHA-512', - 14: 'AES-MAC-128/64', - 15: 'AES-MAC-256/64', - 25: 'AES-MAC-128/128', - 26: 'AES-MAC-256/128' -}; - -const COSEAlgToNodeAlg = { - 'SHA-256_64': 'sha256', - 'SHA-256': 'sha256', - HS256: 'sha256', - 'SHA-384': 'sha384', - 'SHA-512': 'sha512', - 'AES-MAC-128/64': 'aes-cbc-mac-64', - 'AES-MAC-128/128': 'aes-cbc-mac-128', - 'AES-MAC-256/64': 'aes-cbc-mac-64', - 'AES-MAC-256/128': 'aes-cbc-mac-128' -}; - -const CutTo = { - 4: 8, - 5: 32, - 6: 48, - 7: 64 -}; - -const context = {}; -context[MAC0Tag] = 'MAC0'; -context[MACTag] = 'MAC'; - -function doMac (context, p, externalAAD, payload, alg, key) { - return new Promise((resolve, reject) => { - const MACstructure = [ - context, // 'MAC0' or 'MAC1', // context - p, // protected - externalAAD, // bstr, - payload // bstr - ]; - - const toBeMACed = cbor.encode(MACstructure); - if (alg === 'aes-cbc-mac-64') { - const mac = aesCbcMac.create(key, toBeMACed, 8); - resolve(mac); - } else if (alg === 'aes-cbc-mac-128') { - const mac = aesCbcMac.create(key, toBeMACed, 16); - resolve(mac); - } else { - const hmac = crypto.createHmac(alg, key); - hmac.end(toBeMACed, function () { - resolve(hmac.read()); - }); - } - }); -} - -exports.create = async function (headers, payload, recipents, externalAAD, options) { - options = options || {}; - externalAAD = externalAAD || EMPTY_BUFFER; - let u = headers.u || {}; - let p = headers.p || {}; - - p = common.TranslateHeaders(p); - u = common.TranslateHeaders(u); - - const alg = p.get(common.HeaderParameters.alg) || u.get(common.HeaderParameters.alg); - - if (!alg) { - throw new Error('Missing mandatory parameter \'alg\''); - } - - if (recipents.length === 0) { - throw new Error('There has to be at least one recipent'); - } - - const predictableP = (!p.size) ? EMPTY_BUFFER : cbor.encode(p); - if (p.size === 0 && options.encodep === 'empty') { - p = EMPTY_BUFFER; - } else { - p = cbor.encode(p); - } - // TODO check crit headers - if (Array.isArray(recipents)) { - if (recipents.length > 1) { - throw new Error('MACing with multiple recipents is not implemented'); - } - const recipent = recipents[0]; - let tag = await doMac('MAC', predictableP, externalAAD, payload, COSEAlgToNodeAlg[AlgFromTags[alg]], recipent.key); - tag = tag.slice(0, CutTo[alg]); - const ru = common.TranslateHeaders(recipent.u); - const rp = EMPTY_BUFFER; - const maced = [p, u, payload, tag, [[rp, ru, EMPTY_BUFFER]]]; - return cbor.encode(options.excludetag ? maced : new Tagged(MACTag, maced)); - } else { - let tag = await doMac('MAC0', predictableP, externalAAD, payload, COSEAlgToNodeAlg[AlgFromTags[alg]], recipents.key); - tag = tag.slice(0, CutTo[alg]); - const maced = [p, u, payload, tag]; - return cbor.encode(options.excludetag ? maced : new Tagged(MAC0Tag, maced)); - } -}; - -exports.read = async function (data, key, externalAAD, options) { - options = options || {}; - externalAAD = externalAAD || EMPTY_BUFFER; - - let obj = await cbor.decodeFirst(data); - - let type = options.defaultType ? options.defaultType : MAC0Tag; - if (obj instanceof Tagged) { - if (obj.tag !== MAC0Tag && obj.tag !== MACTag) { - throw new Error('Unexpected cbor tag, \'' + obj.tag + '\''); - } - type = obj.tag; - obj = obj.value; - } - - if (!Array.isArray(obj)) { - throw new Error('Expecting Array'); - } - - if (type === MAC0Tag && obj.length !== 4) { - throw new Error('Expecting Array of lenght 4'); - } - if (type === MACTag && obj.length !== 5) { - throw new Error('Expecting Array of lenght 5'); - } - - let [p, u, payload, tag] = obj; - p = (!p.length) ? EMPTY_BUFFER : cbor.decode(p); - p = (!p.size) ? EMPTY_BUFFER : p; - u = (!u.size) ? EMPTY_BUFFER : u; - - // TODO validate protected header - const alg = (p !== EMPTY_BUFFER) ? p.get(common.HeaderParameters.alg) : (u !== EMPTY_BUFFER) ? u.get(common.HeaderParameters.alg) : undefined; - p = (!p.size) ? EMPTY_BUFFER : cbor.encode(p); - if (!AlgFromTags[alg]) { - throw new Error('Unknown algorithm, ' + alg); - } - if (!COSEAlgToNodeAlg[AlgFromTags[alg]]) { - throw new Error('Unsupported algorithm, ' + AlgFromTags[alg]); - } - - let calcTag = await doMac(context[type], p, externalAAD, payload, COSEAlgToNodeAlg[AlgFromTags[alg]], key); - calcTag = calcTag.slice(0, CutTo[alg]); - if (tag.toString('hex') !== calcTag.toString('hex')) { - throw new Error('Tag mismatch'); - } - return payload; -}; - -},{"./common":80,"aes-cbc-mac":2,"any-promise":3,"cbor":66,"crypto":91}],84:[function(require,module,exports){ -(function (Buffer){(function (){ -/* jshint esversion: 6 */ -/* jslint node: true */ -'use strict'; - -const cbor = require('cbor'); -const EC = require('elliptic').ec; -const crypto = require('crypto'); -const NodeRSA = require('node-rsa'); -const common = require('./common'); -const EMPTY_BUFFER = common.EMPTY_BUFFER; -const Tagged = cbor.Tagged; - -const SignTag = exports.SignTag = 98; -const Sign1Tag = exports.Sign1Tag = 18; - -const AlgFromTags = {}; -AlgFromTags[-7] = { sign: 'ES256', digest: 'SHA-256' }; -AlgFromTags[-35] = { sign: 'ES384', digest: 'SHA-384' }; -AlgFromTags[-36] = { sign: 'ES512', digest: 'SHA-512' }; -AlgFromTags[-37] = { sign: 'PS256', digest: 'SHA-256' }; -AlgFromTags[-38] = { sign: 'PS384', digest: 'SHA-384' }; -AlgFromTags[-39] = { sign: 'PS512', digest: 'SHA-512' }; -AlgFromTags[-257] = { sign: 'RS256', digest: 'SHA-256' }; -AlgFromTags[-258] = { sign: 'RS384', digest: 'SHA-384' }; -AlgFromTags[-259] = { sign: 'RS512', digest: 'SHA-512' }; - -const COSEAlgToNodeAlg = { - ES256: { sign: 'p256', digest: 'sha256' }, - ES384: { sign: 'p384', digest: 'sha384' }, - ES512: { sign: 'p521', digest: 'sha512' }, - RS256: { sign: 'RSA-SHA256' }, - RS384: { sign: 'RSA-SHA384' }, - RS512: { sign: 'RSA-SHA512' }, - PS256: { alg: 'pss-sha256', saltLen: 32 }, - PS384: { alg: 'pss-sha384', saltLen: 48 }, - PS512: { alg: 'pss-sha512', saltLen: 64 } -}; - -function doSign (SigStructure, signer, alg) { - if (!AlgFromTags[alg]) { - throw new Error('Unknown algorithm, ' + alg); - } - if (!COSEAlgToNodeAlg[AlgFromTags[alg].sign]) { - throw new Error('Unsupported algorithm, ' + AlgFromTags[alg].sign); - } - - let ToBeSigned = cbor.encode(SigStructure); - - let sig; - if (AlgFromTags[alg].sign.startsWith('ES')) { - const hash = crypto.createHash(COSEAlgToNodeAlg[AlgFromTags[alg].sign].digest); - hash.update(ToBeSigned); - ToBeSigned = hash.digest(); - const ec = new EC(COSEAlgToNodeAlg[AlgFromTags[alg].sign].sign); - const key = ec.keyFromPrivate(signer.key.d); - const signature = key.sign(ToBeSigned); - const bitLength = Math.ceil(ec.curve._bitLength / 8); - sig = Buffer.concat([signature.r.toArrayLike(Buffer, undefined, bitLength), signature.s.toArrayLike(Buffer, undefined, bitLength)]); - } else if (AlgFromTags[alg].sign.startsWith('PS')) { - signer.key.dmp1 = signer.key.dp; - signer.key.dmq1 = signer.key.dq; - signer.key.coeff = signer.key.qi; - const key = new NodeRSA().importKey(signer.key, 'components-private'); - key.setOptions({ - signingScheme: { - scheme: COSEAlgToNodeAlg[AlgFromTags[alg].sign].alg.split('-')[0], - hash: COSEAlgToNodeAlg[AlgFromTags[alg].sign].alg.split('-')[1], - saltLength: COSEAlgToNodeAlg[AlgFromTags[alg].sign].saltLen - } - }); - sig = key.sign(ToBeSigned); - } else { - const sign = crypto.createSign(COSEAlgToNodeAlg[AlgFromTags[alg].sign].sign); - sign.update(ToBeSigned); - sign.end(); - sig = sign.sign(signer.key); - } - return sig; -} - -exports.create = function (headers, payload, signers, options) { - options = options || {}; - let u = headers.u || {}; - let p = headers.p || {}; - - p = common.TranslateHeaders(p); - u = common.TranslateHeaders(u); - let bodyP = p || {}; - bodyP = (bodyP.size === 0) ? EMPTY_BUFFER : cbor.encode(bodyP); - if (Array.isArray(signers)) { - if (signers.length === 0) { - throw new Error('There has to be at least one signer'); - } - if (signers.length > 1) { - throw new Error('Only one signer is supported'); - } - // TODO handle multiple signers - const signer = signers[0]; - const externalAAD = signer.externalAAD || EMPTY_BUFFER; - let signerP = signer.p || {}; - let signerU = signer.u || {}; - - signerP = common.TranslateHeaders(signerP); - signerU = common.TranslateHeaders(signerU); - const alg = signerP.get(common.HeaderParameters.alg); - signerP = (signerP.size === 0) ? EMPTY_BUFFER : cbor.encode(signerP); - - const SigStructure = [ - 'Signature', - bodyP, - signerP, - externalAAD, - payload - ]; - - const sig = doSign(SigStructure, signer, alg); - if (p.size === 0 && options.encodep === 'empty') { - p = EMPTY_BUFFER; - } else { - p = cbor.encode(p); - } - const signed = [p, u, payload, [[signerP, signerU, sig]]]; - return cbor.encodeAsync(options.excludetag ? signed : new Tagged(SignTag, signed)); - } else { - const signer = signers; - const externalAAD = signer.externalAAD || EMPTY_BUFFER; - const alg = p.get(common.HeaderParameters.alg) || u.get(common.HeaderParameters.alg); - const SigStructure = [ - 'Signature1', - bodyP, - externalAAD, - payload - ]; - const sig = doSign(SigStructure, signer, alg); - if (p.size === 0 && options.encodep === 'empty') { - p = EMPTY_BUFFER; - } else { - p = cbor.encode(p); - } - const signed = [p, u, payload, sig]; - return cbor.encodeAsync(options.excludetag ? signed : new Tagged(Sign1Tag, signed), { canonical: true }); - } -}; - -function doVerify (SigStructure, verifier, alg, sig) { - if (!AlgFromTags[alg]) { - throw new Error('Unknown algorithm, ' + alg); - } - const nodeAlg = COSEAlgToNodeAlg[AlgFromTags[alg].sign]; - if (!nodeAlg) { - throw new Error('Unsupported algorithm, ' + AlgFromTags[alg].sign); - } - const ToBeSigned = cbor.encode(SigStructure); - - if (AlgFromTags[alg].sign.startsWith('ES')) { - const hash = crypto.createHash(nodeAlg.digest); - hash.update(ToBeSigned); - const msgHash = hash.digest(); - - const pub = { x: verifier.key.x, y: verifier.key.y }; - const ec = new EC(nodeAlg.sign); - const key = ec.keyFromPublic(pub); - sig = { r: sig.slice(0, sig.length / 2), s: sig.slice(sig.length / 2) }; - if (!key.verify(msgHash, sig)) { - throw new Error('Signature missmatch'); - } - } else if (AlgFromTags[alg].sign.startsWith('PS')) { - const key = new NodeRSA().importKey(verifier.key, 'components-public'); - key.setOptions({ - signingScheme: { - scheme: COSEAlgToNodeAlg[AlgFromTags[alg].sign].alg.split('-')[0], - hash: COSEAlgToNodeAlg[AlgFromTags[alg].sign].alg.split('-')[1], - saltLength: COSEAlgToNodeAlg[AlgFromTags[alg].sign].saltLen - } - }); - if (!key.verify(ToBeSigned, sig, 'buffer', 'buffer')) { - throw new Error('Signature missmatch'); - } - } else { - const verify = crypto.createVerify(nodeAlg.sign); - verify.update(ToBeSigned); - if (!verify.verify(verifier.key, sig)) { - throw new Error('Signature missmatch'); - } - } -} - -function getSigner (signers, verifier) { - for (let i = 0; i < signers.length; i++) { - const kid = signers[i][1].get(common.HeaderParameters.kid); // TODO create constant for header locations - if (kid.equals(Buffer.from(verifier.key.kid, 'utf8'))) { - return signers[i]; - } - } -} - -function getCommonParameter (first, second, parameter) { - let result; - if (first.get) { - result = first.get(parameter); - } - if (!result && second.get) { - result = second.get(parameter); - } - return result; -} - -exports.verify = async function (payload, verifier, options) { - options = options || {}; - const obj = await cbor.decodeFirst(payload); - return verifyInternal(verifier, options, obj); -}; - -exports.verifySync = function (payload, verifier, options) { - options = options || {}; - const obj = cbor.decodeFirstSync(payload); - return verifyInternal(verifier, options, obj); -}; - -function verifyInternal (verifier, options, obj) { - options = options || {}; - let type = options.defaultType ? options.defaultType : SignTag; - if (obj instanceof Tagged) { - if (obj.tag !== SignTag && obj.tag !== Sign1Tag) { - throw new Error('Unexpected cbor tag, \'' + obj.tag + '\''); - } - type = obj.tag; - obj = obj.value; - } - - if (!Array.isArray(obj)) { - throw new Error('Expecting Array'); - } - - if (obj.length !== 4) { - throw new Error('Expecting Array of lenght 4'); - } - - let [p, u, plaintext, signers] = obj; - - if (type === SignTag && !Array.isArray(signers)) { - throw new Error('Expecting signature Array'); - } - - p = (!p.length) ? EMPTY_BUFFER : cbor.decodeFirstSync(p); - u = (!u.size) ? EMPTY_BUFFER : u; - - const signer = (type === SignTag ? getSigner(signers, verifier) : signers); - - if (!signer) { - throw new Error('Failed to find signer with kid' + verifier.key.kid); - } - - if (type === SignTag) { - const externalAAD = verifier.externalAAD || EMPTY_BUFFER; - let [signerP, , sig] = signer; - signerP = (!signerP.length) ? EMPTY_BUFFER : signerP; - p = (!p.size) ? EMPTY_BUFFER : cbor.encode(p); - const signerPMap = cbor.decode(signerP); - const alg = signerPMap.get(common.HeaderParameters.alg); - const SigStructure = [ - 'Signature', - p, - signerP, - externalAAD, - plaintext - ]; - doVerify(SigStructure, verifier, alg, sig); - return plaintext; - } else { - const externalAAD = verifier.externalAAD || EMPTY_BUFFER; - - const alg = getCommonParameter(p, u, common.HeaderParameters.alg); - p = (!p.size) ? EMPTY_BUFFER : cbor.encode(p); - const SigStructure = [ - 'Signature1', - p, - externalAAD, - plaintext - ]; - doVerify(SigStructure, verifier, alg, signer); - return plaintext; - } -} - -}).call(this)}).call(this,require("buffer").Buffer) -},{"./common":80,"buffer":63,"cbor":66,"crypto":91,"elliptic":104,"node-rsa":164}],85:[function(require,module,exports){ -(function (Buffer){(function (){ -var elliptic = require('elliptic') -var BN = require('bn.js') - -module.exports = function createECDH (curve) { - return new ECDH(curve) -} - -var aliases = { - secp256k1: { - name: 'secp256k1', - byteLength: 32 - }, - secp224r1: { - name: 'p224', - byteLength: 28 - }, - prime256v1: { - name: 'p256', - byteLength: 32 - }, - prime192v1: { - name: 'p192', - byteLength: 24 - }, - ed25519: { - name: 'ed25519', - byteLength: 32 - }, - secp384r1: { - name: 'p384', - byteLength: 48 - }, - secp521r1: { - name: 'p521', - byteLength: 66 - } -} - -aliases.p224 = aliases.secp224r1 -aliases.p256 = aliases.secp256r1 = aliases.prime256v1 -aliases.p192 = aliases.secp192r1 = aliases.prime192v1 -aliases.p384 = aliases.secp384r1 -aliases.p521 = aliases.secp521r1 - -function ECDH (curve) { - this.curveType = aliases[curve] - if (!this.curveType) { - this.curveType = { - name: curve - } - } - this.curve = new elliptic.ec(this.curveType.name) // eslint-disable-line new-cap - this.keys = void 0 -} - -ECDH.prototype.generateKeys = function (enc, format) { - this.keys = this.curve.genKeyPair() - return this.getPublicKey(enc, format) -} - -ECDH.prototype.computeSecret = function (other, inenc, enc) { - inenc = inenc || 'utf8' - if (!Buffer.isBuffer(other)) { - other = new Buffer(other, inenc) - } - var otherPub = this.curve.keyFromPublic(other).getPublic() - var out = otherPub.mul(this.keys.getPrivate()).getX() - return formatReturnValue(out, enc, this.curveType.byteLength) -} - -ECDH.prototype.getPublicKey = function (enc, format) { - var key = this.keys.getPublic(format === 'compressed', true) - if (format === 'hybrid') { - if (key[key.length - 1] % 2) { - key[0] = 7 - } else { - key[0] = 6 - } - } - return formatReturnValue(key, enc) -} - -ECDH.prototype.getPrivateKey = function (enc) { - return formatReturnValue(this.keys.getPrivate(), enc) -} - -ECDH.prototype.setPublicKey = function (pub, enc) { - enc = enc || 'utf8' - if (!Buffer.isBuffer(pub)) { - pub = new Buffer(pub, enc) - } - this.keys._importPublic(pub) - return this -} - -ECDH.prototype.setPrivateKey = function (priv, enc) { - enc = enc || 'utf8' - if (!Buffer.isBuffer(priv)) { - priv = new Buffer(priv, enc) - } - - var _priv = new BN(priv) - _priv = _priv.toString(16) - this.keys = this.curve.genKeyPair() - this.keys._importPrivate(_priv) - return this -} - -function formatReturnValue (bn, enc, len) { - if (!Array.isArray(bn)) { - bn = bn.toArray() - } - var buf = new Buffer(bn) - if (len && buf.length < len) { - var zeros = new Buffer(len - buf.length) - zeros.fill(0) - buf = Buffer.concat([zeros, buf]) - } - if (!enc) { - return buf - } else { - return buf.toString(enc) - } -} - -}).call(this)}).call(this,require("buffer").Buffer) -},{"bn.js":86,"buffer":63,"elliptic":104}],86:[function(require,module,exports){ -arguments[4][20][0].apply(exports,arguments) -},{"buffer":34,"dup":20}],87:[function(require,module,exports){ -'use strict' -var inherits = require('inherits') -var MD5 = require('md5.js') -var RIPEMD160 = require('ripemd160') -var sha = require('sha.js') -var Base = require('cipher-base') - -function Hash (hash) { - Base.call(this, 'digest') - - this._hash = hash -} - -inherits(Hash, Base) - -Hash.prototype._update = function (data) { - this._hash.update(data) -} - -Hash.prototype._final = function () { - return this._hash.digest() -} - -module.exports = function createHash (alg) { - alg = alg.toLowerCase() - if (alg === 'md5') return new MD5() - if (alg === 'rmd160' || alg === 'ripemd160') return new RIPEMD160() - - return new Hash(sha(alg)) -} - -},{"cipher-base":77,"inherits":155,"md5.js":157,"ripemd160":220,"sha.js":225}],88:[function(require,module,exports){ -var MD5 = require('md5.js') - -module.exports = function (buffer) { - return new MD5().update(buffer).digest() -} - -},{"md5.js":157}],89:[function(require,module,exports){ -'use strict' -var inherits = require('inherits') -var Legacy = require('./legacy') -var Base = require('cipher-base') -var Buffer = require('safe-buffer').Buffer -var md5 = require('create-hash/md5') -var RIPEMD160 = require('ripemd160') - -var sha = require('sha.js') - -var ZEROS = Buffer.alloc(128) - -function Hmac (alg, key) { - Base.call(this, 'digest') - if (typeof key === 'string') { - key = Buffer.from(key) - } - - var blocksize = (alg === 'sha512' || alg === 'sha384') ? 128 : 64 - - this._alg = alg - this._key = key - if (key.length > blocksize) { - var hash = alg === 'rmd160' ? new RIPEMD160() : sha(alg) - key = hash.update(key).digest() - } else if (key.length < blocksize) { - key = Buffer.concat([key, ZEROS], blocksize) - } - - var ipad = this._ipad = Buffer.allocUnsafe(blocksize) - var opad = this._opad = Buffer.allocUnsafe(blocksize) - - for (var i = 0; i < blocksize; i++) { - ipad[i] = key[i] ^ 0x36 - opad[i] = key[i] ^ 0x5C - } - this._hash = alg === 'rmd160' ? new RIPEMD160() : sha(alg) - this._hash.update(ipad) -} - -inherits(Hmac, Base) - -Hmac.prototype._update = function (data) { - this._hash.update(data) -} - -Hmac.prototype._final = function () { - var h = this._hash.digest() - var hash = this._alg === 'rmd160' ? new RIPEMD160() : sha(this._alg) - return hash.update(this._opad).update(h).digest() -} - -module.exports = function createHmac (alg, key) { - alg = alg.toLowerCase() - if (alg === 'rmd160' || alg === 'ripemd160') { - return new Hmac('rmd160', key) - } - if (alg === 'md5') { - return new Legacy(md5, key) - } - return new Hmac(alg, key) -} - -},{"./legacy":90,"cipher-base":77,"create-hash/md5":88,"inherits":155,"ripemd160":220,"safe-buffer":221,"sha.js":225}],90:[function(require,module,exports){ -'use strict' -var inherits = require('inherits') -var Buffer = require('safe-buffer').Buffer - -var Base = require('cipher-base') - -var ZEROS = Buffer.alloc(128) -var blocksize = 64 - -function Hmac (alg, key) { - Base.call(this, 'digest') - if (typeof key === 'string') { - key = Buffer.from(key) - } - - this._alg = alg - this._key = key - - if (key.length > blocksize) { - key = alg(key) - } else if (key.length < blocksize) { - key = Buffer.concat([key, ZEROS], blocksize) - } - - var ipad = this._ipad = Buffer.allocUnsafe(blocksize) - var opad = this._opad = Buffer.allocUnsafe(blocksize) - - for (var i = 0; i < blocksize; i++) { - ipad[i] = key[i] ^ 0x36 - opad[i] = key[i] ^ 0x5C - } - - this._hash = [ipad] -} - -inherits(Hmac, Base) - -Hmac.prototype._update = function (data) { - this._hash.push(data) -} - -Hmac.prototype._final = function () { - var h = this._alg(Buffer.concat(this._hash)) - return this._alg(Buffer.concat([this._opad, h])) -} -module.exports = Hmac - -},{"cipher-base":77,"inherits":155,"safe-buffer":221}],91:[function(require,module,exports){ -'use strict' - -exports.randomBytes = exports.rng = exports.pseudoRandomBytes = exports.prng = require('randombytes') -exports.createHash = exports.Hash = require('create-hash') -exports.createHmac = exports.Hmac = require('create-hmac') - -var algos = require('browserify-sign/algos') -var algoKeys = Object.keys(algos) -var hashes = ['sha1', 'sha224', 'sha256', 'sha384', 'sha512', 'md5', 'rmd160'].concat(algoKeys) -exports.getHashes = function () { - return hashes -} - -var p = require('pbkdf2') -exports.pbkdf2 = p.pbkdf2 -exports.pbkdf2Sync = p.pbkdf2Sync - -var aes = require('browserify-cipher') - -exports.Cipher = aes.Cipher -exports.createCipher = aes.createCipher -exports.Cipheriv = aes.Cipheriv -exports.createCipheriv = aes.createCipheriv -exports.Decipher = aes.Decipher -exports.createDecipher = aes.createDecipher -exports.Decipheriv = aes.Decipheriv -exports.createDecipheriv = aes.createDecipheriv -exports.getCiphers = aes.getCiphers -exports.listCiphers = aes.listCiphers - -var dh = require('diffie-hellman') - -exports.DiffieHellmanGroup = dh.DiffieHellmanGroup -exports.createDiffieHellmanGroup = dh.createDiffieHellmanGroup -exports.getDiffieHellman = dh.getDiffieHellman -exports.createDiffieHellman = dh.createDiffieHellman -exports.DiffieHellman = dh.DiffieHellman - -var sign = require('browserify-sign') - -exports.createSign = sign.createSign -exports.Sign = sign.Sign -exports.createVerify = sign.createVerify -exports.Verify = sign.Verify - -exports.createECDH = require('create-ecdh') - -var publicEncrypt = require('public-encrypt') - -exports.publicEncrypt = publicEncrypt.publicEncrypt -exports.privateEncrypt = publicEncrypt.privateEncrypt -exports.publicDecrypt = publicEncrypt.publicDecrypt -exports.privateDecrypt = publicEncrypt.privateDecrypt - -// the least I can do is make error messages for the rest of the node.js/crypto api. -// ;[ -// 'createCredentials' -// ].forEach(function (name) { -// exports[name] = function () { -// throw new Error([ -// 'sorry, ' + name + ' is not implemented yet', -// 'we accept pull requests', -// 'https://github.com/crypto-browserify/crypto-browserify' -// ].join('\n')) -// } -// }) - -var rf = require('randomfill') - -exports.randomFill = rf.randomFill -exports.randomFillSync = rf.randomFillSync - -exports.createCredentials = function () { - throw new Error([ - 'sorry, createCredentials is not implemented yet', - 'we accept pull requests', - 'https://github.com/crypto-browserify/crypto-browserify' - ].join('\n')) -} - -exports.constants = { - 'DH_CHECK_P_NOT_SAFE_PRIME': 2, - 'DH_CHECK_P_NOT_PRIME': 1, - 'DH_UNABLE_TO_CHECK_GENERATOR': 4, - 'DH_NOT_SUITABLE_GENERATOR': 8, - 'NPN_ENABLED': 1, - 'ALPN_ENABLED': 1, - 'RSA_PKCS1_PADDING': 1, - 'RSA_SSLV23_PADDING': 2, - 'RSA_NO_PADDING': 3, - 'RSA_PKCS1_OAEP_PADDING': 4, - 'RSA_X931_PADDING': 5, - 'RSA_PKCS1_PSS_PADDING': 6, - 'POINT_CONVERSION_COMPRESSED': 2, - 'POINT_CONVERSION_UNCOMPRESSED': 4, - 'POINT_CONVERSION_HYBRID': 6 -} - -},{"browserify-cipher":52,"browserify-sign":59,"browserify-sign/algos":56,"create-ecdh":85,"create-hash":87,"create-hmac":89,"diffie-hellman":99,"pbkdf2":192,"public-encrypt":200,"randombytes":207,"randomfill":208}],92:[function(require,module,exports){ -'use strict'; - -var $defineProperty = require('es-define-property'); - -var $SyntaxError = require('es-errors/syntax'); -var $TypeError = require('es-errors/type'); - -var gopd = require('gopd'); - -/** @type {import('.')} */ -module.exports = function defineDataProperty( - obj, - property, - value -) { - if (!obj || (typeof obj !== 'object' && typeof obj !== 'function')) { - throw new $TypeError('`obj` must be an object or a function`'); - } - if (typeof property !== 'string' && typeof property !== 'symbol') { - throw new $TypeError('`property` must be a string or a symbol`'); - } - if (arguments.length > 3 && typeof arguments[3] !== 'boolean' && arguments[3] !== null) { - throw new $TypeError('`nonEnumerable`, if provided, must be a boolean or null'); - } - if (arguments.length > 4 && typeof arguments[4] !== 'boolean' && arguments[4] !== null) { - throw new $TypeError('`nonWritable`, if provided, must be a boolean or null'); - } - if (arguments.length > 5 && typeof arguments[5] !== 'boolean' && arguments[5] !== null) { - throw new $TypeError('`nonConfigurable`, if provided, must be a boolean or null'); - } - if (arguments.length > 6 && typeof arguments[6] !== 'boolean') { - throw new $TypeError('`loose`, if provided, must be a boolean'); - } - - var nonEnumerable = arguments.length > 3 ? arguments[3] : null; - var nonWritable = arguments.length > 4 ? arguments[4] : null; - var nonConfigurable = arguments.length > 5 ? arguments[5] : null; - var loose = arguments.length > 6 ? arguments[6] : false; - - /* @type {false | TypedPropertyDescriptor} */ - var desc = !!gopd && gopd(obj, property); - - if ($defineProperty) { - $defineProperty(obj, property, { - configurable: nonConfigurable === null && desc ? desc.configurable : !nonConfigurable, - enumerable: nonEnumerable === null && desc ? desc.enumerable : !nonEnumerable, - value: value, - writable: nonWritable === null && desc ? desc.writable : !nonWritable - }); - } else if (loose || (!nonEnumerable && !nonWritable && !nonConfigurable)) { - // must fall back to [[Set]], and was not explicitly asked to make non-enumerable, non-writable, or non-configurable - obj[property] = value; // eslint-disable-line no-param-reassign - } else { - throw new $SyntaxError('This environment does not support defining a property as non-configurable, non-writable, or non-enumerable.'); - } -}; - -},{"es-define-property":121,"es-errors/syntax":126,"es-errors/type":127,"gopd":134}],93:[function(require,module,exports){ -'use strict'; - -exports.utils = require('./des/utils'); -exports.Cipher = require('./des/cipher'); -exports.DES = require('./des/des'); -exports.CBC = require('./des/cbc'); -exports.EDE = require('./des/ede'); - -},{"./des/cbc":94,"./des/cipher":95,"./des/des":96,"./des/ede":97,"./des/utils":98}],94:[function(require,module,exports){ -'use strict'; - -var assert = require('minimalistic-assert'); -var inherits = require('inherits'); - -var proto = {}; - -function CBCState(iv) { - assert.equal(iv.length, 8, 'Invalid IV length'); - - this.iv = new Array(8); - for (var i = 0; i < this.iv.length; i++) - this.iv[i] = iv[i]; -} - -function instantiate(Base) { - function CBC(options) { - Base.call(this, options); - this._cbcInit(); - } - inherits(CBC, Base); - - var keys = Object.keys(proto); - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - CBC.prototype[key] = proto[key]; - } - - CBC.create = function create(options) { - return new CBC(options); - }; - - return CBC; -} - -exports.instantiate = instantiate; - -proto._cbcInit = function _cbcInit() { - var state = new CBCState(this.options.iv); - this._cbcState = state; -}; - -proto._update = function _update(inp, inOff, out, outOff) { - var state = this._cbcState; - var superProto = this.constructor.super_.prototype; - - var iv = state.iv; - if (this.type === 'encrypt') { - for (var i = 0; i < this.blockSize; i++) - iv[i] ^= inp[inOff + i]; - - superProto._update.call(this, iv, 0, out, outOff); - - for (var i = 0; i < this.blockSize; i++) - iv[i] = out[outOff + i]; - } else { - superProto._update.call(this, inp, inOff, out, outOff); - - for (var i = 0; i < this.blockSize; i++) - out[outOff + i] ^= iv[i]; - - for (var i = 0; i < this.blockSize; i++) - iv[i] = inp[inOff + i]; - } -}; - -},{"inherits":155,"minimalistic-assert":160}],95:[function(require,module,exports){ -'use strict'; - -var assert = require('minimalistic-assert'); - -function Cipher(options) { - this.options = options; - - this.type = this.options.type; - this.blockSize = 8; - this._init(); - - this.buffer = new Array(this.blockSize); - this.bufferOff = 0; - this.padding = options.padding !== false -} -module.exports = Cipher; - -Cipher.prototype._init = function _init() { - // Might be overrided -}; - -Cipher.prototype.update = function update(data) { - if (data.length === 0) - return []; - - if (this.type === 'decrypt') - return this._updateDecrypt(data); - else - return this._updateEncrypt(data); -}; - -Cipher.prototype._buffer = function _buffer(data, off) { - // Append data to buffer - var min = Math.min(this.buffer.length - this.bufferOff, data.length - off); - for (var i = 0; i < min; i++) - this.buffer[this.bufferOff + i] = data[off + i]; - this.bufferOff += min; - - // Shift next - return min; -}; - -Cipher.prototype._flushBuffer = function _flushBuffer(out, off) { - this._update(this.buffer, 0, out, off); - this.bufferOff = 0; - return this.blockSize; -}; - -Cipher.prototype._updateEncrypt = function _updateEncrypt(data) { - var inputOff = 0; - var outputOff = 0; - - var count = ((this.bufferOff + data.length) / this.blockSize) | 0; - var out = new Array(count * this.blockSize); - - if (this.bufferOff !== 0) { - inputOff += this._buffer(data, inputOff); - - if (this.bufferOff === this.buffer.length) - outputOff += this._flushBuffer(out, outputOff); - } - - // Write blocks - var max = data.length - ((data.length - inputOff) % this.blockSize); - for (; inputOff < max; inputOff += this.blockSize) { - this._update(data, inputOff, out, outputOff); - outputOff += this.blockSize; - } - - // Queue rest - for (; inputOff < data.length; inputOff++, this.bufferOff++) - this.buffer[this.bufferOff] = data[inputOff]; - - return out; -}; - -Cipher.prototype._updateDecrypt = function _updateDecrypt(data) { - var inputOff = 0; - var outputOff = 0; - - var count = Math.ceil((this.bufferOff + data.length) / this.blockSize) - 1; - var out = new Array(count * this.blockSize); - - // TODO(indutny): optimize it, this is far from optimal - for (; count > 0; count--) { - inputOff += this._buffer(data, inputOff); - outputOff += this._flushBuffer(out, outputOff); - } - - // Buffer rest of the input - inputOff += this._buffer(data, inputOff); - - return out; -}; - -Cipher.prototype.final = function final(buffer) { - var first; - if (buffer) - first = this.update(buffer); - - var last; - if (this.type === 'encrypt') - last = this._finalEncrypt(); - else - last = this._finalDecrypt(); - - if (first) - return first.concat(last); - else - return last; -}; - -Cipher.prototype._pad = function _pad(buffer, off) { - if (off === 0) - return false; - - while (off < buffer.length) - buffer[off++] = 0; - - return true; -}; - -Cipher.prototype._finalEncrypt = function _finalEncrypt() { - if (!this._pad(this.buffer, this.bufferOff)) - return []; - - var out = new Array(this.blockSize); - this._update(this.buffer, 0, out, 0); - return out; -}; - -Cipher.prototype._unpad = function _unpad(buffer) { - return buffer; -}; - -Cipher.prototype._finalDecrypt = function _finalDecrypt() { - assert.equal(this.bufferOff, this.blockSize, 'Not enough data to decrypt'); - var out = new Array(this.blockSize); - this._flushBuffer(out, 0); - - return this._unpad(out); -}; - -},{"minimalistic-assert":160}],96:[function(require,module,exports){ -'use strict'; - -var assert = require('minimalistic-assert'); -var inherits = require('inherits'); - -var utils = require('./utils'); -var Cipher = require('./cipher'); - -function DESState() { - this.tmp = new Array(2); - this.keys = null; -} - -function DES(options) { - Cipher.call(this, options); - - var state = new DESState(); - this._desState = state; - - this.deriveKeys(state, options.key); -} -inherits(DES, Cipher); -module.exports = DES; - -DES.create = function create(options) { - return new DES(options); -}; - -var shiftTable = [ - 1, 1, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 1 -]; - -DES.prototype.deriveKeys = function deriveKeys(state, key) { - state.keys = new Array(16 * 2); - - assert.equal(key.length, this.blockSize, 'Invalid key length'); - - var kL = utils.readUInt32BE(key, 0); - var kR = utils.readUInt32BE(key, 4); - - utils.pc1(kL, kR, state.tmp, 0); - kL = state.tmp[0]; - kR = state.tmp[1]; - for (var i = 0; i < state.keys.length; i += 2) { - var shift = shiftTable[i >>> 1]; - kL = utils.r28shl(kL, shift); - kR = utils.r28shl(kR, shift); - utils.pc2(kL, kR, state.keys, i); - } -}; - -DES.prototype._update = function _update(inp, inOff, out, outOff) { - var state = this._desState; - - var l = utils.readUInt32BE(inp, inOff); - var r = utils.readUInt32BE(inp, inOff + 4); - - // Initial Permutation - utils.ip(l, r, state.tmp, 0); - l = state.tmp[0]; - r = state.tmp[1]; - - if (this.type === 'encrypt') - this._encrypt(state, l, r, state.tmp, 0); - else - this._decrypt(state, l, r, state.tmp, 0); - - l = state.tmp[0]; - r = state.tmp[1]; - - utils.writeUInt32BE(out, l, outOff); - utils.writeUInt32BE(out, r, outOff + 4); -}; - -DES.prototype._pad = function _pad(buffer, off) { - if (this.padding === false) { - return false; - } - - var value = buffer.length - off; - for (var i = off; i < buffer.length; i++) - buffer[i] = value; - - return true; -}; - -DES.prototype._unpad = function _unpad(buffer) { - if (this.padding === false) { - return buffer; - } - - var pad = buffer[buffer.length - 1]; - for (var i = buffer.length - pad; i < buffer.length; i++) - assert.equal(buffer[i], pad); - - return buffer.slice(0, buffer.length - pad); -}; - -DES.prototype._encrypt = function _encrypt(state, lStart, rStart, out, off) { - var l = lStart; - var r = rStart; - - // Apply f() x16 times - for (var i = 0; i < state.keys.length; i += 2) { - var keyL = state.keys[i]; - var keyR = state.keys[i + 1]; - - // f(r, k) - utils.expand(r, state.tmp, 0); - - keyL ^= state.tmp[0]; - keyR ^= state.tmp[1]; - var s = utils.substitute(keyL, keyR); - var f = utils.permute(s); - - var t = r; - r = (l ^ f) >>> 0; - l = t; - } - - // Reverse Initial Permutation - utils.rip(r, l, out, off); -}; - -DES.prototype._decrypt = function _decrypt(state, lStart, rStart, out, off) { - var l = rStart; - var r = lStart; - - // Apply f() x16 times - for (var i = state.keys.length - 2; i >= 0; i -= 2) { - var keyL = state.keys[i]; - var keyR = state.keys[i + 1]; - - // f(r, k) - utils.expand(l, state.tmp, 0); - - keyL ^= state.tmp[0]; - keyR ^= state.tmp[1]; - var s = utils.substitute(keyL, keyR); - var f = utils.permute(s); - - var t = l; - l = (r ^ f) >>> 0; - r = t; - } - - // Reverse Initial Permutation - utils.rip(l, r, out, off); -}; - -},{"./cipher":95,"./utils":98,"inherits":155,"minimalistic-assert":160}],97:[function(require,module,exports){ -'use strict'; - -var assert = require('minimalistic-assert'); -var inherits = require('inherits'); - -var Cipher = require('./cipher'); -var DES = require('./des'); - -function EDEState(type, key) { - assert.equal(key.length, 24, 'Invalid key length'); - - var k1 = key.slice(0, 8); - var k2 = key.slice(8, 16); - var k3 = key.slice(16, 24); - - if (type === 'encrypt') { - this.ciphers = [ - DES.create({ type: 'encrypt', key: k1 }), - DES.create({ type: 'decrypt', key: k2 }), - DES.create({ type: 'encrypt', key: k3 }) - ]; - } else { - this.ciphers = [ - DES.create({ type: 'decrypt', key: k3 }), - DES.create({ type: 'encrypt', key: k2 }), - DES.create({ type: 'decrypt', key: k1 }) - ]; - } -} - -function EDE(options) { - Cipher.call(this, options); - - var state = new EDEState(this.type, this.options.key); - this._edeState = state; -} -inherits(EDE, Cipher); - -module.exports = EDE; - -EDE.create = function create(options) { - return new EDE(options); -}; - -EDE.prototype._update = function _update(inp, inOff, out, outOff) { - var state = this._edeState; - - state.ciphers[0]._update(inp, inOff, out, outOff); - state.ciphers[1]._update(out, outOff, out, outOff); - state.ciphers[2]._update(out, outOff, out, outOff); -}; - -EDE.prototype._pad = DES.prototype._pad; -EDE.prototype._unpad = DES.prototype._unpad; - -},{"./cipher":95,"./des":96,"inherits":155,"minimalistic-assert":160}],98:[function(require,module,exports){ -'use strict'; - -exports.readUInt32BE = function readUInt32BE(bytes, off) { - var res = (bytes[0 + off] << 24) | - (bytes[1 + off] << 16) | - (bytes[2 + off] << 8) | - bytes[3 + off]; - return res >>> 0; -}; - -exports.writeUInt32BE = function writeUInt32BE(bytes, value, off) { - bytes[0 + off] = value >>> 24; - bytes[1 + off] = (value >>> 16) & 0xff; - bytes[2 + off] = (value >>> 8) & 0xff; - bytes[3 + off] = value & 0xff; -}; - -exports.ip = function ip(inL, inR, out, off) { - var outL = 0; - var outR = 0; - - for (var i = 6; i >= 0; i -= 2) { - for (var j = 0; j <= 24; j += 8) { - outL <<= 1; - outL |= (inR >>> (j + i)) & 1; - } - for (var j = 0; j <= 24; j += 8) { - outL <<= 1; - outL |= (inL >>> (j + i)) & 1; - } - } - - for (var i = 6; i >= 0; i -= 2) { - for (var j = 1; j <= 25; j += 8) { - outR <<= 1; - outR |= (inR >>> (j + i)) & 1; - } - for (var j = 1; j <= 25; j += 8) { - outR <<= 1; - outR |= (inL >>> (j + i)) & 1; - } - } - - out[off + 0] = outL >>> 0; - out[off + 1] = outR >>> 0; -}; - -exports.rip = function rip(inL, inR, out, off) { - var outL = 0; - var outR = 0; - - for (var i = 0; i < 4; i++) { - for (var j = 24; j >= 0; j -= 8) { - outL <<= 1; - outL |= (inR >>> (j + i)) & 1; - outL <<= 1; - outL |= (inL >>> (j + i)) & 1; - } - } - for (var i = 4; i < 8; i++) { - for (var j = 24; j >= 0; j -= 8) { - outR <<= 1; - outR |= (inR >>> (j + i)) & 1; - outR <<= 1; - outR |= (inL >>> (j + i)) & 1; - } - } - - out[off + 0] = outL >>> 0; - out[off + 1] = outR >>> 0; -}; - -exports.pc1 = function pc1(inL, inR, out, off) { - var outL = 0; - var outR = 0; - - // 7, 15, 23, 31, 39, 47, 55, 63 - // 6, 14, 22, 30, 39, 47, 55, 63 - // 5, 13, 21, 29, 39, 47, 55, 63 - // 4, 12, 20, 28 - for (var i = 7; i >= 5; i--) { - for (var j = 0; j <= 24; j += 8) { - outL <<= 1; - outL |= (inR >> (j + i)) & 1; - } - for (var j = 0; j <= 24; j += 8) { - outL <<= 1; - outL |= (inL >> (j + i)) & 1; - } - } - for (var j = 0; j <= 24; j += 8) { - outL <<= 1; - outL |= (inR >> (j + i)) & 1; - } - - // 1, 9, 17, 25, 33, 41, 49, 57 - // 2, 10, 18, 26, 34, 42, 50, 58 - // 3, 11, 19, 27, 35, 43, 51, 59 - // 36, 44, 52, 60 - for (var i = 1; i <= 3; i++) { - for (var j = 0; j <= 24; j += 8) { - outR <<= 1; - outR |= (inR >> (j + i)) & 1; - } - for (var j = 0; j <= 24; j += 8) { - outR <<= 1; - outR |= (inL >> (j + i)) & 1; - } - } - for (var j = 0; j <= 24; j += 8) { - outR <<= 1; - outR |= (inL >> (j + i)) & 1; - } - - out[off + 0] = outL >>> 0; - out[off + 1] = outR >>> 0; -}; - -exports.r28shl = function r28shl(num, shift) { - return ((num << shift) & 0xfffffff) | (num >>> (28 - shift)); -}; - -var pc2table = [ - // inL => outL - 14, 11, 17, 4, 27, 23, 25, 0, - 13, 22, 7, 18, 5, 9, 16, 24, - 2, 20, 12, 21, 1, 8, 15, 26, - - // inR => outR - 15, 4, 25, 19, 9, 1, 26, 16, - 5, 11, 23, 8, 12, 7, 17, 0, - 22, 3, 10, 14, 6, 20, 27, 24 -]; - -exports.pc2 = function pc2(inL, inR, out, off) { - var outL = 0; - var outR = 0; - - var len = pc2table.length >>> 1; - for (var i = 0; i < len; i++) { - outL <<= 1; - outL |= (inL >>> pc2table[i]) & 0x1; - } - for (var i = len; i < pc2table.length; i++) { - outR <<= 1; - outR |= (inR >>> pc2table[i]) & 0x1; - } - - out[off + 0] = outL >>> 0; - out[off + 1] = outR >>> 0; -}; - -exports.expand = function expand(r, out, off) { - var outL = 0; - var outR = 0; - - outL = ((r & 1) << 5) | (r >>> 27); - for (var i = 23; i >= 15; i -= 4) { - outL <<= 6; - outL |= (r >>> i) & 0x3f; - } - for (var i = 11; i >= 3; i -= 4) { - outR |= (r >>> i) & 0x3f; - outR <<= 6; - } - outR |= ((r & 0x1f) << 1) | (r >>> 31); - - out[off + 0] = outL >>> 0; - out[off + 1] = outR >>> 0; -}; - -var sTable = [ - 14, 0, 4, 15, 13, 7, 1, 4, 2, 14, 15, 2, 11, 13, 8, 1, - 3, 10, 10, 6, 6, 12, 12, 11, 5, 9, 9, 5, 0, 3, 7, 8, - 4, 15, 1, 12, 14, 8, 8, 2, 13, 4, 6, 9, 2, 1, 11, 7, - 15, 5, 12, 11, 9, 3, 7, 14, 3, 10, 10, 0, 5, 6, 0, 13, - - 15, 3, 1, 13, 8, 4, 14, 7, 6, 15, 11, 2, 3, 8, 4, 14, - 9, 12, 7, 0, 2, 1, 13, 10, 12, 6, 0, 9, 5, 11, 10, 5, - 0, 13, 14, 8, 7, 10, 11, 1, 10, 3, 4, 15, 13, 4, 1, 2, - 5, 11, 8, 6, 12, 7, 6, 12, 9, 0, 3, 5, 2, 14, 15, 9, - - 10, 13, 0, 7, 9, 0, 14, 9, 6, 3, 3, 4, 15, 6, 5, 10, - 1, 2, 13, 8, 12, 5, 7, 14, 11, 12, 4, 11, 2, 15, 8, 1, - 13, 1, 6, 10, 4, 13, 9, 0, 8, 6, 15, 9, 3, 8, 0, 7, - 11, 4, 1, 15, 2, 14, 12, 3, 5, 11, 10, 5, 14, 2, 7, 12, - - 7, 13, 13, 8, 14, 11, 3, 5, 0, 6, 6, 15, 9, 0, 10, 3, - 1, 4, 2, 7, 8, 2, 5, 12, 11, 1, 12, 10, 4, 14, 15, 9, - 10, 3, 6, 15, 9, 0, 0, 6, 12, 10, 11, 1, 7, 13, 13, 8, - 15, 9, 1, 4, 3, 5, 14, 11, 5, 12, 2, 7, 8, 2, 4, 14, - - 2, 14, 12, 11, 4, 2, 1, 12, 7, 4, 10, 7, 11, 13, 6, 1, - 8, 5, 5, 0, 3, 15, 15, 10, 13, 3, 0, 9, 14, 8, 9, 6, - 4, 11, 2, 8, 1, 12, 11, 7, 10, 1, 13, 14, 7, 2, 8, 13, - 15, 6, 9, 15, 12, 0, 5, 9, 6, 10, 3, 4, 0, 5, 14, 3, - - 12, 10, 1, 15, 10, 4, 15, 2, 9, 7, 2, 12, 6, 9, 8, 5, - 0, 6, 13, 1, 3, 13, 4, 14, 14, 0, 7, 11, 5, 3, 11, 8, - 9, 4, 14, 3, 15, 2, 5, 12, 2, 9, 8, 5, 12, 15, 3, 10, - 7, 11, 0, 14, 4, 1, 10, 7, 1, 6, 13, 0, 11, 8, 6, 13, - - 4, 13, 11, 0, 2, 11, 14, 7, 15, 4, 0, 9, 8, 1, 13, 10, - 3, 14, 12, 3, 9, 5, 7, 12, 5, 2, 10, 15, 6, 8, 1, 6, - 1, 6, 4, 11, 11, 13, 13, 8, 12, 1, 3, 4, 7, 10, 14, 7, - 10, 9, 15, 5, 6, 0, 8, 15, 0, 14, 5, 2, 9, 3, 2, 12, - - 13, 1, 2, 15, 8, 13, 4, 8, 6, 10, 15, 3, 11, 7, 1, 4, - 10, 12, 9, 5, 3, 6, 14, 11, 5, 0, 0, 14, 12, 9, 7, 2, - 7, 2, 11, 1, 4, 14, 1, 7, 9, 4, 12, 10, 14, 8, 2, 13, - 0, 15, 6, 12, 10, 9, 13, 0, 15, 3, 3, 5, 5, 6, 8, 11 -]; - -exports.substitute = function substitute(inL, inR) { - var out = 0; - for (var i = 0; i < 4; i++) { - var b = (inL >>> (18 - i * 6)) & 0x3f; - var sb = sTable[i * 0x40 + b]; - - out <<= 4; - out |= sb; - } - for (var i = 0; i < 4; i++) { - var b = (inR >>> (18 - i * 6)) & 0x3f; - var sb = sTable[4 * 0x40 + i * 0x40 + b]; - - out <<= 4; - out |= sb; - } - return out >>> 0; -}; - -var permuteTable = [ - 16, 25, 12, 11, 3, 20, 4, 15, 31, 17, 9, 6, 27, 14, 1, 22, - 30, 24, 8, 18, 0, 5, 29, 23, 13, 19, 2, 26, 10, 21, 28, 7 -]; - -exports.permute = function permute(num) { - var out = 0; - for (var i = 0; i < permuteTable.length; i++) { - out <<= 1; - out |= (num >>> permuteTable[i]) & 0x1; - } - return out >>> 0; -}; - -exports.padSplit = function padSplit(num, size, group) { - var str = num.toString(2); - while (str.length < size) - str = '0' + str; - - var out = []; - for (var i = 0; i < size; i += group) - out.push(str.slice(i, i + group)); - return out.join(' '); -}; - -},{}],99:[function(require,module,exports){ -(function (Buffer){(function (){ -var generatePrime = require('./lib/generatePrime') -var primes = require('./lib/primes.json') - -var DH = require('./lib/dh') - -function getDiffieHellman (mod) { - var prime = new Buffer(primes[mod].prime, 'hex') - var gen = new Buffer(primes[mod].gen, 'hex') - - return new DH(prime, gen) -} - -var ENCODINGS = { - 'binary': true, 'hex': true, 'base64': true -} - -function createDiffieHellman (prime, enc, generator, genc) { - if (Buffer.isBuffer(enc) || ENCODINGS[enc] === undefined) { - return createDiffieHellman(prime, 'binary', enc, generator) - } - - enc = enc || 'binary' - genc = genc || 'binary' - generator = generator || new Buffer([2]) - - if (!Buffer.isBuffer(generator)) { - generator = new Buffer(generator, genc) - } - - if (typeof prime === 'number') { - return new DH(generatePrime(prime, generator), generator, true) - } - - if (!Buffer.isBuffer(prime)) { - prime = new Buffer(prime, enc) - } - - return new DH(prime, generator, true) -} - -exports.DiffieHellmanGroup = exports.createDiffieHellmanGroup = exports.getDiffieHellman = getDiffieHellman -exports.createDiffieHellman = exports.DiffieHellman = createDiffieHellman - -}).call(this)}).call(this,require("buffer").Buffer) -},{"./lib/dh":100,"./lib/generatePrime":101,"./lib/primes.json":102,"buffer":63}],100:[function(require,module,exports){ -(function (Buffer){(function (){ -var BN = require('bn.js'); -var MillerRabin = require('miller-rabin'); -var millerRabin = new MillerRabin(); -var TWENTYFOUR = new BN(24); -var ELEVEN = new BN(11); -var TEN = new BN(10); -var THREE = new BN(3); -var SEVEN = new BN(7); -var primes = require('./generatePrime'); -var randomBytes = require('randombytes'); -module.exports = DH; - -function setPublicKey(pub, enc) { - enc = enc || 'utf8'; - if (!Buffer.isBuffer(pub)) { - pub = new Buffer(pub, enc); - } - this._pub = new BN(pub); - return this; -} - -function setPrivateKey(priv, enc) { - enc = enc || 'utf8'; - if (!Buffer.isBuffer(priv)) { - priv = new Buffer(priv, enc); - } - this._priv = new BN(priv); - return this; -} - -var primeCache = {}; -function checkPrime(prime, generator) { - var gen = generator.toString('hex'); - var hex = [gen, prime.toString(16)].join('_'); - if (hex in primeCache) { - return primeCache[hex]; - } - var error = 0; - - if (prime.isEven() || - !primes.simpleSieve || - !primes.fermatTest(prime) || - !millerRabin.test(prime)) { - //not a prime so +1 - error += 1; - - if (gen === '02' || gen === '05') { - // we'd be able to check the generator - // it would fail so +8 - error += 8; - } else { - //we wouldn't be able to test the generator - // so +4 - error += 4; - } - primeCache[hex] = error; - return error; - } - if (!millerRabin.test(prime.shrn(1))) { - //not a safe prime - error += 2; - } - var rem; - switch (gen) { - case '02': - if (prime.mod(TWENTYFOUR).cmp(ELEVEN)) { - // unsuidable generator - error += 8; - } - break; - case '05': - rem = prime.mod(TEN); - if (rem.cmp(THREE) && rem.cmp(SEVEN)) { - // prime mod 10 needs to equal 3 or 7 - error += 8; - } - break; - default: - error += 4; - } - primeCache[hex] = error; - return error; -} - -function DH(prime, generator, malleable) { - this.setGenerator(generator); - this.__prime = new BN(prime); - this._prime = BN.mont(this.__prime); - this._primeLen = prime.length; - this._pub = undefined; - this._priv = undefined; - this._primeCode = undefined; - if (malleable) { - this.setPublicKey = setPublicKey; - this.setPrivateKey = setPrivateKey; - } else { - this._primeCode = 8; - } -} -Object.defineProperty(DH.prototype, 'verifyError', { - enumerable: true, - get: function () { - if (typeof this._primeCode !== 'number') { - this._primeCode = checkPrime(this.__prime, this.__gen); - } - return this._primeCode; - } -}); -DH.prototype.generateKeys = function () { - if (!this._priv) { - this._priv = new BN(randomBytes(this._primeLen)); - } - this._pub = this._gen.toRed(this._prime).redPow(this._priv).fromRed(); - return this.getPublicKey(); -}; - -DH.prototype.computeSecret = function (other) { - other = new BN(other); - other = other.toRed(this._prime); - var secret = other.redPow(this._priv).fromRed(); - var out = new Buffer(secret.toArray()); - var prime = this.getPrime(); - if (out.length < prime.length) { - var front = new Buffer(prime.length - out.length); - front.fill(0); - out = Buffer.concat([front, out]); - } - return out; -}; - -DH.prototype.getPublicKey = function getPublicKey(enc) { - return formatReturnValue(this._pub, enc); -}; - -DH.prototype.getPrivateKey = function getPrivateKey(enc) { - return formatReturnValue(this._priv, enc); -}; - -DH.prototype.getPrime = function (enc) { - return formatReturnValue(this.__prime, enc); -}; - -DH.prototype.getGenerator = function (enc) { - return formatReturnValue(this._gen, enc); -}; - -DH.prototype.setGenerator = function (gen, enc) { - enc = enc || 'utf8'; - if (!Buffer.isBuffer(gen)) { - gen = new Buffer(gen, enc); - } - this.__gen = gen; - this._gen = new BN(gen); - return this; -}; - -function formatReturnValue(bn, enc) { - var buf = new Buffer(bn.toArray()); - if (!enc) { - return buf; - } else { - return buf.toString(enc); - } -} - -}).call(this)}).call(this,require("buffer").Buffer) -},{"./generatePrime":101,"bn.js":103,"buffer":63,"miller-rabin":158,"randombytes":207}],101:[function(require,module,exports){ -var randomBytes = require('randombytes'); -module.exports = findPrime; -findPrime.simpleSieve = simpleSieve; -findPrime.fermatTest = fermatTest; -var BN = require('bn.js'); -var TWENTYFOUR = new BN(24); -var MillerRabin = require('miller-rabin'); -var millerRabin = new MillerRabin(); -var ONE = new BN(1); -var TWO = new BN(2); -var FIVE = new BN(5); -var SIXTEEN = new BN(16); -var EIGHT = new BN(8); -var TEN = new BN(10); -var THREE = new BN(3); -var SEVEN = new BN(7); -var ELEVEN = new BN(11); -var FOUR = new BN(4); -var TWELVE = new BN(12); -var primes = null; - -function _getPrimes() { - if (primes !== null) - return primes; - - var limit = 0x100000; - var res = []; - res[0] = 2; - for (var i = 1, k = 3; k < limit; k += 2) { - var sqrt = Math.ceil(Math.sqrt(k)); - for (var j = 0; j < i && res[j] <= sqrt; j++) - if (k % res[j] === 0) - break; - - if (i !== j && res[j] <= sqrt) - continue; - - res[i++] = k; - } - primes = res; - return res; -} - -function simpleSieve(p) { - var primes = _getPrimes(); - - for (var i = 0; i < primes.length; i++) - if (p.modn(primes[i]) === 0) { - if (p.cmpn(primes[i]) === 0) { - return true; - } else { - return false; - } - } - - return true; -} - -function fermatTest(p) { - var red = BN.mont(p); - return TWO.toRed(red).redPow(p.subn(1)).fromRed().cmpn(1) === 0; -} - -function findPrime(bits, gen) { - if (bits < 16) { - // this is what openssl does - if (gen === 2 || gen === 5) { - return new BN([0x8c, 0x7b]); - } else { - return new BN([0x8c, 0x27]); - } - } - gen = new BN(gen); - - var num, n2; - - while (true) { - num = new BN(randomBytes(Math.ceil(bits / 8))); - while (num.bitLength() > bits) { - num.ishrn(1); - } - if (num.isEven()) { - num.iadd(ONE); - } - if (!num.testn(1)) { - num.iadd(TWO); - } - if (!gen.cmp(TWO)) { - while (num.mod(TWENTYFOUR).cmp(ELEVEN)) { - num.iadd(FOUR); - } - } else if (!gen.cmp(FIVE)) { - while (num.mod(TEN).cmp(THREE)) { - num.iadd(FOUR); - } - } - n2 = num.shrn(1); - if (simpleSieve(n2) && simpleSieve(num) && - fermatTest(n2) && fermatTest(num) && - millerRabin.test(n2) && millerRabin.test(num)) { - return num; - } - } - -} - -},{"bn.js":103,"miller-rabin":158,"randombytes":207}],102:[function(require,module,exports){ -module.exports={ - "modp1": { - "gen": "02", - "prime": "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a63a3620ffffffffffffffff" - }, - "modp2": { - "gen": "02", - "prime": "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece65381ffffffffffffffff" - }, - "modp5": { - "gen": "02", - "prime": "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca237327ffffffffffffffff" - }, - "modp14": { - "gen": "02", - "prime": "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aacaa68ffffffffffffffff" - }, - "modp15": { - "gen": "02", - "prime": "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aaac42dad33170d04507a33a85521abdf1cba64ecfb850458dbef0a8aea71575d060c7db3970f85a6e1e4c7abf5ae8cdb0933d71e8c94e04a25619dcee3d2261ad2ee6bf12ffa06d98a0864d87602733ec86a64521f2b18177b200cbbe117577a615d6c770988c0bad946e208e24fa074e5ab3143db5bfce0fd108e4b82d120a93ad2caffffffffffffffff" - }, - "modp16": { - "gen": "02", - "prime": "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aaac42dad33170d04507a33a85521abdf1cba64ecfb850458dbef0a8aea71575d060c7db3970f85a6e1e4c7abf5ae8cdb0933d71e8c94e04a25619dcee3d2261ad2ee6bf12ffa06d98a0864d87602733ec86a64521f2b18177b200cbbe117577a615d6c770988c0bad946e208e24fa074e5ab3143db5bfce0fd108e4b82d120a92108011a723c12a787e6d788719a10bdba5b2699c327186af4e23c1a946834b6150bda2583e9ca2ad44ce8dbbbc2db04de8ef92e8efc141fbecaa6287c59474e6bc05d99b2964fa090c3a2233ba186515be7ed1f612970cee2d7afb81bdd762170481cd0069127d5b05aa993b4ea988d8fddc186ffb7dc90a6c08f4df435c934063199ffffffffffffffff" - }, - "modp17": { - "gen": "02", - "prime": "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aaac42dad33170d04507a33a85521abdf1cba64ecfb850458dbef0a8aea71575d060c7db3970f85a6e1e4c7abf5ae8cdb0933d71e8c94e04a25619dcee3d2261ad2ee6bf12ffa06d98a0864d87602733ec86a64521f2b18177b200cbbe117577a615d6c770988c0bad946e208e24fa074e5ab3143db5bfce0fd108e4b82d120a92108011a723c12a787e6d788719a10bdba5b2699c327186af4e23c1a946834b6150bda2583e9ca2ad44ce8dbbbc2db04de8ef92e8efc141fbecaa6287c59474e6bc05d99b2964fa090c3a2233ba186515be7ed1f612970cee2d7afb81bdd762170481cd0069127d5b05aa993b4ea988d8fddc186ffb7dc90a6c08f4df435c93402849236c3fab4d27c7026c1d4dcb2602646dec9751e763dba37bdf8ff9406ad9e530ee5db382f413001aeb06a53ed9027d831179727b0865a8918da3edbebcf9b14ed44ce6cbaced4bb1bdb7f1447e6cc254b332051512bd7af426fb8f401378cd2bf5983ca01c64b92ecf032ea15d1721d03f482d7ce6e74fef6d55e702f46980c82b5a84031900b1c9e59e7c97fbec7e8f323a97a7e36cc88be0f1d45b7ff585ac54bd407b22b4154aacc8f6d7ebf48e1d814cc5ed20f8037e0a79715eef29be32806a1d58bb7c5da76f550aa3d8a1fbff0eb19ccb1a313d55cda56c9ec2ef29632387fe8d76e3c0468043e8f663f4860ee12bf2d5b0b7474d6e694f91e6dcc4024ffffffffffffffff" - }, - "modp18": { - "gen": "02", - "prime": "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aaac42dad33170d04507a33a85521abdf1cba64ecfb850458dbef0a8aea71575d060c7db3970f85a6e1e4c7abf5ae8cdb0933d71e8c94e04a25619dcee3d2261ad2ee6bf12ffa06d98a0864d87602733ec86a64521f2b18177b200cbbe117577a615d6c770988c0bad946e208e24fa074e5ab3143db5bfce0fd108e4b82d120a92108011a723c12a787e6d788719a10bdba5b2699c327186af4e23c1a946834b6150bda2583e9ca2ad44ce8dbbbc2db04de8ef92e8efc141fbecaa6287c59474e6bc05d99b2964fa090c3a2233ba186515be7ed1f612970cee2d7afb81bdd762170481cd0069127d5b05aa993b4ea988d8fddc186ffb7dc90a6c08f4df435c93402849236c3fab4d27c7026c1d4dcb2602646dec9751e763dba37bdf8ff9406ad9e530ee5db382f413001aeb06a53ed9027d831179727b0865a8918da3edbebcf9b14ed44ce6cbaced4bb1bdb7f1447e6cc254b332051512bd7af426fb8f401378cd2bf5983ca01c64b92ecf032ea15d1721d03f482d7ce6e74fef6d55e702f46980c82b5a84031900b1c9e59e7c97fbec7e8f323a97a7e36cc88be0f1d45b7ff585ac54bd407b22b4154aacc8f6d7ebf48e1d814cc5ed20f8037e0a79715eef29be32806a1d58bb7c5da76f550aa3d8a1fbff0eb19ccb1a313d55cda56c9ec2ef29632387fe8d76e3c0468043e8f663f4860ee12bf2d5b0b7474d6e694f91e6dbe115974a3926f12fee5e438777cb6a932df8cd8bec4d073b931ba3bc832b68d9dd300741fa7bf8afc47ed2576f6936ba424663aab639c5ae4f5683423b4742bf1c978238f16cbe39d652de3fdb8befc848ad922222e04a4037c0713eb57a81a23f0c73473fc646cea306b4bcbc8862f8385ddfa9d4b7fa2c087e879683303ed5bdd3a062b3cf5b3a278a66d2a13f83f44f82ddf310ee074ab6a364597e899a0255dc164f31cc50846851df9ab48195ded7ea1b1d510bd7ee74d73faf36bc31ecfa268359046f4eb879f924009438b481c6cd7889a002ed5ee382bc9190da6fc026e479558e4475677e9aa9e3050e2765694dfc81f56e880b96e7160c980dd98edd3dfffffffffffffffff" - } -} -},{}],103:[function(require,module,exports){ -arguments[4][20][0].apply(exports,arguments) -},{"buffer":34,"dup":20}],104:[function(require,module,exports){ -'use strict'; - -var elliptic = exports; - -elliptic.version = require('../package.json').version; -elliptic.utils = require('./elliptic/utils'); -elliptic.rand = require('brorand'); -elliptic.curve = require('./elliptic/curve'); -elliptic.curves = require('./elliptic/curves'); - -// Protocols -elliptic.ec = require('./elliptic/ec'); -elliptic.eddsa = require('./elliptic/eddsa'); - -},{"../package.json":120,"./elliptic/curve":107,"./elliptic/curves":110,"./elliptic/ec":111,"./elliptic/eddsa":114,"./elliptic/utils":118,"brorand":33}],105:[function(require,module,exports){ -'use strict'; - -var BN = require('bn.js'); -var utils = require('../utils'); -var getNAF = utils.getNAF; -var getJSF = utils.getJSF; -var assert = utils.assert; - -function BaseCurve(type, conf) { - this.type = type; - this.p = new BN(conf.p, 16); - - // Use Montgomery, when there is no fast reduction for the prime - this.red = conf.prime ? BN.red(conf.prime) : BN.mont(this.p); - - // Useful for many curves - this.zero = new BN(0).toRed(this.red); - this.one = new BN(1).toRed(this.red); - this.two = new BN(2).toRed(this.red); - - // Curve configuration, optional - this.n = conf.n && new BN(conf.n, 16); - this.g = conf.g && this.pointFromJSON(conf.g, conf.gRed); - - // Temporary arrays - this._wnafT1 = new Array(4); - this._wnafT2 = new Array(4); - this._wnafT3 = new Array(4); - this._wnafT4 = new Array(4); - - this._bitLength = this.n ? this.n.bitLength() : 0; - - // Generalized Greg Maxwell's trick - var adjustCount = this.n && this.p.div(this.n); - if (!adjustCount || adjustCount.cmpn(100) > 0) { - this.redN = null; - } else { - this._maxwellTrick = true; - this.redN = this.n.toRed(this.red); - } -} -module.exports = BaseCurve; - -BaseCurve.prototype.point = function point() { - throw new Error('Not implemented'); -}; - -BaseCurve.prototype.validate = function validate() { - throw new Error('Not implemented'); -}; - -BaseCurve.prototype._fixedNafMul = function _fixedNafMul(p, k) { - assert(p.precomputed); - var doubles = p._getDoubles(); - - var naf = getNAF(k, 1, this._bitLength); - var I = (1 << (doubles.step + 1)) - (doubles.step % 2 === 0 ? 2 : 1); - I /= 3; - - // Translate into more windowed form - var repr = []; - var j; - var nafW; - for (j = 0; j < naf.length; j += doubles.step) { - nafW = 0; - for (var l = j + doubles.step - 1; l >= j; l--) - nafW = (nafW << 1) + naf[l]; - repr.push(nafW); - } - - var a = this.jpoint(null, null, null); - var b = this.jpoint(null, null, null); - for (var i = I; i > 0; i--) { - for (j = 0; j < repr.length; j++) { - nafW = repr[j]; - if (nafW === i) - b = b.mixedAdd(doubles.points[j]); - else if (nafW === -i) - b = b.mixedAdd(doubles.points[j].neg()); - } - a = a.add(b); - } - return a.toP(); -}; - -BaseCurve.prototype._wnafMul = function _wnafMul(p, k) { - var w = 4; - - // Precompute window - var nafPoints = p._getNAFPoints(w); - w = nafPoints.wnd; - var wnd = nafPoints.points; - - // Get NAF form - var naf = getNAF(k, w, this._bitLength); - - // Add `this`*(N+1) for every w-NAF index - var acc = this.jpoint(null, null, null); - for (var i = naf.length - 1; i >= 0; i--) { - // Count zeroes - for (var l = 0; i >= 0 && naf[i] === 0; i--) - l++; - if (i >= 0) - l++; - acc = acc.dblp(l); - - if (i < 0) - break; - var z = naf[i]; - assert(z !== 0); - if (p.type === 'affine') { - // J +- P - if (z > 0) - acc = acc.mixedAdd(wnd[(z - 1) >> 1]); - else - acc = acc.mixedAdd(wnd[(-z - 1) >> 1].neg()); - } else { - // J +- J - if (z > 0) - acc = acc.add(wnd[(z - 1) >> 1]); - else - acc = acc.add(wnd[(-z - 1) >> 1].neg()); - } - } - return p.type === 'affine' ? acc.toP() : acc; -}; - -BaseCurve.prototype._wnafMulAdd = function _wnafMulAdd(defW, - points, - coeffs, - len, - jacobianResult) { - var wndWidth = this._wnafT1; - var wnd = this._wnafT2; - var naf = this._wnafT3; - - // Fill all arrays - var max = 0; - var i; - var j; - var p; - for (i = 0; i < len; i++) { - p = points[i]; - var nafPoints = p._getNAFPoints(defW); - wndWidth[i] = nafPoints.wnd; - wnd[i] = nafPoints.points; - } - - // Comb small window NAFs - for (i = len - 1; i >= 1; i -= 2) { - var a = i - 1; - var b = i; - if (wndWidth[a] !== 1 || wndWidth[b] !== 1) { - naf[a] = getNAF(coeffs[a], wndWidth[a], this._bitLength); - naf[b] = getNAF(coeffs[b], wndWidth[b], this._bitLength); - max = Math.max(naf[a].length, max); - max = Math.max(naf[b].length, max); - continue; - } - - var comb = [ - points[a], /* 1 */ - null, /* 3 */ - null, /* 5 */ - points[b], /* 7 */ - ]; - - // Try to avoid Projective points, if possible - if (points[a].y.cmp(points[b].y) === 0) { - comb[1] = points[a].add(points[b]); - comb[2] = points[a].toJ().mixedAdd(points[b].neg()); - } else if (points[a].y.cmp(points[b].y.redNeg()) === 0) { - comb[1] = points[a].toJ().mixedAdd(points[b]); - comb[2] = points[a].add(points[b].neg()); - } else { - comb[1] = points[a].toJ().mixedAdd(points[b]); - comb[2] = points[a].toJ().mixedAdd(points[b].neg()); - } - - var index = [ - -3, /* -1 -1 */ - -1, /* -1 0 */ - -5, /* -1 1 */ - -7, /* 0 -1 */ - 0, /* 0 0 */ - 7, /* 0 1 */ - 5, /* 1 -1 */ - 1, /* 1 0 */ - 3, /* 1 1 */ - ]; - - var jsf = getJSF(coeffs[a], coeffs[b]); - max = Math.max(jsf[0].length, max); - naf[a] = new Array(max); - naf[b] = new Array(max); - for (j = 0; j < max; j++) { - var ja = jsf[0][j] | 0; - var jb = jsf[1][j] | 0; - - naf[a][j] = index[(ja + 1) * 3 + (jb + 1)]; - naf[b][j] = 0; - wnd[a] = comb; - } - } - - var acc = this.jpoint(null, null, null); - var tmp = this._wnafT4; - for (i = max; i >= 0; i--) { - var k = 0; - - while (i >= 0) { - var zero = true; - for (j = 0; j < len; j++) { - tmp[j] = naf[j][i] | 0; - if (tmp[j] !== 0) - zero = false; - } - if (!zero) - break; - k++; - i--; - } - if (i >= 0) - k++; - acc = acc.dblp(k); - if (i < 0) - break; - - for (j = 0; j < len; j++) { - var z = tmp[j]; - p; - if (z === 0) - continue; - else if (z > 0) - p = wnd[j][(z - 1) >> 1]; - else if (z < 0) - p = wnd[j][(-z - 1) >> 1].neg(); - - if (p.type === 'affine') - acc = acc.mixedAdd(p); - else - acc = acc.add(p); - } - } - // Zeroify references - for (i = 0; i < len; i++) - wnd[i] = null; - - if (jacobianResult) - return acc; - else - return acc.toP(); -}; - -function BasePoint(curve, type) { - this.curve = curve; - this.type = type; - this.precomputed = null; -} -BaseCurve.BasePoint = BasePoint; - -BasePoint.prototype.eq = function eq(/*other*/) { - throw new Error('Not implemented'); -}; - -BasePoint.prototype.validate = function validate() { - return this.curve.validate(this); -}; - -BaseCurve.prototype.decodePoint = function decodePoint(bytes, enc) { - bytes = utils.toArray(bytes, enc); - - var len = this.p.byteLength(); - - // uncompressed, hybrid-odd, hybrid-even - if ((bytes[0] === 0x04 || bytes[0] === 0x06 || bytes[0] === 0x07) && - bytes.length - 1 === 2 * len) { - if (bytes[0] === 0x06) - assert(bytes[bytes.length - 1] % 2 === 0); - else if (bytes[0] === 0x07) - assert(bytes[bytes.length - 1] % 2 === 1); - - var res = this.point(bytes.slice(1, 1 + len), - bytes.slice(1 + len, 1 + 2 * len)); - - return res; - } else if ((bytes[0] === 0x02 || bytes[0] === 0x03) && - bytes.length - 1 === len) { - return this.pointFromX(bytes.slice(1, 1 + len), bytes[0] === 0x03); - } - throw new Error('Unknown point format'); -}; - -BasePoint.prototype.encodeCompressed = function encodeCompressed(enc) { - return this.encode(enc, true); -}; - -BasePoint.prototype._encode = function _encode(compact) { - var len = this.curve.p.byteLength(); - var x = this.getX().toArray('be', len); - - if (compact) - return [ this.getY().isEven() ? 0x02 : 0x03 ].concat(x); - - return [ 0x04 ].concat(x, this.getY().toArray('be', len)); -}; - -BasePoint.prototype.encode = function encode(enc, compact) { - return utils.encode(this._encode(compact), enc); -}; - -BasePoint.prototype.precompute = function precompute(power) { - if (this.precomputed) - return this; - - var precomputed = { - doubles: null, - naf: null, - beta: null, - }; - precomputed.naf = this._getNAFPoints(8); - precomputed.doubles = this._getDoubles(4, power); - precomputed.beta = this._getBeta(); - this.precomputed = precomputed; - - return this; -}; - -BasePoint.prototype._hasDoubles = function _hasDoubles(k) { - if (!this.precomputed) - return false; - - var doubles = this.precomputed.doubles; - if (!doubles) - return false; - - return doubles.points.length >= Math.ceil((k.bitLength() + 1) / doubles.step); -}; - -BasePoint.prototype._getDoubles = function _getDoubles(step, power) { - if (this.precomputed && this.precomputed.doubles) - return this.precomputed.doubles; - - var doubles = [ this ]; - var acc = this; - for (var i = 0; i < power; i += step) { - for (var j = 0; j < step; j++) - acc = acc.dbl(); - doubles.push(acc); - } - return { - step: step, - points: doubles, - }; -}; - -BasePoint.prototype._getNAFPoints = function _getNAFPoints(wnd) { - if (this.precomputed && this.precomputed.naf) - return this.precomputed.naf; - - var res = [ this ]; - var max = (1 << wnd) - 1; - var dbl = max === 1 ? null : this.dbl(); - for (var i = 1; i < max; i++) - res[i] = res[i - 1].add(dbl); - return { - wnd: wnd, - points: res, - }; -}; - -BasePoint.prototype._getBeta = function _getBeta() { - return null; -}; - -BasePoint.prototype.dblp = function dblp(k) { - var r = this; - for (var i = 0; i < k; i++) - r = r.dbl(); - return r; -}; - -},{"../utils":118,"bn.js":119}],106:[function(require,module,exports){ -'use strict'; - -var utils = require('../utils'); -var BN = require('bn.js'); -var inherits = require('inherits'); -var Base = require('./base'); - -var assert = utils.assert; - -function EdwardsCurve(conf) { - // NOTE: Important as we are creating point in Base.call() - this.twisted = (conf.a | 0) !== 1; - this.mOneA = this.twisted && (conf.a | 0) === -1; - this.extended = this.mOneA; - - Base.call(this, 'edwards', conf); - - this.a = new BN(conf.a, 16).umod(this.red.m); - this.a = this.a.toRed(this.red); - this.c = new BN(conf.c, 16).toRed(this.red); - this.c2 = this.c.redSqr(); - this.d = new BN(conf.d, 16).toRed(this.red); - this.dd = this.d.redAdd(this.d); - - assert(!this.twisted || this.c.fromRed().cmpn(1) === 0); - this.oneC = (conf.c | 0) === 1; -} -inherits(EdwardsCurve, Base); -module.exports = EdwardsCurve; - -EdwardsCurve.prototype._mulA = function _mulA(num) { - if (this.mOneA) - return num.redNeg(); - else - return this.a.redMul(num); -}; - -EdwardsCurve.prototype._mulC = function _mulC(num) { - if (this.oneC) - return num; - else - return this.c.redMul(num); -}; - -// Just for compatibility with Short curve -EdwardsCurve.prototype.jpoint = function jpoint(x, y, z, t) { - return this.point(x, y, z, t); -}; - -EdwardsCurve.prototype.pointFromX = function pointFromX(x, odd) { - x = new BN(x, 16); - if (!x.red) - x = x.toRed(this.red); - - var x2 = x.redSqr(); - var rhs = this.c2.redSub(this.a.redMul(x2)); - var lhs = this.one.redSub(this.c2.redMul(this.d).redMul(x2)); - - var y2 = rhs.redMul(lhs.redInvm()); - var y = y2.redSqrt(); - if (y.redSqr().redSub(y2).cmp(this.zero) !== 0) - throw new Error('invalid point'); - - var isOdd = y.fromRed().isOdd(); - if (odd && !isOdd || !odd && isOdd) - y = y.redNeg(); - - return this.point(x, y); -}; - -EdwardsCurve.prototype.pointFromY = function pointFromY(y, odd) { - y = new BN(y, 16); - if (!y.red) - y = y.toRed(this.red); - - // x^2 = (y^2 - c^2) / (c^2 d y^2 - a) - var y2 = y.redSqr(); - var lhs = y2.redSub(this.c2); - var rhs = y2.redMul(this.d).redMul(this.c2).redSub(this.a); - var x2 = lhs.redMul(rhs.redInvm()); - - if (x2.cmp(this.zero) === 0) { - if (odd) - throw new Error('invalid point'); - else - return this.point(this.zero, y); - } - - var x = x2.redSqrt(); - if (x.redSqr().redSub(x2).cmp(this.zero) !== 0) - throw new Error('invalid point'); - - if (x.fromRed().isOdd() !== odd) - x = x.redNeg(); - - return this.point(x, y); -}; - -EdwardsCurve.prototype.validate = function validate(point) { - if (point.isInfinity()) - return true; - - // Curve: A * X^2 + Y^2 = C^2 * (1 + D * X^2 * Y^2) - point.normalize(); - - var x2 = point.x.redSqr(); - var y2 = point.y.redSqr(); - var lhs = x2.redMul(this.a).redAdd(y2); - var rhs = this.c2.redMul(this.one.redAdd(this.d.redMul(x2).redMul(y2))); - - return lhs.cmp(rhs) === 0; -}; - -function Point(curve, x, y, z, t) { - Base.BasePoint.call(this, curve, 'projective'); - if (x === null && y === null && z === null) { - this.x = this.curve.zero; - this.y = this.curve.one; - this.z = this.curve.one; - this.t = this.curve.zero; - this.zOne = true; - } else { - this.x = new BN(x, 16); - this.y = new BN(y, 16); - this.z = z ? new BN(z, 16) : this.curve.one; - this.t = t && new BN(t, 16); - if (!this.x.red) - this.x = this.x.toRed(this.curve.red); - if (!this.y.red) - this.y = this.y.toRed(this.curve.red); - if (!this.z.red) - this.z = this.z.toRed(this.curve.red); - if (this.t && !this.t.red) - this.t = this.t.toRed(this.curve.red); - this.zOne = this.z === this.curve.one; - - // Use extended coordinates - if (this.curve.extended && !this.t) { - this.t = this.x.redMul(this.y); - if (!this.zOne) - this.t = this.t.redMul(this.z.redInvm()); - } - } -} -inherits(Point, Base.BasePoint); - -EdwardsCurve.prototype.pointFromJSON = function pointFromJSON(obj) { - return Point.fromJSON(this, obj); -}; - -EdwardsCurve.prototype.point = function point(x, y, z, t) { - return new Point(this, x, y, z, t); -}; - -Point.fromJSON = function fromJSON(curve, obj) { - return new Point(curve, obj[0], obj[1], obj[2]); -}; - -Point.prototype.inspect = function inspect() { - if (this.isInfinity()) - return ''; - return ''; -}; - -Point.prototype.isInfinity = function isInfinity() { - // XXX This code assumes that zero is always zero in red - return this.x.cmpn(0) === 0 && - (this.y.cmp(this.z) === 0 || - (this.zOne && this.y.cmp(this.curve.c) === 0)); -}; - -Point.prototype._extDbl = function _extDbl() { - // hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html - // #doubling-dbl-2008-hwcd - // 4M + 4S - - // A = X1^2 - var a = this.x.redSqr(); - // B = Y1^2 - var b = this.y.redSqr(); - // C = 2 * Z1^2 - var c = this.z.redSqr(); - c = c.redIAdd(c); - // D = a * A - var d = this.curve._mulA(a); - // E = (X1 + Y1)^2 - A - B - var e = this.x.redAdd(this.y).redSqr().redISub(a).redISub(b); - // G = D + B - var g = d.redAdd(b); - // F = G - C - var f = g.redSub(c); - // H = D - B - var h = d.redSub(b); - // X3 = E * F - var nx = e.redMul(f); - // Y3 = G * H - var ny = g.redMul(h); - // T3 = E * H - var nt = e.redMul(h); - // Z3 = F * G - var nz = f.redMul(g); - return this.curve.point(nx, ny, nz, nt); -}; - -Point.prototype._projDbl = function _projDbl() { - // hyperelliptic.org/EFD/g1p/auto-twisted-projective.html - // #doubling-dbl-2008-bbjlp - // #doubling-dbl-2007-bl - // and others - // Generally 3M + 4S or 2M + 4S - - // B = (X1 + Y1)^2 - var b = this.x.redAdd(this.y).redSqr(); - // C = X1^2 - var c = this.x.redSqr(); - // D = Y1^2 - var d = this.y.redSqr(); - - var nx; - var ny; - var nz; - var e; - var h; - var j; - if (this.curve.twisted) { - // E = a * C - e = this.curve._mulA(c); - // F = E + D - var f = e.redAdd(d); - if (this.zOne) { - // X3 = (B - C - D) * (F - 2) - nx = b.redSub(c).redSub(d).redMul(f.redSub(this.curve.two)); - // Y3 = F * (E - D) - ny = f.redMul(e.redSub(d)); - // Z3 = F^2 - 2 * F - nz = f.redSqr().redSub(f).redSub(f); - } else { - // H = Z1^2 - h = this.z.redSqr(); - // J = F - 2 * H - j = f.redSub(h).redISub(h); - // X3 = (B-C-D)*J - nx = b.redSub(c).redISub(d).redMul(j); - // Y3 = F * (E - D) - ny = f.redMul(e.redSub(d)); - // Z3 = F * J - nz = f.redMul(j); - } - } else { - // E = C + D - e = c.redAdd(d); - // H = (c * Z1)^2 - h = this.curve._mulC(this.z).redSqr(); - // J = E - 2 * H - j = e.redSub(h).redSub(h); - // X3 = c * (B - E) * J - nx = this.curve._mulC(b.redISub(e)).redMul(j); - // Y3 = c * E * (C - D) - ny = this.curve._mulC(e).redMul(c.redISub(d)); - // Z3 = E * J - nz = e.redMul(j); - } - return this.curve.point(nx, ny, nz); -}; - -Point.prototype.dbl = function dbl() { - if (this.isInfinity()) - return this; - - // Double in extended coordinates - if (this.curve.extended) - return this._extDbl(); - else - return this._projDbl(); -}; - -Point.prototype._extAdd = function _extAdd(p) { - // hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html - // #addition-add-2008-hwcd-3 - // 8M - - // A = (Y1 - X1) * (Y2 - X2) - var a = this.y.redSub(this.x).redMul(p.y.redSub(p.x)); - // B = (Y1 + X1) * (Y2 + X2) - var b = this.y.redAdd(this.x).redMul(p.y.redAdd(p.x)); - // C = T1 * k * T2 - var c = this.t.redMul(this.curve.dd).redMul(p.t); - // D = Z1 * 2 * Z2 - var d = this.z.redMul(p.z.redAdd(p.z)); - // E = B - A - var e = b.redSub(a); - // F = D - C - var f = d.redSub(c); - // G = D + C - var g = d.redAdd(c); - // H = B + A - var h = b.redAdd(a); - // X3 = E * F - var nx = e.redMul(f); - // Y3 = G * H - var ny = g.redMul(h); - // T3 = E * H - var nt = e.redMul(h); - // Z3 = F * G - var nz = f.redMul(g); - return this.curve.point(nx, ny, nz, nt); -}; - -Point.prototype._projAdd = function _projAdd(p) { - // hyperelliptic.org/EFD/g1p/auto-twisted-projective.html - // #addition-add-2008-bbjlp - // #addition-add-2007-bl - // 10M + 1S - - // A = Z1 * Z2 - var a = this.z.redMul(p.z); - // B = A^2 - var b = a.redSqr(); - // C = X1 * X2 - var c = this.x.redMul(p.x); - // D = Y1 * Y2 - var d = this.y.redMul(p.y); - // E = d * C * D - var e = this.curve.d.redMul(c).redMul(d); - // F = B - E - var f = b.redSub(e); - // G = B + E - var g = b.redAdd(e); - // X3 = A * F * ((X1 + Y1) * (X2 + Y2) - C - D) - var tmp = this.x.redAdd(this.y).redMul(p.x.redAdd(p.y)).redISub(c).redISub(d); - var nx = a.redMul(f).redMul(tmp); - var ny; - var nz; - if (this.curve.twisted) { - // Y3 = A * G * (D - a * C) - ny = a.redMul(g).redMul(d.redSub(this.curve._mulA(c))); - // Z3 = F * G - nz = f.redMul(g); - } else { - // Y3 = A * G * (D - C) - ny = a.redMul(g).redMul(d.redSub(c)); - // Z3 = c * F * G - nz = this.curve._mulC(f).redMul(g); - } - return this.curve.point(nx, ny, nz); -}; - -Point.prototype.add = function add(p) { - if (this.isInfinity()) - return p; - if (p.isInfinity()) - return this; - - if (this.curve.extended) - return this._extAdd(p); - else - return this._projAdd(p); -}; - -Point.prototype.mul = function mul(k) { - if (this._hasDoubles(k)) - return this.curve._fixedNafMul(this, k); - else - return this.curve._wnafMul(this, k); -}; - -Point.prototype.mulAdd = function mulAdd(k1, p, k2) { - return this.curve._wnafMulAdd(1, [ this, p ], [ k1, k2 ], 2, false); -}; - -Point.prototype.jmulAdd = function jmulAdd(k1, p, k2) { - return this.curve._wnafMulAdd(1, [ this, p ], [ k1, k2 ], 2, true); -}; - -Point.prototype.normalize = function normalize() { - if (this.zOne) - return this; - - // Normalize coordinates - var zi = this.z.redInvm(); - this.x = this.x.redMul(zi); - this.y = this.y.redMul(zi); - if (this.t) - this.t = this.t.redMul(zi); - this.z = this.curve.one; - this.zOne = true; - return this; -}; - -Point.prototype.neg = function neg() { - return this.curve.point(this.x.redNeg(), - this.y, - this.z, - this.t && this.t.redNeg()); -}; - -Point.prototype.getX = function getX() { - this.normalize(); - return this.x.fromRed(); -}; - -Point.prototype.getY = function getY() { - this.normalize(); - return this.y.fromRed(); -}; - -Point.prototype.eq = function eq(other) { - return this === other || - this.getX().cmp(other.getX()) === 0 && - this.getY().cmp(other.getY()) === 0; -}; - -Point.prototype.eqXToP = function eqXToP(x) { - var rx = x.toRed(this.curve.red).redMul(this.z); - if (this.x.cmp(rx) === 0) - return true; - - var xc = x.clone(); - var t = this.curve.redN.redMul(this.z); - for (;;) { - xc.iadd(this.curve.n); - if (xc.cmp(this.curve.p) >= 0) - return false; - - rx.redIAdd(t); - if (this.x.cmp(rx) === 0) - return true; - } -}; - -// Compatibility with BaseCurve -Point.prototype.toP = Point.prototype.normalize; -Point.prototype.mixedAdd = Point.prototype.add; - -},{"../utils":118,"./base":105,"bn.js":119,"inherits":155}],107:[function(require,module,exports){ -'use strict'; - -var curve = exports; - -curve.base = require('./base'); -curve.short = require('./short'); -curve.mont = require('./mont'); -curve.edwards = require('./edwards'); - -},{"./base":105,"./edwards":106,"./mont":108,"./short":109}],108:[function(require,module,exports){ -'use strict'; - -var BN = require('bn.js'); -var inherits = require('inherits'); -var Base = require('./base'); - -var utils = require('../utils'); - -function MontCurve(conf) { - Base.call(this, 'mont', conf); - - this.a = new BN(conf.a, 16).toRed(this.red); - this.b = new BN(conf.b, 16).toRed(this.red); - this.i4 = new BN(4).toRed(this.red).redInvm(); - this.two = new BN(2).toRed(this.red); - this.a24 = this.i4.redMul(this.a.redAdd(this.two)); -} -inherits(MontCurve, Base); -module.exports = MontCurve; - -MontCurve.prototype.validate = function validate(point) { - var x = point.normalize().x; - var x2 = x.redSqr(); - var rhs = x2.redMul(x).redAdd(x2.redMul(this.a)).redAdd(x); - var y = rhs.redSqrt(); - - return y.redSqr().cmp(rhs) === 0; -}; - -function Point(curve, x, z) { - Base.BasePoint.call(this, curve, 'projective'); - if (x === null && z === null) { - this.x = this.curve.one; - this.z = this.curve.zero; - } else { - this.x = new BN(x, 16); - this.z = new BN(z, 16); - if (!this.x.red) - this.x = this.x.toRed(this.curve.red); - if (!this.z.red) - this.z = this.z.toRed(this.curve.red); - } -} -inherits(Point, Base.BasePoint); - -MontCurve.prototype.decodePoint = function decodePoint(bytes, enc) { - return this.point(utils.toArray(bytes, enc), 1); -}; - -MontCurve.prototype.point = function point(x, z) { - return new Point(this, x, z); -}; - -MontCurve.prototype.pointFromJSON = function pointFromJSON(obj) { - return Point.fromJSON(this, obj); -}; - -Point.prototype.precompute = function precompute() { - // No-op -}; - -Point.prototype._encode = function _encode() { - return this.getX().toArray('be', this.curve.p.byteLength()); -}; - -Point.fromJSON = function fromJSON(curve, obj) { - return new Point(curve, obj[0], obj[1] || curve.one); -}; - -Point.prototype.inspect = function inspect() { - if (this.isInfinity()) - return ''; - return ''; -}; - -Point.prototype.isInfinity = function isInfinity() { - // XXX This code assumes that zero is always zero in red - return this.z.cmpn(0) === 0; -}; - -Point.prototype.dbl = function dbl() { - // http://hyperelliptic.org/EFD/g1p/auto-montgom-xz.html#doubling-dbl-1987-m-3 - // 2M + 2S + 4A - - // A = X1 + Z1 - var a = this.x.redAdd(this.z); - // AA = A^2 - var aa = a.redSqr(); - // B = X1 - Z1 - var b = this.x.redSub(this.z); - // BB = B^2 - var bb = b.redSqr(); - // C = AA - BB - var c = aa.redSub(bb); - // X3 = AA * BB - var nx = aa.redMul(bb); - // Z3 = C * (BB + A24 * C) - var nz = c.redMul(bb.redAdd(this.curve.a24.redMul(c))); - return this.curve.point(nx, nz); -}; - -Point.prototype.add = function add() { - throw new Error('Not supported on Montgomery curve'); -}; - -Point.prototype.diffAdd = function diffAdd(p, diff) { - // http://hyperelliptic.org/EFD/g1p/auto-montgom-xz.html#diffadd-dadd-1987-m-3 - // 4M + 2S + 6A - - // A = X2 + Z2 - var a = this.x.redAdd(this.z); - // B = X2 - Z2 - var b = this.x.redSub(this.z); - // C = X3 + Z3 - var c = p.x.redAdd(p.z); - // D = X3 - Z3 - var d = p.x.redSub(p.z); - // DA = D * A - var da = d.redMul(a); - // CB = C * B - var cb = c.redMul(b); - // X5 = Z1 * (DA + CB)^2 - var nx = diff.z.redMul(da.redAdd(cb).redSqr()); - // Z5 = X1 * (DA - CB)^2 - var nz = diff.x.redMul(da.redISub(cb).redSqr()); - return this.curve.point(nx, nz); -}; - -Point.prototype.mul = function mul(k) { - var t = k.clone(); - var a = this; // (N / 2) * Q + Q - var b = this.curve.point(null, null); // (N / 2) * Q - var c = this; // Q - - for (var bits = []; t.cmpn(0) !== 0; t.iushrn(1)) - bits.push(t.andln(1)); - - for (var i = bits.length - 1; i >= 0; i--) { - if (bits[i] === 0) { - // N * Q + Q = ((N / 2) * Q + Q)) + (N / 2) * Q - a = a.diffAdd(b, c); - // N * Q = 2 * ((N / 2) * Q + Q)) - b = b.dbl(); - } else { - // N * Q = ((N / 2) * Q + Q) + ((N / 2) * Q) - b = a.diffAdd(b, c); - // N * Q + Q = 2 * ((N / 2) * Q + Q) - a = a.dbl(); - } - } - return b; -}; - -Point.prototype.mulAdd = function mulAdd() { - throw new Error('Not supported on Montgomery curve'); -}; - -Point.prototype.jumlAdd = function jumlAdd() { - throw new Error('Not supported on Montgomery curve'); -}; - -Point.prototype.eq = function eq(other) { - return this.getX().cmp(other.getX()) === 0; -}; - -Point.prototype.normalize = function normalize() { - this.x = this.x.redMul(this.z.redInvm()); - this.z = this.curve.one; - return this; -}; - -Point.prototype.getX = function getX() { - // Normalize coordinates - this.normalize(); - - return this.x.fromRed(); -}; - -},{"../utils":118,"./base":105,"bn.js":119,"inherits":155}],109:[function(require,module,exports){ -'use strict'; - -var utils = require('../utils'); -var BN = require('bn.js'); -var inherits = require('inherits'); -var Base = require('./base'); - -var assert = utils.assert; - -function ShortCurve(conf) { - Base.call(this, 'short', conf); - - this.a = new BN(conf.a, 16).toRed(this.red); - this.b = new BN(conf.b, 16).toRed(this.red); - this.tinv = this.two.redInvm(); - - this.zeroA = this.a.fromRed().cmpn(0) === 0; - this.threeA = this.a.fromRed().sub(this.p).cmpn(-3) === 0; - - // If the curve is endomorphic, precalculate beta and lambda - this.endo = this._getEndomorphism(conf); - this._endoWnafT1 = new Array(4); - this._endoWnafT2 = new Array(4); -} -inherits(ShortCurve, Base); -module.exports = ShortCurve; - -ShortCurve.prototype._getEndomorphism = function _getEndomorphism(conf) { - // No efficient endomorphism - if (!this.zeroA || !this.g || !this.n || this.p.modn(3) !== 1) - return; - - // Compute beta and lambda, that lambda * P = (beta * Px; Py) - var beta; - var lambda; - if (conf.beta) { - beta = new BN(conf.beta, 16).toRed(this.red); - } else { - var betas = this._getEndoRoots(this.p); - // Choose the smallest beta - beta = betas[0].cmp(betas[1]) < 0 ? betas[0] : betas[1]; - beta = beta.toRed(this.red); - } - if (conf.lambda) { - lambda = new BN(conf.lambda, 16); - } else { - // Choose the lambda that is matching selected beta - var lambdas = this._getEndoRoots(this.n); - if (this.g.mul(lambdas[0]).x.cmp(this.g.x.redMul(beta)) === 0) { - lambda = lambdas[0]; - } else { - lambda = lambdas[1]; - assert(this.g.mul(lambda).x.cmp(this.g.x.redMul(beta)) === 0); - } - } - - // Get basis vectors, used for balanced length-two representation - var basis; - if (conf.basis) { - basis = conf.basis.map(function(vec) { - return { - a: new BN(vec.a, 16), - b: new BN(vec.b, 16), - }; - }); - } else { - basis = this._getEndoBasis(lambda); - } - - return { - beta: beta, - lambda: lambda, - basis: basis, - }; -}; - -ShortCurve.prototype._getEndoRoots = function _getEndoRoots(num) { - // Find roots of for x^2 + x + 1 in F - // Root = (-1 +- Sqrt(-3)) / 2 - // - var red = num === this.p ? this.red : BN.mont(num); - var tinv = new BN(2).toRed(red).redInvm(); - var ntinv = tinv.redNeg(); - - var s = new BN(3).toRed(red).redNeg().redSqrt().redMul(tinv); - - var l1 = ntinv.redAdd(s).fromRed(); - var l2 = ntinv.redSub(s).fromRed(); - return [ l1, l2 ]; -}; - -ShortCurve.prototype._getEndoBasis = function _getEndoBasis(lambda) { - // aprxSqrt >= sqrt(this.n) - var aprxSqrt = this.n.ushrn(Math.floor(this.n.bitLength() / 2)); - - // 3.74 - // Run EGCD, until r(L + 1) < aprxSqrt - var u = lambda; - var v = this.n.clone(); - var x1 = new BN(1); - var y1 = new BN(0); - var x2 = new BN(0); - var y2 = new BN(1); - - // NOTE: all vectors are roots of: a + b * lambda = 0 (mod n) - var a0; - var b0; - // First vector - var a1; - var b1; - // Second vector - var a2; - var b2; - - var prevR; - var i = 0; - var r; - var x; - while (u.cmpn(0) !== 0) { - var q = v.div(u); - r = v.sub(q.mul(u)); - x = x2.sub(q.mul(x1)); - var y = y2.sub(q.mul(y1)); - - if (!a1 && r.cmp(aprxSqrt) < 0) { - a0 = prevR.neg(); - b0 = x1; - a1 = r.neg(); - b1 = x; - } else if (a1 && ++i === 2) { - break; - } - prevR = r; - - v = u; - u = r; - x2 = x1; - x1 = x; - y2 = y1; - y1 = y; - } - a2 = r.neg(); - b2 = x; - - var len1 = a1.sqr().add(b1.sqr()); - var len2 = a2.sqr().add(b2.sqr()); - if (len2.cmp(len1) >= 0) { - a2 = a0; - b2 = b0; - } - - // Normalize signs - if (a1.negative) { - a1 = a1.neg(); - b1 = b1.neg(); - } - if (a2.negative) { - a2 = a2.neg(); - b2 = b2.neg(); - } - - return [ - { a: a1, b: b1 }, - { a: a2, b: b2 }, - ]; -}; - -ShortCurve.prototype._endoSplit = function _endoSplit(k) { - var basis = this.endo.basis; - var v1 = basis[0]; - var v2 = basis[1]; - - var c1 = v2.b.mul(k).divRound(this.n); - var c2 = v1.b.neg().mul(k).divRound(this.n); - - var p1 = c1.mul(v1.a); - var p2 = c2.mul(v2.a); - var q1 = c1.mul(v1.b); - var q2 = c2.mul(v2.b); - - // Calculate answer - var k1 = k.sub(p1).sub(p2); - var k2 = q1.add(q2).neg(); - return { k1: k1, k2: k2 }; -}; - -ShortCurve.prototype.pointFromX = function pointFromX(x, odd) { - x = new BN(x, 16); - if (!x.red) - x = x.toRed(this.red); - - var y2 = x.redSqr().redMul(x).redIAdd(x.redMul(this.a)).redIAdd(this.b); - var y = y2.redSqrt(); - if (y.redSqr().redSub(y2).cmp(this.zero) !== 0) - throw new Error('invalid point'); - - // XXX Is there any way to tell if the number is odd without converting it - // to non-red form? - var isOdd = y.fromRed().isOdd(); - if (odd && !isOdd || !odd && isOdd) - y = y.redNeg(); - - return this.point(x, y); -}; - -ShortCurve.prototype.validate = function validate(point) { - if (point.inf) - return true; - - var x = point.x; - var y = point.y; - - var ax = this.a.redMul(x); - var rhs = x.redSqr().redMul(x).redIAdd(ax).redIAdd(this.b); - return y.redSqr().redISub(rhs).cmpn(0) === 0; -}; - -ShortCurve.prototype._endoWnafMulAdd = - function _endoWnafMulAdd(points, coeffs, jacobianResult) { - var npoints = this._endoWnafT1; - var ncoeffs = this._endoWnafT2; - for (var i = 0; i < points.length; i++) { - var split = this._endoSplit(coeffs[i]); - var p = points[i]; - var beta = p._getBeta(); - - if (split.k1.negative) { - split.k1.ineg(); - p = p.neg(true); - } - if (split.k2.negative) { - split.k2.ineg(); - beta = beta.neg(true); - } - - npoints[i * 2] = p; - npoints[i * 2 + 1] = beta; - ncoeffs[i * 2] = split.k1; - ncoeffs[i * 2 + 1] = split.k2; - } - var res = this._wnafMulAdd(1, npoints, ncoeffs, i * 2, jacobianResult); - - // Clean-up references to points and coefficients - for (var j = 0; j < i * 2; j++) { - npoints[j] = null; - ncoeffs[j] = null; - } - return res; - }; - -function Point(curve, x, y, isRed) { - Base.BasePoint.call(this, curve, 'affine'); - if (x === null && y === null) { - this.x = null; - this.y = null; - this.inf = true; - } else { - this.x = new BN(x, 16); - this.y = new BN(y, 16); - // Force redgomery representation when loading from JSON - if (isRed) { - this.x.forceRed(this.curve.red); - this.y.forceRed(this.curve.red); - } - if (!this.x.red) - this.x = this.x.toRed(this.curve.red); - if (!this.y.red) - this.y = this.y.toRed(this.curve.red); - this.inf = false; - } -} -inherits(Point, Base.BasePoint); - -ShortCurve.prototype.point = function point(x, y, isRed) { - return new Point(this, x, y, isRed); -}; - -ShortCurve.prototype.pointFromJSON = function pointFromJSON(obj, red) { - return Point.fromJSON(this, obj, red); -}; - -Point.prototype._getBeta = function _getBeta() { - if (!this.curve.endo) - return; - - var pre = this.precomputed; - if (pre && pre.beta) - return pre.beta; - - var beta = this.curve.point(this.x.redMul(this.curve.endo.beta), this.y); - if (pre) { - var curve = this.curve; - var endoMul = function(p) { - return curve.point(p.x.redMul(curve.endo.beta), p.y); - }; - pre.beta = beta; - beta.precomputed = { - beta: null, - naf: pre.naf && { - wnd: pre.naf.wnd, - points: pre.naf.points.map(endoMul), - }, - doubles: pre.doubles && { - step: pre.doubles.step, - points: pre.doubles.points.map(endoMul), - }, - }; - } - return beta; -}; - -Point.prototype.toJSON = function toJSON() { - if (!this.precomputed) - return [ this.x, this.y ]; - - return [ this.x, this.y, this.precomputed && { - doubles: this.precomputed.doubles && { - step: this.precomputed.doubles.step, - points: this.precomputed.doubles.points.slice(1), - }, - naf: this.precomputed.naf && { - wnd: this.precomputed.naf.wnd, - points: this.precomputed.naf.points.slice(1), - }, - } ]; -}; - -Point.fromJSON = function fromJSON(curve, obj, red) { - if (typeof obj === 'string') - obj = JSON.parse(obj); - var res = curve.point(obj[0], obj[1], red); - if (!obj[2]) - return res; - - function obj2point(obj) { - return curve.point(obj[0], obj[1], red); - } - - var pre = obj[2]; - res.precomputed = { - beta: null, - doubles: pre.doubles && { - step: pre.doubles.step, - points: [ res ].concat(pre.doubles.points.map(obj2point)), - }, - naf: pre.naf && { - wnd: pre.naf.wnd, - points: [ res ].concat(pre.naf.points.map(obj2point)), - }, - }; - return res; -}; - -Point.prototype.inspect = function inspect() { - if (this.isInfinity()) - return ''; - return ''; -}; - -Point.prototype.isInfinity = function isInfinity() { - return this.inf; -}; - -Point.prototype.add = function add(p) { - // O + P = P - if (this.inf) - return p; - - // P + O = P - if (p.inf) - return this; - - // P + P = 2P - if (this.eq(p)) - return this.dbl(); - - // P + (-P) = O - if (this.neg().eq(p)) - return this.curve.point(null, null); - - // P + Q = O - if (this.x.cmp(p.x) === 0) - return this.curve.point(null, null); - - var c = this.y.redSub(p.y); - if (c.cmpn(0) !== 0) - c = c.redMul(this.x.redSub(p.x).redInvm()); - var nx = c.redSqr().redISub(this.x).redISub(p.x); - var ny = c.redMul(this.x.redSub(nx)).redISub(this.y); - return this.curve.point(nx, ny); -}; - -Point.prototype.dbl = function dbl() { - if (this.inf) - return this; - - // 2P = O - var ys1 = this.y.redAdd(this.y); - if (ys1.cmpn(0) === 0) - return this.curve.point(null, null); - - var a = this.curve.a; - - var x2 = this.x.redSqr(); - var dyinv = ys1.redInvm(); - var c = x2.redAdd(x2).redIAdd(x2).redIAdd(a).redMul(dyinv); - - var nx = c.redSqr().redISub(this.x.redAdd(this.x)); - var ny = c.redMul(this.x.redSub(nx)).redISub(this.y); - return this.curve.point(nx, ny); -}; - -Point.prototype.getX = function getX() { - return this.x.fromRed(); -}; - -Point.prototype.getY = function getY() { - return this.y.fromRed(); -}; - -Point.prototype.mul = function mul(k) { - k = new BN(k, 16); - if (this.isInfinity()) - return this; - else if (this._hasDoubles(k)) - return this.curve._fixedNafMul(this, k); - else if (this.curve.endo) - return this.curve._endoWnafMulAdd([ this ], [ k ]); - else - return this.curve._wnafMul(this, k); -}; - -Point.prototype.mulAdd = function mulAdd(k1, p2, k2) { - var points = [ this, p2 ]; - var coeffs = [ k1, k2 ]; - if (this.curve.endo) - return this.curve._endoWnafMulAdd(points, coeffs); - else - return this.curve._wnafMulAdd(1, points, coeffs, 2); -}; - -Point.prototype.jmulAdd = function jmulAdd(k1, p2, k2) { - var points = [ this, p2 ]; - var coeffs = [ k1, k2 ]; - if (this.curve.endo) - return this.curve._endoWnafMulAdd(points, coeffs, true); - else - return this.curve._wnafMulAdd(1, points, coeffs, 2, true); -}; - -Point.prototype.eq = function eq(p) { - return this === p || - this.inf === p.inf && - (this.inf || this.x.cmp(p.x) === 0 && this.y.cmp(p.y) === 0); -}; - -Point.prototype.neg = function neg(_precompute) { - if (this.inf) - return this; - - var res = this.curve.point(this.x, this.y.redNeg()); - if (_precompute && this.precomputed) { - var pre = this.precomputed; - var negate = function(p) { - return p.neg(); - }; - res.precomputed = { - naf: pre.naf && { - wnd: pre.naf.wnd, - points: pre.naf.points.map(negate), - }, - doubles: pre.doubles && { - step: pre.doubles.step, - points: pre.doubles.points.map(negate), - }, - }; - } - return res; -}; - -Point.prototype.toJ = function toJ() { - if (this.inf) - return this.curve.jpoint(null, null, null); - - var res = this.curve.jpoint(this.x, this.y, this.curve.one); - return res; -}; - -function JPoint(curve, x, y, z) { - Base.BasePoint.call(this, curve, 'jacobian'); - if (x === null && y === null && z === null) { - this.x = this.curve.one; - this.y = this.curve.one; - this.z = new BN(0); - } else { - this.x = new BN(x, 16); - this.y = new BN(y, 16); - this.z = new BN(z, 16); - } - if (!this.x.red) - this.x = this.x.toRed(this.curve.red); - if (!this.y.red) - this.y = this.y.toRed(this.curve.red); - if (!this.z.red) - this.z = this.z.toRed(this.curve.red); - - this.zOne = this.z === this.curve.one; -} -inherits(JPoint, Base.BasePoint); - -ShortCurve.prototype.jpoint = function jpoint(x, y, z) { - return new JPoint(this, x, y, z); -}; - -JPoint.prototype.toP = function toP() { - if (this.isInfinity()) - return this.curve.point(null, null); - - var zinv = this.z.redInvm(); - var zinv2 = zinv.redSqr(); - var ax = this.x.redMul(zinv2); - var ay = this.y.redMul(zinv2).redMul(zinv); - - return this.curve.point(ax, ay); -}; - -JPoint.prototype.neg = function neg() { - return this.curve.jpoint(this.x, this.y.redNeg(), this.z); -}; - -JPoint.prototype.add = function add(p) { - // O + P = P - if (this.isInfinity()) - return p; - - // P + O = P - if (p.isInfinity()) - return this; - - // 12M + 4S + 7A - var pz2 = p.z.redSqr(); - var z2 = this.z.redSqr(); - var u1 = this.x.redMul(pz2); - var u2 = p.x.redMul(z2); - var s1 = this.y.redMul(pz2.redMul(p.z)); - var s2 = p.y.redMul(z2.redMul(this.z)); - - var h = u1.redSub(u2); - var r = s1.redSub(s2); - if (h.cmpn(0) === 0) { - if (r.cmpn(0) !== 0) - return this.curve.jpoint(null, null, null); - else - return this.dbl(); - } - - var h2 = h.redSqr(); - var h3 = h2.redMul(h); - var v = u1.redMul(h2); - - var nx = r.redSqr().redIAdd(h3).redISub(v).redISub(v); - var ny = r.redMul(v.redISub(nx)).redISub(s1.redMul(h3)); - var nz = this.z.redMul(p.z).redMul(h); - - return this.curve.jpoint(nx, ny, nz); -}; - -JPoint.prototype.mixedAdd = function mixedAdd(p) { - // O + P = P - if (this.isInfinity()) - return p.toJ(); - - // P + O = P - if (p.isInfinity()) - return this; - - // 8M + 3S + 7A - var z2 = this.z.redSqr(); - var u1 = this.x; - var u2 = p.x.redMul(z2); - var s1 = this.y; - var s2 = p.y.redMul(z2).redMul(this.z); - - var h = u1.redSub(u2); - var r = s1.redSub(s2); - if (h.cmpn(0) === 0) { - if (r.cmpn(0) !== 0) - return this.curve.jpoint(null, null, null); - else - return this.dbl(); - } - - var h2 = h.redSqr(); - var h3 = h2.redMul(h); - var v = u1.redMul(h2); - - var nx = r.redSqr().redIAdd(h3).redISub(v).redISub(v); - var ny = r.redMul(v.redISub(nx)).redISub(s1.redMul(h3)); - var nz = this.z.redMul(h); - - return this.curve.jpoint(nx, ny, nz); -}; - -JPoint.prototype.dblp = function dblp(pow) { - if (pow === 0) - return this; - if (this.isInfinity()) - return this; - if (!pow) - return this.dbl(); - - var i; - if (this.curve.zeroA || this.curve.threeA) { - var r = this; - for (i = 0; i < pow; i++) - r = r.dbl(); - return r; - } - - // 1M + 2S + 1A + N * (4S + 5M + 8A) - // N = 1 => 6M + 6S + 9A - var a = this.curve.a; - var tinv = this.curve.tinv; - - var jx = this.x; - var jy = this.y; - var jz = this.z; - var jz4 = jz.redSqr().redSqr(); - - // Reuse results - var jyd = jy.redAdd(jy); - for (i = 0; i < pow; i++) { - var jx2 = jx.redSqr(); - var jyd2 = jyd.redSqr(); - var jyd4 = jyd2.redSqr(); - var c = jx2.redAdd(jx2).redIAdd(jx2).redIAdd(a.redMul(jz4)); - - var t1 = jx.redMul(jyd2); - var nx = c.redSqr().redISub(t1.redAdd(t1)); - var t2 = t1.redISub(nx); - var dny = c.redMul(t2); - dny = dny.redIAdd(dny).redISub(jyd4); - var nz = jyd.redMul(jz); - if (i + 1 < pow) - jz4 = jz4.redMul(jyd4); - - jx = nx; - jz = nz; - jyd = dny; - } - - return this.curve.jpoint(jx, jyd.redMul(tinv), jz); -}; - -JPoint.prototype.dbl = function dbl() { - if (this.isInfinity()) - return this; - - if (this.curve.zeroA) - return this._zeroDbl(); - else if (this.curve.threeA) - return this._threeDbl(); - else - return this._dbl(); -}; - -JPoint.prototype._zeroDbl = function _zeroDbl() { - var nx; - var ny; - var nz; - // Z = 1 - if (this.zOne) { - // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html - // #doubling-mdbl-2007-bl - // 1M + 5S + 14A - - // XX = X1^2 - var xx = this.x.redSqr(); - // YY = Y1^2 - var yy = this.y.redSqr(); - // YYYY = YY^2 - var yyyy = yy.redSqr(); - // S = 2 * ((X1 + YY)^2 - XX - YYYY) - var s = this.x.redAdd(yy).redSqr().redISub(xx).redISub(yyyy); - s = s.redIAdd(s); - // M = 3 * XX + a; a = 0 - var m = xx.redAdd(xx).redIAdd(xx); - // T = M ^ 2 - 2*S - var t = m.redSqr().redISub(s).redISub(s); - - // 8 * YYYY - var yyyy8 = yyyy.redIAdd(yyyy); - yyyy8 = yyyy8.redIAdd(yyyy8); - yyyy8 = yyyy8.redIAdd(yyyy8); - - // X3 = T - nx = t; - // Y3 = M * (S - T) - 8 * YYYY - ny = m.redMul(s.redISub(t)).redISub(yyyy8); - // Z3 = 2*Y1 - nz = this.y.redAdd(this.y); - } else { - // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html - // #doubling-dbl-2009-l - // 2M + 5S + 13A - - // A = X1^2 - var a = this.x.redSqr(); - // B = Y1^2 - var b = this.y.redSqr(); - // C = B^2 - var c = b.redSqr(); - // D = 2 * ((X1 + B)^2 - A - C) - var d = this.x.redAdd(b).redSqr().redISub(a).redISub(c); - d = d.redIAdd(d); - // E = 3 * A - var e = a.redAdd(a).redIAdd(a); - // F = E^2 - var f = e.redSqr(); - - // 8 * C - var c8 = c.redIAdd(c); - c8 = c8.redIAdd(c8); - c8 = c8.redIAdd(c8); - - // X3 = F - 2 * D - nx = f.redISub(d).redISub(d); - // Y3 = E * (D - X3) - 8 * C - ny = e.redMul(d.redISub(nx)).redISub(c8); - // Z3 = 2 * Y1 * Z1 - nz = this.y.redMul(this.z); - nz = nz.redIAdd(nz); - } - - return this.curve.jpoint(nx, ny, nz); -}; - -JPoint.prototype._threeDbl = function _threeDbl() { - var nx; - var ny; - var nz; - // Z = 1 - if (this.zOne) { - // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html - // #doubling-mdbl-2007-bl - // 1M + 5S + 15A - - // XX = X1^2 - var xx = this.x.redSqr(); - // YY = Y1^2 - var yy = this.y.redSqr(); - // YYYY = YY^2 - var yyyy = yy.redSqr(); - // S = 2 * ((X1 + YY)^2 - XX - YYYY) - var s = this.x.redAdd(yy).redSqr().redISub(xx).redISub(yyyy); - s = s.redIAdd(s); - // M = 3 * XX + a - var m = xx.redAdd(xx).redIAdd(xx).redIAdd(this.curve.a); - // T = M^2 - 2 * S - var t = m.redSqr().redISub(s).redISub(s); - // X3 = T - nx = t; - // Y3 = M * (S - T) - 8 * YYYY - var yyyy8 = yyyy.redIAdd(yyyy); - yyyy8 = yyyy8.redIAdd(yyyy8); - yyyy8 = yyyy8.redIAdd(yyyy8); - ny = m.redMul(s.redISub(t)).redISub(yyyy8); - // Z3 = 2 * Y1 - nz = this.y.redAdd(this.y); - } else { - // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2001-b - // 3M + 5S - - // delta = Z1^2 - var delta = this.z.redSqr(); - // gamma = Y1^2 - var gamma = this.y.redSqr(); - // beta = X1 * gamma - var beta = this.x.redMul(gamma); - // alpha = 3 * (X1 - delta) * (X1 + delta) - var alpha = this.x.redSub(delta).redMul(this.x.redAdd(delta)); - alpha = alpha.redAdd(alpha).redIAdd(alpha); - // X3 = alpha^2 - 8 * beta - var beta4 = beta.redIAdd(beta); - beta4 = beta4.redIAdd(beta4); - var beta8 = beta4.redAdd(beta4); - nx = alpha.redSqr().redISub(beta8); - // Z3 = (Y1 + Z1)^2 - gamma - delta - nz = this.y.redAdd(this.z).redSqr().redISub(gamma).redISub(delta); - // Y3 = alpha * (4 * beta - X3) - 8 * gamma^2 - var ggamma8 = gamma.redSqr(); - ggamma8 = ggamma8.redIAdd(ggamma8); - ggamma8 = ggamma8.redIAdd(ggamma8); - ggamma8 = ggamma8.redIAdd(ggamma8); - ny = alpha.redMul(beta4.redISub(nx)).redISub(ggamma8); - } - - return this.curve.jpoint(nx, ny, nz); -}; - -JPoint.prototype._dbl = function _dbl() { - var a = this.curve.a; - - // 4M + 6S + 10A - var jx = this.x; - var jy = this.y; - var jz = this.z; - var jz4 = jz.redSqr().redSqr(); - - var jx2 = jx.redSqr(); - var jy2 = jy.redSqr(); - - var c = jx2.redAdd(jx2).redIAdd(jx2).redIAdd(a.redMul(jz4)); - - var jxd4 = jx.redAdd(jx); - jxd4 = jxd4.redIAdd(jxd4); - var t1 = jxd4.redMul(jy2); - var nx = c.redSqr().redISub(t1.redAdd(t1)); - var t2 = t1.redISub(nx); - - var jyd8 = jy2.redSqr(); - jyd8 = jyd8.redIAdd(jyd8); - jyd8 = jyd8.redIAdd(jyd8); - jyd8 = jyd8.redIAdd(jyd8); - var ny = c.redMul(t2).redISub(jyd8); - var nz = jy.redAdd(jy).redMul(jz); - - return this.curve.jpoint(nx, ny, nz); -}; - -JPoint.prototype.trpl = function trpl() { - if (!this.curve.zeroA) - return this.dbl().add(this); - - // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#tripling-tpl-2007-bl - // 5M + 10S + ... - - // XX = X1^2 - var xx = this.x.redSqr(); - // YY = Y1^2 - var yy = this.y.redSqr(); - // ZZ = Z1^2 - var zz = this.z.redSqr(); - // YYYY = YY^2 - var yyyy = yy.redSqr(); - // M = 3 * XX + a * ZZ2; a = 0 - var m = xx.redAdd(xx).redIAdd(xx); - // MM = M^2 - var mm = m.redSqr(); - // E = 6 * ((X1 + YY)^2 - XX - YYYY) - MM - var e = this.x.redAdd(yy).redSqr().redISub(xx).redISub(yyyy); - e = e.redIAdd(e); - e = e.redAdd(e).redIAdd(e); - e = e.redISub(mm); - // EE = E^2 - var ee = e.redSqr(); - // T = 16*YYYY - var t = yyyy.redIAdd(yyyy); - t = t.redIAdd(t); - t = t.redIAdd(t); - t = t.redIAdd(t); - // U = (M + E)^2 - MM - EE - T - var u = m.redIAdd(e).redSqr().redISub(mm).redISub(ee).redISub(t); - // X3 = 4 * (X1 * EE - 4 * YY * U) - var yyu4 = yy.redMul(u); - yyu4 = yyu4.redIAdd(yyu4); - yyu4 = yyu4.redIAdd(yyu4); - var nx = this.x.redMul(ee).redISub(yyu4); - nx = nx.redIAdd(nx); - nx = nx.redIAdd(nx); - // Y3 = 8 * Y1 * (U * (T - U) - E * EE) - var ny = this.y.redMul(u.redMul(t.redISub(u)).redISub(e.redMul(ee))); - ny = ny.redIAdd(ny); - ny = ny.redIAdd(ny); - ny = ny.redIAdd(ny); - // Z3 = (Z1 + E)^2 - ZZ - EE - var nz = this.z.redAdd(e).redSqr().redISub(zz).redISub(ee); - - return this.curve.jpoint(nx, ny, nz); -}; - -JPoint.prototype.mul = function mul(k, kbase) { - k = new BN(k, kbase); - - return this.curve._wnafMul(this, k); -}; - -JPoint.prototype.eq = function eq(p) { - if (p.type === 'affine') - return this.eq(p.toJ()); - - if (this === p) - return true; - - // x1 * z2^2 == x2 * z1^2 - var z2 = this.z.redSqr(); - var pz2 = p.z.redSqr(); - if (this.x.redMul(pz2).redISub(p.x.redMul(z2)).cmpn(0) !== 0) - return false; - - // y1 * z2^3 == y2 * z1^3 - var z3 = z2.redMul(this.z); - var pz3 = pz2.redMul(p.z); - return this.y.redMul(pz3).redISub(p.y.redMul(z3)).cmpn(0) === 0; -}; - -JPoint.prototype.eqXToP = function eqXToP(x) { - var zs = this.z.redSqr(); - var rx = x.toRed(this.curve.red).redMul(zs); - if (this.x.cmp(rx) === 0) - return true; - - var xc = x.clone(); - var t = this.curve.redN.redMul(zs); - for (;;) { - xc.iadd(this.curve.n); - if (xc.cmp(this.curve.p) >= 0) - return false; - - rx.redIAdd(t); - if (this.x.cmp(rx) === 0) - return true; - } -}; - -JPoint.prototype.inspect = function inspect() { - if (this.isInfinity()) - return ''; - return ''; -}; - -JPoint.prototype.isInfinity = function isInfinity() { - // XXX This code assumes that zero is always zero in red - return this.z.cmpn(0) === 0; -}; - -},{"../utils":118,"./base":105,"bn.js":119,"inherits":155}],110:[function(require,module,exports){ -'use strict'; - -var curves = exports; - -var hash = require('hash.js'); -var curve = require('./curve'); -var utils = require('./utils'); - -var assert = utils.assert; - -function PresetCurve(options) { - if (options.type === 'short') - this.curve = new curve.short(options); - else if (options.type === 'edwards') - this.curve = new curve.edwards(options); - else - this.curve = new curve.mont(options); - this.g = this.curve.g; - this.n = this.curve.n; - this.hash = options.hash; - - assert(this.g.validate(), 'Invalid curve'); - assert(this.g.mul(this.n).isInfinity(), 'Invalid curve, G*N != O'); -} -curves.PresetCurve = PresetCurve; - -function defineCurve(name, options) { - Object.defineProperty(curves, name, { - configurable: true, - enumerable: true, - get: function() { - var curve = new PresetCurve(options); - Object.defineProperty(curves, name, { - configurable: true, - enumerable: true, - value: curve, - }); - return curve; - }, - }); -} - -defineCurve('p192', { - type: 'short', - prime: 'p192', - p: 'ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff', - a: 'ffffffff ffffffff ffffffff fffffffe ffffffff fffffffc', - b: '64210519 e59c80e7 0fa7e9ab 72243049 feb8deec c146b9b1', - n: 'ffffffff ffffffff ffffffff 99def836 146bc9b1 b4d22831', - hash: hash.sha256, - gRed: false, - g: [ - '188da80e b03090f6 7cbf20eb 43a18800 f4ff0afd 82ff1012', - '07192b95 ffc8da78 631011ed 6b24cdd5 73f977a1 1e794811', - ], -}); - -defineCurve('p224', { - type: 'short', - prime: 'p224', - p: 'ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001', - a: 'ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff fffffffe', - b: 'b4050a85 0c04b3ab f5413256 5044b0b7 d7bfd8ba 270b3943 2355ffb4', - n: 'ffffffff ffffffff ffffffff ffff16a2 e0b8f03e 13dd2945 5c5c2a3d', - hash: hash.sha256, - gRed: false, - g: [ - 'b70e0cbd 6bb4bf7f 321390b9 4a03c1d3 56c21122 343280d6 115c1d21', - 'bd376388 b5f723fb 4c22dfe6 cd4375a0 5a074764 44d58199 85007e34', - ], -}); - -defineCurve('p256', { - type: 'short', - prime: null, - p: 'ffffffff 00000001 00000000 00000000 00000000 ffffffff ffffffff ffffffff', - a: 'ffffffff 00000001 00000000 00000000 00000000 ffffffff ffffffff fffffffc', - b: '5ac635d8 aa3a93e7 b3ebbd55 769886bc 651d06b0 cc53b0f6 3bce3c3e 27d2604b', - n: 'ffffffff 00000000 ffffffff ffffffff bce6faad a7179e84 f3b9cac2 fc632551', - hash: hash.sha256, - gRed: false, - g: [ - '6b17d1f2 e12c4247 f8bce6e5 63a440f2 77037d81 2deb33a0 f4a13945 d898c296', - '4fe342e2 fe1a7f9b 8ee7eb4a 7c0f9e16 2bce3357 6b315ece cbb64068 37bf51f5', - ], -}); - -defineCurve('p384', { - type: 'short', - prime: null, - p: 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ' + - 'fffffffe ffffffff 00000000 00000000 ffffffff', - a: 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ' + - 'fffffffe ffffffff 00000000 00000000 fffffffc', - b: 'b3312fa7 e23ee7e4 988e056b e3f82d19 181d9c6e fe814112 0314088f ' + - '5013875a c656398d 8a2ed19d 2a85c8ed d3ec2aef', - n: 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff c7634d81 ' + - 'f4372ddf 581a0db2 48b0a77a ecec196a ccc52973', - hash: hash.sha384, - gRed: false, - g: [ - 'aa87ca22 be8b0537 8eb1c71e f320ad74 6e1d3b62 8ba79b98 59f741e0 82542a38 ' + - '5502f25d bf55296c 3a545e38 72760ab7', - '3617de4a 96262c6f 5d9e98bf 9292dc29 f8f41dbd 289a147c e9da3113 b5f0b8c0 ' + - '0a60b1ce 1d7e819d 7a431d7c 90ea0e5f', - ], -}); - -defineCurve('p521', { - type: 'short', - prime: null, - p: '000001ff ffffffff ffffffff ffffffff ffffffff ffffffff ' + - 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ' + - 'ffffffff ffffffff ffffffff ffffffff ffffffff', - a: '000001ff ffffffff ffffffff ffffffff ffffffff ffffffff ' + - 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ' + - 'ffffffff ffffffff ffffffff ffffffff fffffffc', - b: '00000051 953eb961 8e1c9a1f 929a21a0 b68540ee a2da725b ' + - '99b315f3 b8b48991 8ef109e1 56193951 ec7e937b 1652c0bd ' + - '3bb1bf07 3573df88 3d2c34f1 ef451fd4 6b503f00', - n: '000001ff ffffffff ffffffff ffffffff ffffffff ffffffff ' + - 'ffffffff ffffffff fffffffa 51868783 bf2f966b 7fcc0148 ' + - 'f709a5d0 3bb5c9b8 899c47ae bb6fb71e 91386409', - hash: hash.sha512, - gRed: false, - g: [ - '000000c6 858e06b7 0404e9cd 9e3ecb66 2395b442 9c648139 ' + - '053fb521 f828af60 6b4d3dba a14b5e77 efe75928 fe1dc127 ' + - 'a2ffa8de 3348b3c1 856a429b f97e7e31 c2e5bd66', - '00000118 39296a78 9a3bc004 5c8a5fb4 2c7d1bd9 98f54449 ' + - '579b4468 17afbd17 273e662c 97ee7299 5ef42640 c550b901 ' + - '3fad0761 353c7086 a272c240 88be9476 9fd16650', - ], -}); - -defineCurve('curve25519', { - type: 'mont', - prime: 'p25519', - p: '7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed', - a: '76d06', - b: '1', - n: '1000000000000000 0000000000000000 14def9dea2f79cd6 5812631a5cf5d3ed', - hash: hash.sha256, - gRed: false, - g: [ - '9', - ], -}); - -defineCurve('ed25519', { - type: 'edwards', - prime: 'p25519', - p: '7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed', - a: '-1', - c: '1', - // -121665 * (121666^(-1)) (mod P) - d: '52036cee2b6ffe73 8cc740797779e898 00700a4d4141d8ab 75eb4dca135978a3', - n: '1000000000000000 0000000000000000 14def9dea2f79cd6 5812631a5cf5d3ed', - hash: hash.sha256, - gRed: false, - g: [ - '216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51a', - - // 4/5 - '6666666666666666666666666666666666666666666666666666666666666658', - ], -}); - -var pre; -try { - pre = require('./precomputed/secp256k1'); -} catch (e) { - pre = undefined; -} - -defineCurve('secp256k1', { - type: 'short', - prime: 'k256', - p: 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f', - a: '0', - b: '7', - n: 'ffffffff ffffffff ffffffff fffffffe baaedce6 af48a03b bfd25e8c d0364141', - h: '1', - hash: hash.sha256, - - // Precomputed endomorphism - beta: '7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee', - lambda: '5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72', - basis: [ - { - a: '3086d221a7d46bcde86c90e49284eb15', - b: '-e4437ed6010e88286f547fa90abfe4c3', - }, - { - a: '114ca50f7a8e2f3f657c1108d9d44cfd8', - b: '3086d221a7d46bcde86c90e49284eb15', - }, - ], - - gRed: false, - g: [ - '79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798', - '483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8', - pre, - ], -}); - -},{"./curve":107,"./precomputed/secp256k1":117,"./utils":118,"hash.js":140}],111:[function(require,module,exports){ -'use strict'; - -var BN = require('bn.js'); -var HmacDRBG = require('hmac-drbg'); -var utils = require('../utils'); -var curves = require('../curves'); -var rand = require('brorand'); -var assert = utils.assert; - -var KeyPair = require('./key'); -var Signature = require('./signature'); - -function EC(options) { - if (!(this instanceof EC)) - return new EC(options); - - // Shortcut `elliptic.ec(curve-name)` - if (typeof options === 'string') { - assert(Object.prototype.hasOwnProperty.call(curves, options), - 'Unknown curve ' + options); - - options = curves[options]; - } - - // Shortcut for `elliptic.ec(elliptic.curves.curveName)` - if (options instanceof curves.PresetCurve) - options = { curve: options }; - - this.curve = options.curve.curve; - this.n = this.curve.n; - this.nh = this.n.ushrn(1); - this.g = this.curve.g; - - // Point on curve - this.g = options.curve.g; - this.g.precompute(options.curve.n.bitLength() + 1); - - // Hash for function for DRBG - this.hash = options.hash || options.curve.hash; -} -module.exports = EC; - -EC.prototype.keyPair = function keyPair(options) { - return new KeyPair(this, options); -}; - -EC.prototype.keyFromPrivate = function keyFromPrivate(priv, enc) { - return KeyPair.fromPrivate(this, priv, enc); -}; - -EC.prototype.keyFromPublic = function keyFromPublic(pub, enc) { - return KeyPair.fromPublic(this, pub, enc); -}; - -EC.prototype.genKeyPair = function genKeyPair(options) { - if (!options) - options = {}; - - // Instantiate Hmac_DRBG - var drbg = new HmacDRBG({ - hash: this.hash, - pers: options.pers, - persEnc: options.persEnc || 'utf8', - entropy: options.entropy || rand(this.hash.hmacStrength), - entropyEnc: options.entropy && options.entropyEnc || 'utf8', - nonce: this.n.toArray(), - }); - - var bytes = this.n.byteLength(); - var ns2 = this.n.sub(new BN(2)); - for (;;) { - var priv = new BN(drbg.generate(bytes)); - if (priv.cmp(ns2) > 0) - continue; - - priv.iaddn(1); - return this.keyFromPrivate(priv); - } -}; - -EC.prototype._truncateToN = function _truncateToN(msg, truncOnly) { - var delta = msg.byteLength() * 8 - this.n.bitLength(); - if (delta > 0) - msg = msg.ushrn(delta); - if (!truncOnly && msg.cmp(this.n) >= 0) - return msg.sub(this.n); - else - return msg; -}; - -EC.prototype.sign = function sign(msg, key, enc, options) { - if (typeof enc === 'object') { - options = enc; - enc = null; - } - if (!options) - options = {}; - - key = this.keyFromPrivate(key, enc); - msg = this._truncateToN(new BN(msg, 16)); - - // Zero-extend key to provide enough entropy - var bytes = this.n.byteLength(); - var bkey = key.getPrivate().toArray('be', bytes); - - // Zero-extend nonce to have the same byte size as N - var nonce = msg.toArray('be', bytes); - - // Instantiate Hmac_DRBG - var drbg = new HmacDRBG({ - hash: this.hash, - entropy: bkey, - nonce: nonce, - pers: options.pers, - persEnc: options.persEnc || 'utf8', - }); - - // Number of bytes to generate - var ns1 = this.n.sub(new BN(1)); - - for (var iter = 0; ; iter++) { - var k = options.k ? - options.k(iter) : - new BN(drbg.generate(this.n.byteLength())); - k = this._truncateToN(k, true); - if (k.cmpn(1) <= 0 || k.cmp(ns1) >= 0) - continue; - - var kp = this.g.mul(k); - if (kp.isInfinity()) - continue; - - var kpX = kp.getX(); - var r = kpX.umod(this.n); - if (r.cmpn(0) === 0) - continue; - - var s = k.invm(this.n).mul(r.mul(key.getPrivate()).iadd(msg)); - s = s.umod(this.n); - if (s.cmpn(0) === 0) - continue; - - var recoveryParam = (kp.getY().isOdd() ? 1 : 0) | - (kpX.cmp(r) !== 0 ? 2 : 0); - - // Use complement of `s`, if it is > `n / 2` - if (options.canonical && s.cmp(this.nh) > 0) { - s = this.n.sub(s); - recoveryParam ^= 1; - } - - return new Signature({ r: r, s: s, recoveryParam: recoveryParam }); - } -}; - -EC.prototype.verify = function verify(msg, signature, key, enc) { - msg = this._truncateToN(new BN(msg, 16)); - key = this.keyFromPublic(key, enc); - signature = new Signature(signature, 'hex'); - - // Perform primitive values validation - var r = signature.r; - var s = signature.s; - if (r.cmpn(1) < 0 || r.cmp(this.n) >= 0) - return false; - if (s.cmpn(1) < 0 || s.cmp(this.n) >= 0) - return false; - - // Validate signature - var sinv = s.invm(this.n); - var u1 = sinv.mul(msg).umod(this.n); - var u2 = sinv.mul(r).umod(this.n); - var p; - - if (!this.curve._maxwellTrick) { - p = this.g.mulAdd(u1, key.getPublic(), u2); - if (p.isInfinity()) - return false; - - return p.getX().umod(this.n).cmp(r) === 0; - } - - // NOTE: Greg Maxwell's trick, inspired by: - // https://git.io/vad3K - - p = this.g.jmulAdd(u1, key.getPublic(), u2); - if (p.isInfinity()) - return false; - - // Compare `p.x` of Jacobian point with `r`, - // this will do `p.x == r * p.z^2` instead of multiplying `p.x` by the - // inverse of `p.z^2` - return p.eqXToP(r); -}; - -EC.prototype.recoverPubKey = function(msg, signature, j, enc) { - assert((3 & j) === j, 'The recovery param is more than two bits'); - signature = new Signature(signature, enc); - - var n = this.n; - var e = new BN(msg); - var r = signature.r; - var s = signature.s; - - // A set LSB signifies that the y-coordinate is odd - var isYOdd = j & 1; - var isSecondKey = j >> 1; - if (r.cmp(this.curve.p.umod(this.curve.n)) >= 0 && isSecondKey) - throw new Error('Unable to find sencond key candinate'); - - // 1.1. Let x = r + jn. - if (isSecondKey) - r = this.curve.pointFromX(r.add(this.curve.n), isYOdd); - else - r = this.curve.pointFromX(r, isYOdd); - - var rInv = signature.r.invm(n); - var s1 = n.sub(e).mul(rInv).umod(n); - var s2 = s.mul(rInv).umod(n); - - // 1.6.1 Compute Q = r^-1 (sR - eG) - // Q = r^-1 (sR + -eG) - return this.g.mulAdd(s1, r, s2); -}; - -EC.prototype.getKeyRecoveryParam = function(e, signature, Q, enc) { - signature = new Signature(signature, enc); - if (signature.recoveryParam !== null) - return signature.recoveryParam; - - for (var i = 0; i < 4; i++) { - var Qprime; - try { - Qprime = this.recoverPubKey(e, signature, i); - } catch (e) { - continue; - } - - if (Qprime.eq(Q)) - return i; - } - throw new Error('Unable to find valid recovery factor'); -}; - -},{"../curves":110,"../utils":118,"./key":112,"./signature":113,"bn.js":119,"brorand":33,"hmac-drbg":153}],112:[function(require,module,exports){ -'use strict'; - -var BN = require('bn.js'); -var utils = require('../utils'); -var assert = utils.assert; - -function KeyPair(ec, options) { - this.ec = ec; - this.priv = null; - this.pub = null; - - // KeyPair(ec, { priv: ..., pub: ... }) - if (options.priv) - this._importPrivate(options.priv, options.privEnc); - if (options.pub) - this._importPublic(options.pub, options.pubEnc); -} -module.exports = KeyPair; - -KeyPair.fromPublic = function fromPublic(ec, pub, enc) { - if (pub instanceof KeyPair) - return pub; - - return new KeyPair(ec, { - pub: pub, - pubEnc: enc, - }); -}; - -KeyPair.fromPrivate = function fromPrivate(ec, priv, enc) { - if (priv instanceof KeyPair) - return priv; - - return new KeyPair(ec, { - priv: priv, - privEnc: enc, - }); -}; - -KeyPair.prototype.validate = function validate() { - var pub = this.getPublic(); - - if (pub.isInfinity()) - return { result: false, reason: 'Invalid public key' }; - if (!pub.validate()) - return { result: false, reason: 'Public key is not a point' }; - if (!pub.mul(this.ec.curve.n).isInfinity()) - return { result: false, reason: 'Public key * N != O' }; - - return { result: true, reason: null }; -}; - -KeyPair.prototype.getPublic = function getPublic(compact, enc) { - // compact is optional argument - if (typeof compact === 'string') { - enc = compact; - compact = null; - } - - if (!this.pub) - this.pub = this.ec.g.mul(this.priv); - - if (!enc) - return this.pub; - - return this.pub.encode(enc, compact); -}; - -KeyPair.prototype.getPrivate = function getPrivate(enc) { - if (enc === 'hex') - return this.priv.toString(16, 2); - else - return this.priv; -}; - -KeyPair.prototype._importPrivate = function _importPrivate(key, enc) { - this.priv = new BN(key, enc || 16); - - // Ensure that the priv won't be bigger than n, otherwise we may fail - // in fixed multiplication method - this.priv = this.priv.umod(this.ec.curve.n); -}; - -KeyPair.prototype._importPublic = function _importPublic(key, enc) { - if (key.x || key.y) { - // Montgomery points only have an `x` coordinate. - // Weierstrass/Edwards points on the other hand have both `x` and - // `y` coordinates. - if (this.ec.curve.type === 'mont') { - assert(key.x, 'Need x coordinate'); - } else if (this.ec.curve.type === 'short' || - this.ec.curve.type === 'edwards') { - assert(key.x && key.y, 'Need both x and y coordinate'); - } - this.pub = this.ec.curve.point(key.x, key.y); - return; - } - this.pub = this.ec.curve.decodePoint(key, enc); -}; - -// ECDH -KeyPair.prototype.derive = function derive(pub) { - if(!pub.validate()) { - assert(pub.validate(), 'public point not validated'); - } - return pub.mul(this.priv).getX(); -}; - -// ECDSA -KeyPair.prototype.sign = function sign(msg, enc, options) { - return this.ec.sign(msg, this, enc, options); -}; - -KeyPair.prototype.verify = function verify(msg, signature) { - return this.ec.verify(msg, signature, this); -}; - -KeyPair.prototype.inspect = function inspect() { - return ''; -}; - -},{"../utils":118,"bn.js":119}],113:[function(require,module,exports){ -'use strict'; - -var BN = require('bn.js'); - -var utils = require('../utils'); -var assert = utils.assert; - -function Signature(options, enc) { - if (options instanceof Signature) - return options; - - if (this._importDER(options, enc)) - return; - - assert(options.r && options.s, 'Signature without r or s'); - this.r = new BN(options.r, 16); - this.s = new BN(options.s, 16); - if (options.recoveryParam === undefined) - this.recoveryParam = null; - else - this.recoveryParam = options.recoveryParam; -} -module.exports = Signature; - -function Position() { - this.place = 0; -} - -function getLength(buf, p) { - var initial = buf[p.place++]; - if (!(initial & 0x80)) { - return initial; - } - var octetLen = initial & 0xf; - - // Indefinite length or overflow - if (octetLen === 0 || octetLen > 4) { - return false; - } - - var val = 0; - for (var i = 0, off = p.place; i < octetLen; i++, off++) { - val <<= 8; - val |= buf[off]; - val >>>= 0; - } - - // Leading zeroes - if (val <= 0x7f) { - return false; - } - - p.place = off; - return val; -} - -function rmPadding(buf) { - var i = 0; - var len = buf.length - 1; - while (!buf[i] && !(buf[i + 1] & 0x80) && i < len) { - i++; - } - if (i === 0) { - return buf; - } - return buf.slice(i); -} - -Signature.prototype._importDER = function _importDER(data, enc) { - data = utils.toArray(data, enc); - var p = new Position(); - if (data[p.place++] !== 0x30) { - return false; - } - var len = getLength(data, p); - if (len === false) { - return false; - } - if ((len + p.place) !== data.length) { - return false; - } - if (data[p.place++] !== 0x02) { - return false; - } - var rlen = getLength(data, p); - if (rlen === false) { - return false; - } - var r = data.slice(p.place, rlen + p.place); - p.place += rlen; - if (data[p.place++] !== 0x02) { - return false; - } - var slen = getLength(data, p); - if (slen === false) { - return false; - } - if (data.length !== slen + p.place) { - return false; - } - var s = data.slice(p.place, slen + p.place); - if (r[0] === 0) { - if (r[1] & 0x80) { - r = r.slice(1); - } else { - // Leading zeroes - return false; - } - } - if (s[0] === 0) { - if (s[1] & 0x80) { - s = s.slice(1); - } else { - // Leading zeroes - return false; - } - } - - this.r = new BN(r); - this.s = new BN(s); - this.recoveryParam = null; - - return true; -}; - -function constructLength(arr, len) { - if (len < 0x80) { - arr.push(len); - return; - } - var octets = 1 + (Math.log(len) / Math.LN2 >>> 3); - arr.push(octets | 0x80); - while (--octets) { - arr.push((len >>> (octets << 3)) & 0xff); - } - arr.push(len); -} - -Signature.prototype.toDER = function toDER(enc) { - var r = this.r.toArray(); - var s = this.s.toArray(); - - // Pad values - if (r[0] & 0x80) - r = [ 0 ].concat(r); - // Pad values - if (s[0] & 0x80) - s = [ 0 ].concat(s); - - r = rmPadding(r); - s = rmPadding(s); - - while (!s[0] && !(s[1] & 0x80)) { - s = s.slice(1); - } - var arr = [ 0x02 ]; - constructLength(arr, r.length); - arr = arr.concat(r); - arr.push(0x02); - constructLength(arr, s.length); - var backHalf = arr.concat(s); - var res = [ 0x30 ]; - constructLength(res, backHalf.length); - res = res.concat(backHalf); - return utils.encode(res, enc); -}; - -},{"../utils":118,"bn.js":119}],114:[function(require,module,exports){ -'use strict'; - -var hash = require('hash.js'); -var curves = require('../curves'); -var utils = require('../utils'); -var assert = utils.assert; -var parseBytes = utils.parseBytes; -var KeyPair = require('./key'); -var Signature = require('./signature'); - -function EDDSA(curve) { - assert(curve === 'ed25519', 'only tested with ed25519 so far'); - - if (!(this instanceof EDDSA)) - return new EDDSA(curve); - - curve = curves[curve].curve; - this.curve = curve; - this.g = curve.g; - this.g.precompute(curve.n.bitLength() + 1); - - this.pointClass = curve.point().constructor; - this.encodingLength = Math.ceil(curve.n.bitLength() / 8); - this.hash = hash.sha512; -} - -module.exports = EDDSA; - -/** -* @param {Array|String} message - message bytes -* @param {Array|String|KeyPair} secret - secret bytes or a keypair -* @returns {Signature} - signature -*/ -EDDSA.prototype.sign = function sign(message, secret) { - message = parseBytes(message); - var key = this.keyFromSecret(secret); - var r = this.hashInt(key.messagePrefix(), message); - var R = this.g.mul(r); - var Rencoded = this.encodePoint(R); - var s_ = this.hashInt(Rencoded, key.pubBytes(), message) - .mul(key.priv()); - var S = r.add(s_).umod(this.curve.n); - return this.makeSignature({ R: R, S: S, Rencoded: Rencoded }); -}; - -/** -* @param {Array} message - message bytes -* @param {Array|String|Signature} sig - sig bytes -* @param {Array|String|Point|KeyPair} pub - public key -* @returns {Boolean} - true if public key matches sig of message -*/ -EDDSA.prototype.verify = function verify(message, sig, pub) { - message = parseBytes(message); - sig = this.makeSignature(sig); - if (sig.S().gte(sig.eddsa.curve.n) || sig.S().isNeg()) { - return false; - } - var key = this.keyFromPublic(pub); - var h = this.hashInt(sig.Rencoded(), key.pubBytes(), message); - var SG = this.g.mul(sig.S()); - var RplusAh = sig.R().add(key.pub().mul(h)); - return RplusAh.eq(SG); -}; - -EDDSA.prototype.hashInt = function hashInt() { - var hash = this.hash(); - for (var i = 0; i < arguments.length; i++) - hash.update(arguments[i]); - return utils.intFromLE(hash.digest()).umod(this.curve.n); -}; - -EDDSA.prototype.keyFromPublic = function keyFromPublic(pub) { - return KeyPair.fromPublic(this, pub); -}; - -EDDSA.prototype.keyFromSecret = function keyFromSecret(secret) { - return KeyPair.fromSecret(this, secret); -}; - -EDDSA.prototype.makeSignature = function makeSignature(sig) { - if (sig instanceof Signature) - return sig; - return new Signature(this, sig); -}; - -/** -* * https://tools.ietf.org/html/draft-josefsson-eddsa-ed25519-03#section-5.2 -* -* EDDSA defines methods for encoding and decoding points and integers. These are -* helper convenience methods, that pass along to utility functions implied -* parameters. -* -*/ -EDDSA.prototype.encodePoint = function encodePoint(point) { - var enc = point.getY().toArray('le', this.encodingLength); - enc[this.encodingLength - 1] |= point.getX().isOdd() ? 0x80 : 0; - return enc; -}; - -EDDSA.prototype.decodePoint = function decodePoint(bytes) { - bytes = utils.parseBytes(bytes); - - var lastIx = bytes.length - 1; - var normed = bytes.slice(0, lastIx).concat(bytes[lastIx] & ~0x80); - var xIsOdd = (bytes[lastIx] & 0x80) !== 0; - - var y = utils.intFromLE(normed); - return this.curve.pointFromY(y, xIsOdd); -}; - -EDDSA.prototype.encodeInt = function encodeInt(num) { - return num.toArray('le', this.encodingLength); -}; - -EDDSA.prototype.decodeInt = function decodeInt(bytes) { - return utils.intFromLE(bytes); -}; - -EDDSA.prototype.isPoint = function isPoint(val) { - return val instanceof this.pointClass; -}; - -},{"../curves":110,"../utils":118,"./key":115,"./signature":116,"hash.js":140}],115:[function(require,module,exports){ -'use strict'; - -var utils = require('../utils'); -var assert = utils.assert; -var parseBytes = utils.parseBytes; -var cachedProperty = utils.cachedProperty; - -/** -* @param {EDDSA} eddsa - instance -* @param {Object} params - public/private key parameters -* -* @param {Array} [params.secret] - secret seed bytes -* @param {Point} [params.pub] - public key point (aka `A` in eddsa terms) -* @param {Array} [params.pub] - public key point encoded as bytes -* -*/ -function KeyPair(eddsa, params) { - this.eddsa = eddsa; - this._secret = parseBytes(params.secret); - if (eddsa.isPoint(params.pub)) - this._pub = params.pub; - else - this._pubBytes = parseBytes(params.pub); -} - -KeyPair.fromPublic = function fromPublic(eddsa, pub) { - if (pub instanceof KeyPair) - return pub; - return new KeyPair(eddsa, { pub: pub }); -}; - -KeyPair.fromSecret = function fromSecret(eddsa, secret) { - if (secret instanceof KeyPair) - return secret; - return new KeyPair(eddsa, { secret: secret }); -}; - -KeyPair.prototype.secret = function secret() { - return this._secret; -}; - -cachedProperty(KeyPair, 'pubBytes', function pubBytes() { - return this.eddsa.encodePoint(this.pub()); -}); - -cachedProperty(KeyPair, 'pub', function pub() { - if (this._pubBytes) - return this.eddsa.decodePoint(this._pubBytes); - return this.eddsa.g.mul(this.priv()); -}); - -cachedProperty(KeyPair, 'privBytes', function privBytes() { - var eddsa = this.eddsa; - var hash = this.hash(); - var lastIx = eddsa.encodingLength - 1; - - var a = hash.slice(0, eddsa.encodingLength); - a[0] &= 248; - a[lastIx] &= 127; - a[lastIx] |= 64; - - return a; -}); - -cachedProperty(KeyPair, 'priv', function priv() { - return this.eddsa.decodeInt(this.privBytes()); -}); - -cachedProperty(KeyPair, 'hash', function hash() { - return this.eddsa.hash().update(this.secret()).digest(); -}); - -cachedProperty(KeyPair, 'messagePrefix', function messagePrefix() { - return this.hash().slice(this.eddsa.encodingLength); -}); - -KeyPair.prototype.sign = function sign(message) { - assert(this._secret, 'KeyPair can only verify'); - return this.eddsa.sign(message, this); -}; - -KeyPair.prototype.verify = function verify(message, sig) { - return this.eddsa.verify(message, sig, this); -}; - -KeyPair.prototype.getSecret = function getSecret(enc) { - assert(this._secret, 'KeyPair is public only'); - return utils.encode(this.secret(), enc); -}; - -KeyPair.prototype.getPublic = function getPublic(enc) { - return utils.encode(this.pubBytes(), enc); -}; - -module.exports = KeyPair; - -},{"../utils":118}],116:[function(require,module,exports){ -'use strict'; - -var BN = require('bn.js'); -var utils = require('../utils'); -var assert = utils.assert; -var cachedProperty = utils.cachedProperty; -var parseBytes = utils.parseBytes; - -/** -* @param {EDDSA} eddsa - eddsa instance -* @param {Array|Object} sig - -* @param {Array|Point} [sig.R] - R point as Point or bytes -* @param {Array|bn} [sig.S] - S scalar as bn or bytes -* @param {Array} [sig.Rencoded] - R point encoded -* @param {Array} [sig.Sencoded] - S scalar encoded -*/ -function Signature(eddsa, sig) { - this.eddsa = eddsa; - - if (typeof sig !== 'object') - sig = parseBytes(sig); - - if (Array.isArray(sig)) { - sig = { - R: sig.slice(0, eddsa.encodingLength), - S: sig.slice(eddsa.encodingLength), - }; - } - - assert(sig.R && sig.S, 'Signature without R or S'); - - if (eddsa.isPoint(sig.R)) - this._R = sig.R; - if (sig.S instanceof BN) - this._S = sig.S; - - this._Rencoded = Array.isArray(sig.R) ? sig.R : sig.Rencoded; - this._Sencoded = Array.isArray(sig.S) ? sig.S : sig.Sencoded; -} - -cachedProperty(Signature, 'S', function S() { - return this.eddsa.decodeInt(this.Sencoded()); -}); - -cachedProperty(Signature, 'R', function R() { - return this.eddsa.decodePoint(this.Rencoded()); -}); - -cachedProperty(Signature, 'Rencoded', function Rencoded() { - return this.eddsa.encodePoint(this.R()); -}); - -cachedProperty(Signature, 'Sencoded', function Sencoded() { - return this.eddsa.encodeInt(this.S()); -}); - -Signature.prototype.toBytes = function toBytes() { - return this.Rencoded().concat(this.Sencoded()); -}; - -Signature.prototype.toHex = function toHex() { - return utils.encode(this.toBytes(), 'hex').toUpperCase(); -}; - -module.exports = Signature; - -},{"../utils":118,"bn.js":119}],117:[function(require,module,exports){ -module.exports = { - doubles: { - step: 4, - points: [ - [ - 'e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a', - 'f7e3507399e595929db99f34f57937101296891e44d23f0be1f32cce69616821', - ], - [ - '8282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508', - '11f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf', - ], - [ - '175e159f728b865a72f99cc6c6fc846de0b93833fd2222ed73fce5b551e5b739', - 'd3506e0d9e3c79eba4ef97a51ff71f5eacb5955add24345c6efa6ffee9fed695', - ], - [ - '363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640', - '4e273adfc732221953b445397f3363145b9a89008199ecb62003c7f3bee9de9', - ], - [ - '8b4b5f165df3c2be8c6244b5b745638843e4a781a15bcd1b69f79a55dffdf80c', - '4aad0a6f68d308b4b3fbd7813ab0da04f9e336546162ee56b3eff0c65fd4fd36', - ], - [ - '723cbaa6e5db996d6bf771c00bd548c7b700dbffa6c0e77bcb6115925232fcda', - '96e867b5595cc498a921137488824d6e2660a0653779494801dc069d9eb39f5f', - ], - [ - 'eebfa4d493bebf98ba5feec812c2d3b50947961237a919839a533eca0e7dd7fa', - '5d9a8ca3970ef0f269ee7edaf178089d9ae4cdc3a711f712ddfd4fdae1de8999', - ], - [ - '100f44da696e71672791d0a09b7bde459f1215a29b3c03bfefd7835b39a48db0', - 'cdd9e13192a00b772ec8f3300c090666b7ff4a18ff5195ac0fbd5cd62bc65a09', - ], - [ - 'e1031be262c7ed1b1dc9227a4a04c017a77f8d4464f3b3852c8acde6e534fd2d', - '9d7061928940405e6bb6a4176597535af292dd419e1ced79a44f18f29456a00d', - ], - [ - 'feea6cae46d55b530ac2839f143bd7ec5cf8b266a41d6af52d5e688d9094696d', - 'e57c6b6c97dce1bab06e4e12bf3ecd5c981c8957cc41442d3155debf18090088', - ], - [ - 'da67a91d91049cdcb367be4be6ffca3cfeed657d808583de33fa978bc1ec6cb1', - '9bacaa35481642bc41f463f7ec9780e5dec7adc508f740a17e9ea8e27a68be1d', - ], - [ - '53904faa0b334cdda6e000935ef22151ec08d0f7bb11069f57545ccc1a37b7c0', - '5bc087d0bc80106d88c9eccac20d3c1c13999981e14434699dcb096b022771c8', - ], - [ - '8e7bcd0bd35983a7719cca7764ca906779b53a043a9b8bcaeff959f43ad86047', - '10b7770b2a3da4b3940310420ca9514579e88e2e47fd68b3ea10047e8460372a', - ], - [ - '385eed34c1cdff21e6d0818689b81bde71a7f4f18397e6690a841e1599c43862', - '283bebc3e8ea23f56701de19e9ebf4576b304eec2086dc8cc0458fe5542e5453', - ], - [ - '6f9d9b803ecf191637c73a4413dfa180fddf84a5947fbc9c606ed86c3fac3a7', - '7c80c68e603059ba69b8e2a30e45c4d47ea4dd2f5c281002d86890603a842160', - ], - [ - '3322d401243c4e2582a2147c104d6ecbf774d163db0f5e5313b7e0e742d0e6bd', - '56e70797e9664ef5bfb019bc4ddaf9b72805f63ea2873af624f3a2e96c28b2a0', - ], - [ - '85672c7d2de0b7da2bd1770d89665868741b3f9af7643397721d74d28134ab83', - '7c481b9b5b43b2eb6374049bfa62c2e5e77f17fcc5298f44c8e3094f790313a6', - ], - [ - '948bf809b1988a46b06c9f1919413b10f9226c60f668832ffd959af60c82a0a', - '53a562856dcb6646dc6b74c5d1c3418c6d4dff08c97cd2bed4cb7f88d8c8e589', - ], - [ - '6260ce7f461801c34f067ce0f02873a8f1b0e44dfc69752accecd819f38fd8e8', - 'bc2da82b6fa5b571a7f09049776a1ef7ecd292238051c198c1a84e95b2b4ae17', - ], - [ - 'e5037de0afc1d8d43d8348414bbf4103043ec8f575bfdc432953cc8d2037fa2d', - '4571534baa94d3b5f9f98d09fb990bddbd5f5b03ec481f10e0e5dc841d755bda', - ], - [ - 'e06372b0f4a207adf5ea905e8f1771b4e7e8dbd1c6a6c5b725866a0ae4fce725', - '7a908974bce18cfe12a27bb2ad5a488cd7484a7787104870b27034f94eee31dd', - ], - [ - '213c7a715cd5d45358d0bbf9dc0ce02204b10bdde2a3f58540ad6908d0559754', - '4b6dad0b5ae462507013ad06245ba190bb4850f5f36a7eeddff2c27534b458f2', - ], - [ - '4e7c272a7af4b34e8dbb9352a5419a87e2838c70adc62cddf0cc3a3b08fbd53c', - '17749c766c9d0b18e16fd09f6def681b530b9614bff7dd33e0b3941817dcaae6', - ], - [ - 'fea74e3dbe778b1b10f238ad61686aa5c76e3db2be43057632427e2840fb27b6', - '6e0568db9b0b13297cf674deccb6af93126b596b973f7b77701d3db7f23cb96f', - ], - [ - '76e64113f677cf0e10a2570d599968d31544e179b760432952c02a4417bdde39', - 'c90ddf8dee4e95cf577066d70681f0d35e2a33d2b56d2032b4b1752d1901ac01', - ], - [ - 'c738c56b03b2abe1e8281baa743f8f9a8f7cc643df26cbee3ab150242bcbb891', - '893fb578951ad2537f718f2eacbfbbbb82314eef7880cfe917e735d9699a84c3', - ], - [ - 'd895626548b65b81e264c7637c972877d1d72e5f3a925014372e9f6588f6c14b', - 'febfaa38f2bc7eae728ec60818c340eb03428d632bb067e179363ed75d7d991f', - ], - [ - 'b8da94032a957518eb0f6433571e8761ceffc73693e84edd49150a564f676e03', - '2804dfa44805a1e4d7c99cc9762808b092cc584d95ff3b511488e4e74efdf6e7', - ], - [ - 'e80fea14441fb33a7d8adab9475d7fab2019effb5156a792f1a11778e3c0df5d', - 'eed1de7f638e00771e89768ca3ca94472d155e80af322ea9fcb4291b6ac9ec78', - ], - [ - 'a301697bdfcd704313ba48e51d567543f2a182031efd6915ddc07bbcc4e16070', - '7370f91cfb67e4f5081809fa25d40f9b1735dbf7c0a11a130c0d1a041e177ea1', - ], - [ - '90ad85b389d6b936463f9d0512678de208cc330b11307fffab7ac63e3fb04ed4', - 'e507a3620a38261affdcbd9427222b839aefabe1582894d991d4d48cb6ef150', - ], - [ - '8f68b9d2f63b5f339239c1ad981f162ee88c5678723ea3351b7b444c9ec4c0da', - '662a9f2dba063986de1d90c2b6be215dbbea2cfe95510bfdf23cbf79501fff82', - ], - [ - 'e4f3fb0176af85d65ff99ff9198c36091f48e86503681e3e6686fd5053231e11', - '1e63633ad0ef4f1c1661a6d0ea02b7286cc7e74ec951d1c9822c38576feb73bc', - ], - [ - '8c00fa9b18ebf331eb961537a45a4266c7034f2f0d4e1d0716fb6eae20eae29e', - 'efa47267fea521a1a9dc343a3736c974c2fadafa81e36c54e7d2a4c66702414b', - ], - [ - 'e7a26ce69dd4829f3e10cec0a9e98ed3143d084f308b92c0997fddfc60cb3e41', - '2a758e300fa7984b471b006a1aafbb18d0a6b2c0420e83e20e8a9421cf2cfd51', - ], - [ - 'b6459e0ee3662ec8d23540c223bcbdc571cbcb967d79424f3cf29eb3de6b80ef', - '67c876d06f3e06de1dadf16e5661db3c4b3ae6d48e35b2ff30bf0b61a71ba45', - ], - [ - 'd68a80c8280bb840793234aa118f06231d6f1fc67e73c5a5deda0f5b496943e8', - 'db8ba9fff4b586d00c4b1f9177b0e28b5b0e7b8f7845295a294c84266b133120', - ], - [ - '324aed7df65c804252dc0270907a30b09612aeb973449cea4095980fc28d3d5d', - '648a365774b61f2ff130c0c35aec1f4f19213b0c7e332843967224af96ab7c84', - ], - [ - '4df9c14919cde61f6d51dfdbe5fee5dceec4143ba8d1ca888e8bd373fd054c96', - '35ec51092d8728050974c23a1d85d4b5d506cdc288490192ebac06cad10d5d', - ], - [ - '9c3919a84a474870faed8a9c1cc66021523489054d7f0308cbfc99c8ac1f98cd', - 'ddb84f0f4a4ddd57584f044bf260e641905326f76c64c8e6be7e5e03d4fc599d', - ], - [ - '6057170b1dd12fdf8de05f281d8e06bb91e1493a8b91d4cc5a21382120a959e5', - '9a1af0b26a6a4807add9a2daf71df262465152bc3ee24c65e899be932385a2a8', - ], - [ - 'a576df8e23a08411421439a4518da31880cef0fba7d4df12b1a6973eecb94266', - '40a6bf20e76640b2c92b97afe58cd82c432e10a7f514d9f3ee8be11ae1b28ec8', - ], - [ - '7778a78c28dec3e30a05fe9629de8c38bb30d1f5cf9a3a208f763889be58ad71', - '34626d9ab5a5b22ff7098e12f2ff580087b38411ff24ac563b513fc1fd9f43ac', - ], - [ - '928955ee637a84463729fd30e7afd2ed5f96274e5ad7e5cb09eda9c06d903ac', - 'c25621003d3f42a827b78a13093a95eeac3d26efa8a8d83fc5180e935bcd091f', - ], - [ - '85d0fef3ec6db109399064f3a0e3b2855645b4a907ad354527aae75163d82751', - '1f03648413a38c0be29d496e582cf5663e8751e96877331582c237a24eb1f962', - ], - [ - 'ff2b0dce97eece97c1c9b6041798b85dfdfb6d8882da20308f5404824526087e', - '493d13fef524ba188af4c4dc54d07936c7b7ed6fb90e2ceb2c951e01f0c29907', - ], - [ - '827fbbe4b1e880ea9ed2b2e6301b212b57f1ee148cd6dd28780e5e2cf856e241', - 'c60f9c923c727b0b71bef2c67d1d12687ff7a63186903166d605b68baec293ec', - ], - [ - 'eaa649f21f51bdbae7be4ae34ce6e5217a58fdce7f47f9aa7f3b58fa2120e2b3', - 'be3279ed5bbbb03ac69a80f89879aa5a01a6b965f13f7e59d47a5305ba5ad93d', - ], - [ - 'e4a42d43c5cf169d9391df6decf42ee541b6d8f0c9a137401e23632dda34d24f', - '4d9f92e716d1c73526fc99ccfb8ad34ce886eedfa8d8e4f13a7f7131deba9414', - ], - [ - '1ec80fef360cbdd954160fadab352b6b92b53576a88fea4947173b9d4300bf19', - 'aeefe93756b5340d2f3a4958a7abbf5e0146e77f6295a07b671cdc1cc107cefd', - ], - [ - '146a778c04670c2f91b00af4680dfa8bce3490717d58ba889ddb5928366642be', - 'b318e0ec3354028add669827f9d4b2870aaa971d2f7e5ed1d0b297483d83efd0', - ], - [ - 'fa50c0f61d22e5f07e3acebb1aa07b128d0012209a28b9776d76a8793180eef9', - '6b84c6922397eba9b72cd2872281a68a5e683293a57a213b38cd8d7d3f4f2811', - ], - [ - 'da1d61d0ca721a11b1a5bf6b7d88e8421a288ab5d5bba5220e53d32b5f067ec2', - '8157f55a7c99306c79c0766161c91e2966a73899d279b48a655fba0f1ad836f1', - ], - [ - 'a8e282ff0c9706907215ff98e8fd416615311de0446f1e062a73b0610d064e13', - '7f97355b8db81c09abfb7f3c5b2515888b679a3e50dd6bd6cef7c73111f4cc0c', - ], - [ - '174a53b9c9a285872d39e56e6913cab15d59b1fa512508c022f382de8319497c', - 'ccc9dc37abfc9c1657b4155f2c47f9e6646b3a1d8cb9854383da13ac079afa73', - ], - [ - '959396981943785c3d3e57edf5018cdbe039e730e4918b3d884fdff09475b7ba', - '2e7e552888c331dd8ba0386a4b9cd6849c653f64c8709385e9b8abf87524f2fd', - ], - [ - 'd2a63a50ae401e56d645a1153b109a8fcca0a43d561fba2dbb51340c9d82b151', - 'e82d86fb6443fcb7565aee58b2948220a70f750af484ca52d4142174dcf89405', - ], - [ - '64587e2335471eb890ee7896d7cfdc866bacbdbd3839317b3436f9b45617e073', - 'd99fcdd5bf6902e2ae96dd6447c299a185b90a39133aeab358299e5e9faf6589', - ], - [ - '8481bde0e4e4d885b3a546d3e549de042f0aa6cea250e7fd358d6c86dd45e458', - '38ee7b8cba5404dd84a25bf39cecb2ca900a79c42b262e556d64b1b59779057e', - ], - [ - '13464a57a78102aa62b6979ae817f4637ffcfed3c4b1ce30bcd6303f6caf666b', - '69be159004614580ef7e433453ccb0ca48f300a81d0942e13f495a907f6ecc27', - ], - [ - 'bc4a9df5b713fe2e9aef430bcc1dc97a0cd9ccede2f28588cada3a0d2d83f366', - 'd3a81ca6e785c06383937adf4b798caa6e8a9fbfa547b16d758d666581f33c1', - ], - [ - '8c28a97bf8298bc0d23d8c749452a32e694b65e30a9472a3954ab30fe5324caa', - '40a30463a3305193378fedf31f7cc0eb7ae784f0451cb9459e71dc73cbef9482', - ], - [ - '8ea9666139527a8c1dd94ce4f071fd23c8b350c5a4bb33748c4ba111faccae0', - '620efabbc8ee2782e24e7c0cfb95c5d735b783be9cf0f8e955af34a30e62b945', - ], - [ - 'dd3625faef5ba06074669716bbd3788d89bdde815959968092f76cc4eb9a9787', - '7a188fa3520e30d461da2501045731ca941461982883395937f68d00c644a573', - ], - [ - 'f710d79d9eb962297e4f6232b40e8f7feb2bc63814614d692c12de752408221e', - 'ea98e67232d3b3295d3b535532115ccac8612c721851617526ae47a9c77bfc82', - ], - ], - }, - naf: { - wnd: 7, - points: [ - [ - 'f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9', - '388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672', - ], - [ - '2f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4', - 'd8ac222636e5e3d6d4dba9dda6c9c426f788271bab0d6840dca87d3aa6ac62d6', - ], - [ - '5cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc', - '6aebca40ba255960a3178d6d861a54dba813d0b813fde7b5a5082628087264da', - ], - [ - 'acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe', - 'cc338921b0a7d9fd64380971763b61e9add888a4375f8e0f05cc262ac64f9c37', - ], - [ - '774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb', - 'd984a032eb6b5e190243dd56d7b7b365372db1e2dff9d6a8301d74c9c953c61b', - ], - [ - 'f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8', - 'ab0902e8d880a89758212eb65cdaf473a1a06da521fa91f29b5cb52db03ed81', - ], - [ - 'd7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e', - '581e2872a86c72a683842ec228cc6defea40af2bd896d3a5c504dc9ff6a26b58', - ], - [ - 'defdea4cdb677750a420fee807eacf21eb9898ae79b9768766e4faa04a2d4a34', - '4211ab0694635168e997b0ead2a93daeced1f4a04a95c0f6cfb199f69e56eb77', - ], - [ - '2b4ea0a797a443d293ef5cff444f4979f06acfebd7e86d277475656138385b6c', - '85e89bc037945d93b343083b5a1c86131a01f60c50269763b570c854e5c09b7a', - ], - [ - '352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5', - '321eb4075348f534d59c18259dda3e1f4a1b3b2e71b1039c67bd3d8bcf81998c', - ], - [ - '2fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f', - '2de1068295dd865b64569335bd5dd80181d70ecfc882648423ba76b532b7d67', - ], - [ - '9248279b09b4d68dab21a9b066edda83263c3d84e09572e269ca0cd7f5453714', - '73016f7bf234aade5d1aa71bdea2b1ff3fc0de2a887912ffe54a32ce97cb3402', - ], - [ - 'daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729', - 'a69dce4a7d6c98e8d4a1aca87ef8d7003f83c230f3afa726ab40e52290be1c55', - ], - [ - 'c44d12c7065d812e8acf28d7cbb19f9011ecd9e9fdf281b0e6a3b5e87d22e7db', - '2119a460ce326cdc76c45926c982fdac0e106e861edf61c5a039063f0e0e6482', - ], - [ - '6a245bf6dc698504c89a20cfded60853152b695336c28063b61c65cbd269e6b4', - 'e022cf42c2bd4a708b3f5126f16a24ad8b33ba48d0423b6efd5e6348100d8a82', - ], - [ - '1697ffa6fd9de627c077e3d2fe541084ce13300b0bec1146f95ae57f0d0bd6a5', - 'b9c398f186806f5d27561506e4557433a2cf15009e498ae7adee9d63d01b2396', - ], - [ - '605bdb019981718b986d0f07e834cb0d9deb8360ffb7f61df982345ef27a7479', - '2972d2de4f8d20681a78d93ec96fe23c26bfae84fb14db43b01e1e9056b8c49', - ], - [ - '62d14dab4150bf497402fdc45a215e10dcb01c354959b10cfe31c7e9d87ff33d', - '80fc06bd8cc5b01098088a1950eed0db01aa132967ab472235f5642483b25eaf', - ], - [ - '80c60ad0040f27dade5b4b06c408e56b2c50e9f56b9b8b425e555c2f86308b6f', - '1c38303f1cc5c30f26e66bad7fe72f70a65eed4cbe7024eb1aa01f56430bd57a', - ], - [ - '7a9375ad6167ad54aa74c6348cc54d344cc5dc9487d847049d5eabb0fa03c8fb', - 'd0e3fa9eca8726909559e0d79269046bdc59ea10c70ce2b02d499ec224dc7f7', - ], - [ - 'd528ecd9b696b54c907a9ed045447a79bb408ec39b68df504bb51f459bc3ffc9', - 'eecf41253136e5f99966f21881fd656ebc4345405c520dbc063465b521409933', - ], - [ - '49370a4b5f43412ea25f514e8ecdad05266115e4a7ecb1387231808f8b45963', - '758f3f41afd6ed428b3081b0512fd62a54c3f3afbb5b6764b653052a12949c9a', - ], - [ - '77f230936ee88cbbd73df930d64702ef881d811e0e1498e2f1c13eb1fc345d74', - '958ef42a7886b6400a08266e9ba1b37896c95330d97077cbbe8eb3c7671c60d6', - ], - [ - 'f2dac991cc4ce4b9ea44887e5c7c0bce58c80074ab9d4dbaeb28531b7739f530', - 'e0dedc9b3b2f8dad4da1f32dec2531df9eb5fbeb0598e4fd1a117dba703a3c37', - ], - [ - '463b3d9f662621fb1b4be8fbbe2520125a216cdfc9dae3debcba4850c690d45b', - '5ed430d78c296c3543114306dd8622d7c622e27c970a1de31cb377b01af7307e', - ], - [ - 'f16f804244e46e2a09232d4aff3b59976b98fac14328a2d1a32496b49998f247', - 'cedabd9b82203f7e13d206fcdf4e33d92a6c53c26e5cce26d6579962c4e31df6', - ], - [ - 'caf754272dc84563b0352b7a14311af55d245315ace27c65369e15f7151d41d1', - 'cb474660ef35f5f2a41b643fa5e460575f4fa9b7962232a5c32f908318a04476', - ], - [ - '2600ca4b282cb986f85d0f1709979d8b44a09c07cb86d7c124497bc86f082120', - '4119b88753c15bd6a693b03fcddbb45d5ac6be74ab5f0ef44b0be9475a7e4b40', - ], - [ - '7635ca72d7e8432c338ec53cd12220bc01c48685e24f7dc8c602a7746998e435', - '91b649609489d613d1d5e590f78e6d74ecfc061d57048bad9e76f302c5b9c61', - ], - [ - '754e3239f325570cdbbf4a87deee8a66b7f2b33479d468fbc1a50743bf56cc18', - '673fb86e5bda30fb3cd0ed304ea49a023ee33d0197a695d0c5d98093c536683', - ], - [ - 'e3e6bd1071a1e96aff57859c82d570f0330800661d1c952f9fe2694691d9b9e8', - '59c9e0bba394e76f40c0aa58379a3cb6a5a2283993e90c4167002af4920e37f5', - ], - [ - '186b483d056a033826ae73d88f732985c4ccb1f32ba35f4b4cc47fdcf04aa6eb', - '3b952d32c67cf77e2e17446e204180ab21fb8090895138b4a4a797f86e80888b', - ], - [ - 'df9d70a6b9876ce544c98561f4be4f725442e6d2b737d9c91a8321724ce0963f', - '55eb2dafd84d6ccd5f862b785dc39d4ab157222720ef9da217b8c45cf2ba2417', - ], - [ - '5edd5cc23c51e87a497ca815d5dce0f8ab52554f849ed8995de64c5f34ce7143', - 'efae9c8dbc14130661e8cec030c89ad0c13c66c0d17a2905cdc706ab7399a868', - ], - [ - '290798c2b6476830da12fe02287e9e777aa3fba1c355b17a722d362f84614fba', - 'e38da76dcd440621988d00bcf79af25d5b29c094db2a23146d003afd41943e7a', - ], - [ - 'af3c423a95d9f5b3054754efa150ac39cd29552fe360257362dfdecef4053b45', - 'f98a3fd831eb2b749a93b0e6f35cfb40c8cd5aa667a15581bc2feded498fd9c6', - ], - [ - '766dbb24d134e745cccaa28c99bf274906bb66b26dcf98df8d2fed50d884249a', - '744b1152eacbe5e38dcc887980da38b897584a65fa06cedd2c924f97cbac5996', - ], - [ - '59dbf46f8c94759ba21277c33784f41645f7b44f6c596a58ce92e666191abe3e', - 'c534ad44175fbc300f4ea6ce648309a042ce739a7919798cd85e216c4a307f6e', - ], - [ - 'f13ada95103c4537305e691e74e9a4a8dd647e711a95e73cb62dc6018cfd87b8', - 'e13817b44ee14de663bf4bc808341f326949e21a6a75c2570778419bdaf5733d', - ], - [ - '7754b4fa0e8aced06d4167a2c59cca4cda1869c06ebadfb6488550015a88522c', - '30e93e864e669d82224b967c3020b8fa8d1e4e350b6cbcc537a48b57841163a2', - ], - [ - '948dcadf5990e048aa3874d46abef9d701858f95de8041d2a6828c99e2262519', - 'e491a42537f6e597d5d28a3224b1bc25df9154efbd2ef1d2cbba2cae5347d57e', - ], - [ - '7962414450c76c1689c7b48f8202ec37fb224cf5ac0bfa1570328a8a3d7c77ab', - '100b610ec4ffb4760d5c1fc133ef6f6b12507a051f04ac5760afa5b29db83437', - ], - [ - '3514087834964b54b15b160644d915485a16977225b8847bb0dd085137ec47ca', - 'ef0afbb2056205448e1652c48e8127fc6039e77c15c2378b7e7d15a0de293311', - ], - [ - 'd3cc30ad6b483e4bc79ce2c9dd8bc54993e947eb8df787b442943d3f7b527eaf', - '8b378a22d827278d89c5e9be8f9508ae3c2ad46290358630afb34db04eede0a4', - ], - [ - '1624d84780732860ce1c78fcbfefe08b2b29823db913f6493975ba0ff4847610', - '68651cf9b6da903e0914448c6cd9d4ca896878f5282be4c8cc06e2a404078575', - ], - [ - '733ce80da955a8a26902c95633e62a985192474b5af207da6df7b4fd5fc61cd4', - 'f5435a2bd2badf7d485a4d8b8db9fcce3e1ef8e0201e4578c54673bc1dc5ea1d', - ], - [ - '15d9441254945064cf1a1c33bbd3b49f8966c5092171e699ef258dfab81c045c', - 'd56eb30b69463e7234f5137b73b84177434800bacebfc685fc37bbe9efe4070d', - ], - [ - 'a1d0fcf2ec9de675b612136e5ce70d271c21417c9d2b8aaaac138599d0717940', - 'edd77f50bcb5a3cab2e90737309667f2641462a54070f3d519212d39c197a629', - ], - [ - 'e22fbe15c0af8ccc5780c0735f84dbe9a790badee8245c06c7ca37331cb36980', - 'a855babad5cd60c88b430a69f53a1a7a38289154964799be43d06d77d31da06', - ], - [ - '311091dd9860e8e20ee13473c1155f5f69635e394704eaa74009452246cfa9b3', - '66db656f87d1f04fffd1f04788c06830871ec5a64feee685bd80f0b1286d8374', - ], - [ - '34c1fd04d301be89b31c0442d3e6ac24883928b45a9340781867d4232ec2dbdf', - '9414685e97b1b5954bd46f730174136d57f1ceeb487443dc5321857ba73abee', - ], - [ - 'f219ea5d6b54701c1c14de5b557eb42a8d13f3abbcd08affcc2a5e6b049b8d63', - '4cb95957e83d40b0f73af4544cccf6b1f4b08d3c07b27fb8d8c2962a400766d1', - ], - [ - 'd7b8740f74a8fbaab1f683db8f45de26543a5490bca627087236912469a0b448', - 'fa77968128d9c92ee1010f337ad4717eff15db5ed3c049b3411e0315eaa4593b', - ], - [ - '32d31c222f8f6f0ef86f7c98d3a3335ead5bcd32abdd94289fe4d3091aa824bf', - '5f3032f5892156e39ccd3d7915b9e1da2e6dac9e6f26e961118d14b8462e1661', - ], - [ - '7461f371914ab32671045a155d9831ea8793d77cd59592c4340f86cbc18347b5', - '8ec0ba238b96bec0cbdddcae0aa442542eee1ff50c986ea6b39847b3cc092ff6', - ], - [ - 'ee079adb1df1860074356a25aa38206a6d716b2c3e67453d287698bad7b2b2d6', - '8dc2412aafe3be5c4c5f37e0ecc5f9f6a446989af04c4e25ebaac479ec1c8c1e', - ], - [ - '16ec93e447ec83f0467b18302ee620f7e65de331874c9dc72bfd8616ba9da6b5', - '5e4631150e62fb40d0e8c2a7ca5804a39d58186a50e497139626778e25b0674d', - ], - [ - 'eaa5f980c245f6f038978290afa70b6bd8855897f98b6aa485b96065d537bd99', - 'f65f5d3e292c2e0819a528391c994624d784869d7e6ea67fb18041024edc07dc', - ], - [ - '78c9407544ac132692ee1910a02439958ae04877151342ea96c4b6b35a49f51', - 'f3e0319169eb9b85d5404795539a5e68fa1fbd583c064d2462b675f194a3ddb4', - ], - [ - '494f4be219a1a77016dcd838431aea0001cdc8ae7a6fc688726578d9702857a5', - '42242a969283a5f339ba7f075e36ba2af925ce30d767ed6e55f4b031880d562c', - ], - [ - 'a598a8030da6d86c6bc7f2f5144ea549d28211ea58faa70ebf4c1e665c1fe9b5', - '204b5d6f84822c307e4b4a7140737aec23fc63b65b35f86a10026dbd2d864e6b', - ], - [ - 'c41916365abb2b5d09192f5f2dbeafec208f020f12570a184dbadc3e58595997', - '4f14351d0087efa49d245b328984989d5caf9450f34bfc0ed16e96b58fa9913', - ], - [ - '841d6063a586fa475a724604da03bc5b92a2e0d2e0a36acfe4c73a5514742881', - '73867f59c0659e81904f9a1c7543698e62562d6744c169ce7a36de01a8d6154', - ], - [ - '5e95bb399a6971d376026947f89bde2f282b33810928be4ded112ac4d70e20d5', - '39f23f366809085beebfc71181313775a99c9aed7d8ba38b161384c746012865', - ], - [ - '36e4641a53948fd476c39f8a99fd974e5ec07564b5315d8bf99471bca0ef2f66', - 'd2424b1b1abe4eb8164227b085c9aa9456ea13493fd563e06fd51cf5694c78fc', - ], - [ - '336581ea7bfbbb290c191a2f507a41cf5643842170e914faeab27c2c579f726', - 'ead12168595fe1be99252129b6e56b3391f7ab1410cd1e0ef3dcdcabd2fda224', - ], - [ - '8ab89816dadfd6b6a1f2634fcf00ec8403781025ed6890c4849742706bd43ede', - '6fdcef09f2f6d0a044e654aef624136f503d459c3e89845858a47a9129cdd24e', - ], - [ - '1e33f1a746c9c5778133344d9299fcaa20b0938e8acff2544bb40284b8c5fb94', - '60660257dd11b3aa9c8ed618d24edff2306d320f1d03010e33a7d2057f3b3b6', - ], - [ - '85b7c1dcb3cec1b7ee7f30ded79dd20a0ed1f4cc18cbcfcfa410361fd8f08f31', - '3d98a9cdd026dd43f39048f25a8847f4fcafad1895d7a633c6fed3c35e999511', - ], - [ - '29df9fbd8d9e46509275f4b125d6d45d7fbe9a3b878a7af872a2800661ac5f51', - 'b4c4fe99c775a606e2d8862179139ffda61dc861c019e55cd2876eb2a27d84b', - ], - [ - 'a0b1cae06b0a847a3fea6e671aaf8adfdfe58ca2f768105c8082b2e449fce252', - 'ae434102edde0958ec4b19d917a6a28e6b72da1834aff0e650f049503a296cf2', - ], - [ - '4e8ceafb9b3e9a136dc7ff67e840295b499dfb3b2133e4ba113f2e4c0e121e5', - 'cf2174118c8b6d7a4b48f6d534ce5c79422c086a63460502b827ce62a326683c', - ], - [ - 'd24a44e047e19b6f5afb81c7ca2f69080a5076689a010919f42725c2b789a33b', - '6fb8d5591b466f8fc63db50f1c0f1c69013f996887b8244d2cdec417afea8fa3', - ], - [ - 'ea01606a7a6c9cdd249fdfcfacb99584001edd28abbab77b5104e98e8e3b35d4', - '322af4908c7312b0cfbfe369f7a7b3cdb7d4494bc2823700cfd652188a3ea98d', - ], - [ - 'af8addbf2b661c8a6c6328655eb96651252007d8c5ea31be4ad196de8ce2131f', - '6749e67c029b85f52a034eafd096836b2520818680e26ac8f3dfbcdb71749700', - ], - [ - 'e3ae1974566ca06cc516d47e0fb165a674a3dabcfca15e722f0e3450f45889', - '2aeabe7e4531510116217f07bf4d07300de97e4874f81f533420a72eeb0bd6a4', - ], - [ - '591ee355313d99721cf6993ffed1e3e301993ff3ed258802075ea8ced397e246', - 'b0ea558a113c30bea60fc4775460c7901ff0b053d25ca2bdeee98f1a4be5d196', - ], - [ - '11396d55fda54c49f19aa97318d8da61fa8584e47b084945077cf03255b52984', - '998c74a8cd45ac01289d5833a7beb4744ff536b01b257be4c5767bea93ea57a4', - ], - [ - '3c5d2a1ba39c5a1790000738c9e0c40b8dcdfd5468754b6405540157e017aa7a', - 'b2284279995a34e2f9d4de7396fc18b80f9b8b9fdd270f6661f79ca4c81bd257', - ], - [ - 'cc8704b8a60a0defa3a99a7299f2e9c3fbc395afb04ac078425ef8a1793cc030', - 'bdd46039feed17881d1e0862db347f8cf395b74fc4bcdc4e940b74e3ac1f1b13', - ], - [ - 'c533e4f7ea8555aacd9777ac5cad29b97dd4defccc53ee7ea204119b2889b197', - '6f0a256bc5efdf429a2fb6242f1a43a2d9b925bb4a4b3a26bb8e0f45eb596096', - ], - [ - 'c14f8f2ccb27d6f109f6d08d03cc96a69ba8c34eec07bbcf566d48e33da6593', - 'c359d6923bb398f7fd4473e16fe1c28475b740dd098075e6c0e8649113dc3a38', - ], - [ - 'a6cbc3046bc6a450bac24789fa17115a4c9739ed75f8f21ce441f72e0b90e6ef', - '21ae7f4680e889bb130619e2c0f95a360ceb573c70603139862afd617fa9b9f', - ], - [ - '347d6d9a02c48927ebfb86c1359b1caf130a3c0267d11ce6344b39f99d43cc38', - '60ea7f61a353524d1c987f6ecec92f086d565ab687870cb12689ff1e31c74448', - ], - [ - 'da6545d2181db8d983f7dcb375ef5866d47c67b1bf31c8cf855ef7437b72656a', - '49b96715ab6878a79e78f07ce5680c5d6673051b4935bd897fea824b77dc208a', - ], - [ - 'c40747cc9d012cb1a13b8148309c6de7ec25d6945d657146b9d5994b8feb1111', - '5ca560753be2a12fc6de6caf2cb489565db936156b9514e1bb5e83037e0fa2d4', - ], - [ - '4e42c8ec82c99798ccf3a610be870e78338c7f713348bd34c8203ef4037f3502', - '7571d74ee5e0fb92a7a8b33a07783341a5492144cc54bcc40a94473693606437', - ], - [ - '3775ab7089bc6af823aba2e1af70b236d251cadb0c86743287522a1b3b0dedea', - 'be52d107bcfa09d8bcb9736a828cfa7fac8db17bf7a76a2c42ad961409018cf7', - ], - [ - 'cee31cbf7e34ec379d94fb814d3d775ad954595d1314ba8846959e3e82f74e26', - '8fd64a14c06b589c26b947ae2bcf6bfa0149ef0be14ed4d80f448a01c43b1c6d', - ], - [ - 'b4f9eaea09b6917619f6ea6a4eb5464efddb58fd45b1ebefcdc1a01d08b47986', - '39e5c9925b5a54b07433a4f18c61726f8bb131c012ca542eb24a8ac07200682a', - ], - [ - 'd4263dfc3d2df923a0179a48966d30ce84e2515afc3dccc1b77907792ebcc60e', - '62dfaf07a0f78feb30e30d6295853ce189e127760ad6cf7fae164e122a208d54', - ], - [ - '48457524820fa65a4f8d35eb6930857c0032acc0a4a2de422233eeda897612c4', - '25a748ab367979d98733c38a1fa1c2e7dc6cc07db2d60a9ae7a76aaa49bd0f77', - ], - [ - 'dfeeef1881101f2cb11644f3a2afdfc2045e19919152923f367a1767c11cceda', - 'ecfb7056cf1de042f9420bab396793c0c390bde74b4bbdff16a83ae09a9a7517', - ], - [ - '6d7ef6b17543f8373c573f44e1f389835d89bcbc6062ced36c82df83b8fae859', - 'cd450ec335438986dfefa10c57fea9bcc521a0959b2d80bbf74b190dca712d10', - ], - [ - 'e75605d59102a5a2684500d3b991f2e3f3c88b93225547035af25af66e04541f', - 'f5c54754a8f71ee540b9b48728473e314f729ac5308b06938360990e2bfad125', - ], - [ - 'eb98660f4c4dfaa06a2be453d5020bc99a0c2e60abe388457dd43fefb1ed620c', - '6cb9a8876d9cb8520609af3add26cd20a0a7cd8a9411131ce85f44100099223e', - ], - [ - '13e87b027d8514d35939f2e6892b19922154596941888336dc3563e3b8dba942', - 'fef5a3c68059a6dec5d624114bf1e91aac2b9da568d6abeb2570d55646b8adf1', - ], - [ - 'ee163026e9fd6fe017c38f06a5be6fc125424b371ce2708e7bf4491691e5764a', - '1acb250f255dd61c43d94ccc670d0f58f49ae3fa15b96623e5430da0ad6c62b2', - ], - [ - 'b268f5ef9ad51e4d78de3a750c2dc89b1e626d43505867999932e5db33af3d80', - '5f310d4b3c99b9ebb19f77d41c1dee018cf0d34fd4191614003e945a1216e423', - ], - [ - 'ff07f3118a9df035e9fad85eb6c7bfe42b02f01ca99ceea3bf7ffdba93c4750d', - '438136d603e858a3a5c440c38eccbaddc1d2942114e2eddd4740d098ced1f0d8', - ], - [ - '8d8b9855c7c052a34146fd20ffb658bea4b9f69e0d825ebec16e8c3ce2b526a1', - 'cdb559eedc2d79f926baf44fb84ea4d44bcf50fee51d7ceb30e2e7f463036758', - ], - [ - '52db0b5384dfbf05bfa9d472d7ae26dfe4b851ceca91b1eba54263180da32b63', - 'c3b997d050ee5d423ebaf66a6db9f57b3180c902875679de924b69d84a7b375', - ], - [ - 'e62f9490d3d51da6395efd24e80919cc7d0f29c3f3fa48c6fff543becbd43352', - '6d89ad7ba4876b0b22c2ca280c682862f342c8591f1daf5170e07bfd9ccafa7d', - ], - [ - '7f30ea2476b399b4957509c88f77d0191afa2ff5cb7b14fd6d8e7d65aaab1193', - 'ca5ef7d4b231c94c3b15389a5f6311e9daff7bb67b103e9880ef4bff637acaec', - ], - [ - '5098ff1e1d9f14fb46a210fada6c903fef0fb7b4a1dd1d9ac60a0361800b7a00', - '9731141d81fc8f8084d37c6e7542006b3ee1b40d60dfe5362a5b132fd17ddc0', - ], - [ - '32b78c7de9ee512a72895be6b9cbefa6e2f3c4ccce445c96b9f2c81e2778ad58', - 'ee1849f513df71e32efc3896ee28260c73bb80547ae2275ba497237794c8753c', - ], - [ - 'e2cb74fddc8e9fbcd076eef2a7c72b0ce37d50f08269dfc074b581550547a4f7', - 'd3aa2ed71c9dd2247a62df062736eb0baddea9e36122d2be8641abcb005cc4a4', - ], - [ - '8438447566d4d7bedadc299496ab357426009a35f235cb141be0d99cd10ae3a8', - 'c4e1020916980a4da5d01ac5e6ad330734ef0d7906631c4f2390426b2edd791f', - ], - [ - '4162d488b89402039b584c6fc6c308870587d9c46f660b878ab65c82c711d67e', - '67163e903236289f776f22c25fb8a3afc1732f2b84b4e95dbda47ae5a0852649', - ], - [ - '3fad3fa84caf0f34f0f89bfd2dcf54fc175d767aec3e50684f3ba4a4bf5f683d', - 'cd1bc7cb6cc407bb2f0ca647c718a730cf71872e7d0d2a53fa20efcdfe61826', - ], - [ - '674f2600a3007a00568c1a7ce05d0816c1fb84bf1370798f1c69532faeb1a86b', - '299d21f9413f33b3edf43b257004580b70db57da0b182259e09eecc69e0d38a5', - ], - [ - 'd32f4da54ade74abb81b815ad1fb3b263d82d6c692714bcff87d29bd5ee9f08f', - 'f9429e738b8e53b968e99016c059707782e14f4535359d582fc416910b3eea87', - ], - [ - '30e4e670435385556e593657135845d36fbb6931f72b08cb1ed954f1e3ce3ff6', - '462f9bce619898638499350113bbc9b10a878d35da70740dc695a559eb88db7b', - ], - [ - 'be2062003c51cc3004682904330e4dee7f3dcd10b01e580bf1971b04d4cad297', - '62188bc49d61e5428573d48a74e1c655b1c61090905682a0d5558ed72dccb9bc', - ], - [ - '93144423ace3451ed29e0fb9ac2af211cb6e84a601df5993c419859fff5df04a', - '7c10dfb164c3425f5c71a3f9d7992038f1065224f72bb9d1d902a6d13037b47c', - ], - [ - 'b015f8044f5fcbdcf21ca26d6c34fb8197829205c7b7d2a7cb66418c157b112c', - 'ab8c1e086d04e813744a655b2df8d5f83b3cdc6faa3088c1d3aea1454e3a1d5f', - ], - [ - 'd5e9e1da649d97d89e4868117a465a3a4f8a18de57a140d36b3f2af341a21b52', - '4cb04437f391ed73111a13cc1d4dd0db1693465c2240480d8955e8592f27447a', - ], - [ - 'd3ae41047dd7ca065dbf8ed77b992439983005cd72e16d6f996a5316d36966bb', - 'bd1aeb21ad22ebb22a10f0303417c6d964f8cdd7df0aca614b10dc14d125ac46', - ], - [ - '463e2763d885f958fc66cdd22800f0a487197d0a82e377b49f80af87c897b065', - 'bfefacdb0e5d0fd7df3a311a94de062b26b80c61fbc97508b79992671ef7ca7f', - ], - [ - '7985fdfd127c0567c6f53ec1bb63ec3158e597c40bfe747c83cddfc910641917', - '603c12daf3d9862ef2b25fe1de289aed24ed291e0ec6708703a5bd567f32ed03', - ], - [ - '74a1ad6b5f76e39db2dd249410eac7f99e74c59cb83d2d0ed5ff1543da7703e9', - 'cc6157ef18c9c63cd6193d83631bbea0093e0968942e8c33d5737fd790e0db08', - ], - [ - '30682a50703375f602d416664ba19b7fc9bab42c72747463a71d0896b22f6da3', - '553e04f6b018b4fa6c8f39e7f311d3176290d0e0f19ca73f17714d9977a22ff8', - ], - [ - '9e2158f0d7c0d5f26c3791efefa79597654e7a2b2464f52b1ee6c1347769ef57', - '712fcdd1b9053f09003a3481fa7762e9ffd7c8ef35a38509e2fbf2629008373', - ], - [ - '176e26989a43c9cfeba4029c202538c28172e566e3c4fce7322857f3be327d66', - 'ed8cc9d04b29eb877d270b4878dc43c19aefd31f4eee09ee7b47834c1fa4b1c3', - ], - [ - '75d46efea3771e6e68abb89a13ad747ecf1892393dfc4f1b7004788c50374da8', - '9852390a99507679fd0b86fd2b39a868d7efc22151346e1a3ca4726586a6bed8', - ], - [ - '809a20c67d64900ffb698c4c825f6d5f2310fb0451c869345b7319f645605721', - '9e994980d9917e22b76b061927fa04143d096ccc54963e6a5ebfa5f3f8e286c1', - ], - [ - '1b38903a43f7f114ed4500b4eac7083fdefece1cf29c63528d563446f972c180', - '4036edc931a60ae889353f77fd53de4a2708b26b6f5da72ad3394119daf408f9', - ], - ], - }, -}; - -},{}],118:[function(require,module,exports){ -'use strict'; - -var utils = exports; -var BN = require('bn.js'); -var minAssert = require('minimalistic-assert'); -var minUtils = require('minimalistic-crypto-utils'); - -utils.assert = minAssert; -utils.toArray = minUtils.toArray; -utils.zero2 = minUtils.zero2; -utils.toHex = minUtils.toHex; -utils.encode = minUtils.encode; - -// Represent num in a w-NAF form -function getNAF(num, w, bits) { - var naf = new Array(Math.max(num.bitLength(), bits) + 1); - var i; - for (i = 0; i < naf.length; i += 1) { - naf[i] = 0; - } - - var ws = 1 << (w + 1); - var k = num.clone(); - - for (i = 0; i < naf.length; i++) { - var z; - var mod = k.andln(ws - 1); - if (k.isOdd()) { - if (mod > (ws >> 1) - 1) - z = (ws >> 1) - mod; - else - z = mod; - k.isubn(z); - } else { - z = 0; - } - - naf[i] = z; - k.iushrn(1); - } - - return naf; -} -utils.getNAF = getNAF; - -// Represent k1, k2 in a Joint Sparse Form -function getJSF(k1, k2) { - var jsf = [ - [], - [], - ]; - - k1 = k1.clone(); - k2 = k2.clone(); - var d1 = 0; - var d2 = 0; - var m8; - while (k1.cmpn(-d1) > 0 || k2.cmpn(-d2) > 0) { - // First phase - var m14 = (k1.andln(3) + d1) & 3; - var m24 = (k2.andln(3) + d2) & 3; - if (m14 === 3) - m14 = -1; - if (m24 === 3) - m24 = -1; - var u1; - if ((m14 & 1) === 0) { - u1 = 0; - } else { - m8 = (k1.andln(7) + d1) & 7; - if ((m8 === 3 || m8 === 5) && m24 === 2) - u1 = -m14; - else - u1 = m14; - } - jsf[0].push(u1); - - var u2; - if ((m24 & 1) === 0) { - u2 = 0; - } else { - m8 = (k2.andln(7) + d2) & 7; - if ((m8 === 3 || m8 === 5) && m14 === 2) - u2 = -m24; - else - u2 = m24; - } - jsf[1].push(u2); - - // Second phase - if (2 * d1 === u1 + 1) - d1 = 1 - d1; - if (2 * d2 === u2 + 1) - d2 = 1 - d2; - k1.iushrn(1); - k2.iushrn(1); - } - - return jsf; -} -utils.getJSF = getJSF; - -function cachedProperty(obj, name, computer) { - var key = '_' + name; - obj.prototype[name] = function cachedProperty() { - return this[key] !== undefined ? this[key] : - this[key] = computer.call(this); - }; -} -utils.cachedProperty = cachedProperty; - -function parseBytes(bytes) { - return typeof bytes === 'string' ? utils.toArray(bytes, 'hex') : - bytes; -} -utils.parseBytes = parseBytes; - -function intFromLE(bytes) { - return new BN(bytes, 'hex', 'le'); -} -utils.intFromLE = intFromLE; - - -},{"bn.js":119,"minimalistic-assert":160,"minimalistic-crypto-utils":161}],119:[function(require,module,exports){ -arguments[4][20][0].apply(exports,arguments) -},{"buffer":34,"dup":20}],120:[function(require,module,exports){ -module.exports={ - "name": "elliptic", - "version": "6.5.6", - "description": "EC cryptography", - "main": "lib/elliptic.js", - "files": [ - "lib" - ], - "scripts": { - "lint": "eslint lib test", - "lint:fix": "npm run lint -- --fix", - "unit": "istanbul test _mocha --reporter=spec test/index.js", - "test": "npm run lint && npm run unit", - "version": "grunt dist && git add dist/" - }, - "repository": { - "type": "git", - "url": "git@github.com:indutny/elliptic" - }, - "keywords": [ - "EC", - "Elliptic", - "curve", - "Cryptography" - ], - "author": "Fedor Indutny ", - "license": "MIT", - "bugs": { - "url": "https://github.com/indutny/elliptic/issues" - }, - "homepage": "https://github.com/indutny/elliptic", - "devDependencies": { - "brfs": "^2.0.2", - "coveralls": "^3.1.0", - "eslint": "^7.6.0", - "grunt": "^1.2.1", - "grunt-browserify": "^5.3.0", - "grunt-cli": "^1.3.2", - "grunt-contrib-connect": "^3.0.0", - "grunt-contrib-copy": "^1.0.0", - "grunt-contrib-uglify": "^5.0.0", - "grunt-mocha-istanbul": "^5.0.2", - "grunt-saucelabs": "^9.0.1", - "istanbul": "^0.4.5", - "mocha": "^8.0.1" - }, - "dependencies": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" - } -} - -},{}],121:[function(require,module,exports){ -'use strict'; - -var GetIntrinsic = require('get-intrinsic'); - -/** @type {import('.')} */ -var $defineProperty = GetIntrinsic('%Object.defineProperty%', true) || false; -if ($defineProperty) { - try { - $defineProperty({}, 'a', { value: 1 }); - } catch (e) { - // IE 8 has a broken defineProperty - $defineProperty = false; - } -} - -module.exports = $defineProperty; - -},{"get-intrinsic":133}],122:[function(require,module,exports){ -'use strict'; - -/** @type {import('./eval')} */ -module.exports = EvalError; - -},{}],123:[function(require,module,exports){ -'use strict'; - -/** @type {import('.')} */ -module.exports = Error; - -},{}],124:[function(require,module,exports){ -'use strict'; - -/** @type {import('./range')} */ -module.exports = RangeError; - -},{}],125:[function(require,module,exports){ -'use strict'; - -/** @type {import('./ref')} */ -module.exports = ReferenceError; - -},{}],126:[function(require,module,exports){ -'use strict'; - -/** @type {import('./syntax')} */ -module.exports = SyntaxError; - -},{}],127:[function(require,module,exports){ -'use strict'; - -/** @type {import('./type')} */ -module.exports = TypeError; - -},{}],128:[function(require,module,exports){ -'use strict'; - -/** @type {import('./uri')} */ -module.exports = URIError; - -},{}],129:[function(require,module,exports){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -'use strict'; - -var R = typeof Reflect === 'object' ? Reflect : null -var ReflectApply = R && typeof R.apply === 'function' - ? R.apply - : function ReflectApply(target, receiver, args) { - return Function.prototype.apply.call(target, receiver, args); - } - -var ReflectOwnKeys -if (R && typeof R.ownKeys === 'function') { - ReflectOwnKeys = R.ownKeys -} else if (Object.getOwnPropertySymbols) { - ReflectOwnKeys = function ReflectOwnKeys(target) { - return Object.getOwnPropertyNames(target) - .concat(Object.getOwnPropertySymbols(target)); - }; -} else { - ReflectOwnKeys = function ReflectOwnKeys(target) { - return Object.getOwnPropertyNames(target); - }; -} - -function ProcessEmitWarning(warning) { - if (console && console.warn) console.warn(warning); -} - -var NumberIsNaN = Number.isNaN || function NumberIsNaN(value) { - return value !== value; -} - -function EventEmitter() { - EventEmitter.init.call(this); -} -module.exports = EventEmitter; -module.exports.once = once; - -// Backwards-compat with node 0.10.x -EventEmitter.EventEmitter = EventEmitter; - -EventEmitter.prototype._events = undefined; -EventEmitter.prototype._eventsCount = 0; -EventEmitter.prototype._maxListeners = undefined; - -// By default EventEmitters will print a warning if more than 10 listeners are -// added to it. This is a useful default which helps finding memory leaks. -var defaultMaxListeners = 10; - -function checkListener(listener) { - if (typeof listener !== 'function') { - throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener); - } -} - -Object.defineProperty(EventEmitter, 'defaultMaxListeners', { - enumerable: true, - get: function() { - return defaultMaxListeners; - }, - set: function(arg) { - if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) { - throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + '.'); - } - defaultMaxListeners = arg; - } -}); - -EventEmitter.init = function() { - - if (this._events === undefined || - this._events === Object.getPrototypeOf(this)._events) { - this._events = Object.create(null); - this._eventsCount = 0; - } - - this._maxListeners = this._maxListeners || undefined; -}; - -// Obviously not all Emitters should be limited to 10. This function allows -// that to be increased. Set to zero for unlimited. -EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) { - if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) { - throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + '.'); - } - this._maxListeners = n; - return this; -}; - -function _getMaxListeners(that) { - if (that._maxListeners === undefined) - return EventEmitter.defaultMaxListeners; - return that._maxListeners; -} - -EventEmitter.prototype.getMaxListeners = function getMaxListeners() { - return _getMaxListeners(this); -}; - -EventEmitter.prototype.emit = function emit(type) { - var args = []; - for (var i = 1; i < arguments.length; i++) args.push(arguments[i]); - var doError = (type === 'error'); - - var events = this._events; - if (events !== undefined) - doError = (doError && events.error === undefined); - else if (!doError) - return false; - - // If there is no 'error' event listener then throw. - if (doError) { - var er; - if (args.length > 0) - er = args[0]; - if (er instanceof Error) { - // Note: The comments on the `throw` lines are intentional, they show - // up in Node's output if this results in an unhandled exception. - throw er; // Unhandled 'error' event - } - // At least give some kind of context to the user - var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : '')); - err.context = er; - throw err; // Unhandled 'error' event - } - - var handler = events[type]; - - if (handler === undefined) - return false; - - if (typeof handler === 'function') { - ReflectApply(handler, this, args); - } else { - var len = handler.length; - var listeners = arrayClone(handler, len); - for (var i = 0; i < len; ++i) - ReflectApply(listeners[i], this, args); - } - - return true; -}; - -function _addListener(target, type, listener, prepend) { - var m; - var events; - var existing; - - checkListener(listener); - - events = target._events; - if (events === undefined) { - events = target._events = Object.create(null); - target._eventsCount = 0; - } else { - // To avoid recursion in the case that type === "newListener"! Before - // adding it to the listeners, first emit "newListener". - if (events.newListener !== undefined) { - target.emit('newListener', type, - listener.listener ? listener.listener : listener); - - // Re-assign `events` because a newListener handler could have caused the - // this._events to be assigned to a new object - events = target._events; - } - existing = events[type]; - } - - if (existing === undefined) { - // Optimize the case of one listener. Don't need the extra array object. - existing = events[type] = listener; - ++target._eventsCount; - } else { - if (typeof existing === 'function') { - // Adding the second element, need to change to array. - existing = events[type] = - prepend ? [listener, existing] : [existing, listener]; - // If we've already got an array, just append. - } else if (prepend) { - existing.unshift(listener); - } else { - existing.push(listener); - } - - // Check for listener leak - m = _getMaxListeners(target); - if (m > 0 && existing.length > m && !existing.warned) { - existing.warned = true; - // No error code for this since it is a Warning - // eslint-disable-next-line no-restricted-syntax - var w = new Error('Possible EventEmitter memory leak detected. ' + - existing.length + ' ' + String(type) + ' listeners ' + - 'added. Use emitter.setMaxListeners() to ' + - 'increase limit'); - w.name = 'MaxListenersExceededWarning'; - w.emitter = target; - w.type = type; - w.count = existing.length; - ProcessEmitWarning(w); - } - } - - return target; -} - -EventEmitter.prototype.addListener = function addListener(type, listener) { - return _addListener(this, type, listener, false); -}; - -EventEmitter.prototype.on = EventEmitter.prototype.addListener; - -EventEmitter.prototype.prependListener = - function prependListener(type, listener) { - return _addListener(this, type, listener, true); - }; - -function onceWrapper() { - if (!this.fired) { - this.target.removeListener(this.type, this.wrapFn); - this.fired = true; - if (arguments.length === 0) - return this.listener.call(this.target); - return this.listener.apply(this.target, arguments); - } -} - -function _onceWrap(target, type, listener) { - var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener }; - var wrapped = onceWrapper.bind(state); - wrapped.listener = listener; - state.wrapFn = wrapped; - return wrapped; -} - -EventEmitter.prototype.once = function once(type, listener) { - checkListener(listener); - this.on(type, _onceWrap(this, type, listener)); - return this; -}; - -EventEmitter.prototype.prependOnceListener = - function prependOnceListener(type, listener) { - checkListener(listener); - this.prependListener(type, _onceWrap(this, type, listener)); - return this; - }; - -// Emits a 'removeListener' event if and only if the listener was removed. -EventEmitter.prototype.removeListener = - function removeListener(type, listener) { - var list, events, position, i, originalListener; - - checkListener(listener); - - events = this._events; - if (events === undefined) - return this; - - list = events[type]; - if (list === undefined) - return this; - - if (list === listener || list.listener === listener) { - if (--this._eventsCount === 0) - this._events = Object.create(null); - else { - delete events[type]; - if (events.removeListener) - this.emit('removeListener', type, list.listener || listener); - } - } else if (typeof list !== 'function') { - position = -1; - - for (i = list.length - 1; i >= 0; i--) { - if (list[i] === listener || list[i].listener === listener) { - originalListener = list[i].listener; - position = i; - break; - } - } - - if (position < 0) - return this; - - if (position === 0) - list.shift(); - else { - spliceOne(list, position); - } - - if (list.length === 1) - events[type] = list[0]; - - if (events.removeListener !== undefined) - this.emit('removeListener', type, originalListener || listener); - } - - return this; - }; - -EventEmitter.prototype.off = EventEmitter.prototype.removeListener; - -EventEmitter.prototype.removeAllListeners = - function removeAllListeners(type) { - var listeners, events, i; - - events = this._events; - if (events === undefined) - return this; - - // not listening for removeListener, no need to emit - if (events.removeListener === undefined) { - if (arguments.length === 0) { - this._events = Object.create(null); - this._eventsCount = 0; - } else if (events[type] !== undefined) { - if (--this._eventsCount === 0) - this._events = Object.create(null); - else - delete events[type]; - } - return this; - } - - // emit removeListener for all listeners on all events - if (arguments.length === 0) { - var keys = Object.keys(events); - var key; - for (i = 0; i < keys.length; ++i) { - key = keys[i]; - if (key === 'removeListener') continue; - this.removeAllListeners(key); - } - this.removeAllListeners('removeListener'); - this._events = Object.create(null); - this._eventsCount = 0; - return this; - } - - listeners = events[type]; - - if (typeof listeners === 'function') { - this.removeListener(type, listeners); - } else if (listeners !== undefined) { - // LIFO order - for (i = listeners.length - 1; i >= 0; i--) { - this.removeListener(type, listeners[i]); - } - } - - return this; - }; - -function _listeners(target, type, unwrap) { - var events = target._events; - - if (events === undefined) - return []; - - var evlistener = events[type]; - if (evlistener === undefined) - return []; - - if (typeof evlistener === 'function') - return unwrap ? [evlistener.listener || evlistener] : [evlistener]; - - return unwrap ? - unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length); -} - -EventEmitter.prototype.listeners = function listeners(type) { - return _listeners(this, type, true); -}; - -EventEmitter.prototype.rawListeners = function rawListeners(type) { - return _listeners(this, type, false); -}; - -EventEmitter.listenerCount = function(emitter, type) { - if (typeof emitter.listenerCount === 'function') { - return emitter.listenerCount(type); - } else { - return listenerCount.call(emitter, type); - } -}; - -EventEmitter.prototype.listenerCount = listenerCount; -function listenerCount(type) { - var events = this._events; - - if (events !== undefined) { - var evlistener = events[type]; - - if (typeof evlistener === 'function') { - return 1; - } else if (evlistener !== undefined) { - return evlistener.length; - } - } - - return 0; -} - -EventEmitter.prototype.eventNames = function eventNames() { - return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : []; -}; - -function arrayClone(arr, n) { - var copy = new Array(n); - for (var i = 0; i < n; ++i) - copy[i] = arr[i]; - return copy; -} - -function spliceOne(list, index) { - for (; index + 1 < list.length; index++) - list[index] = list[index + 1]; - list.pop(); -} - -function unwrapListeners(arr) { - var ret = new Array(arr.length); - for (var i = 0; i < ret.length; ++i) { - ret[i] = arr[i].listener || arr[i]; - } - return ret; -} - -function once(emitter, name) { - return new Promise(function (resolve, reject) { - function errorListener(err) { - emitter.removeListener(name, resolver); - reject(err); - } - - function resolver() { - if (typeof emitter.removeListener === 'function') { - emitter.removeListener('error', errorListener); - } - resolve([].slice.call(arguments)); - }; - - eventTargetAgnosticAddListener(emitter, name, resolver, { once: true }); - if (name !== 'error') { - addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true }); - } - }); -} - -function addErrorHandlerIfEventEmitter(emitter, handler, flags) { - if (typeof emitter.on === 'function') { - eventTargetAgnosticAddListener(emitter, 'error', handler, flags); - } -} - -function eventTargetAgnosticAddListener(emitter, name, listener, flags) { - if (typeof emitter.on === 'function') { - if (flags.once) { - emitter.once(name, listener); - } else { - emitter.on(name, listener); - } - } else if (typeof emitter.addEventListener === 'function') { - // EventTarget does not have `error` event semantics like Node - // EventEmitters, we do not listen for `error` events here. - emitter.addEventListener(name, function wrapListener(arg) { - // IE does not have builtin `{ once: true }` support so we - // have to do it manually. - if (flags.once) { - emitter.removeEventListener(name, wrapListener); - } - listener(arg); - }); - } else { - throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type ' + typeof emitter); - } -} - -},{}],130:[function(require,module,exports){ -var Buffer = require('safe-buffer').Buffer -var MD5 = require('md5.js') - -/* eslint-disable camelcase */ -function EVP_BytesToKey (password, salt, keyBits, ivLen) { - if (!Buffer.isBuffer(password)) password = Buffer.from(password, 'binary') - if (salt) { - if (!Buffer.isBuffer(salt)) salt = Buffer.from(salt, 'binary') - if (salt.length !== 8) throw new RangeError('salt should be Buffer with 8 byte length') - } - - var keyLen = keyBits / 8 - var key = Buffer.alloc(keyLen) - var iv = Buffer.alloc(ivLen || 0) - var tmp = Buffer.alloc(0) - - while (keyLen > 0 || ivLen > 0) { - var hash = new MD5() - hash.update(tmp) - hash.update(password) - if (salt) hash.update(salt) - tmp = hash.digest() - - var used = 0 - - if (keyLen > 0) { - var keyStart = key.length - keyLen - used = Math.min(keyLen, tmp.length) - tmp.copy(key, keyStart, 0, used) - keyLen -= used - } - - if (used < tmp.length && ivLen > 0) { - var ivStart = iv.length - ivLen - var length = Math.min(ivLen, tmp.length - used) - tmp.copy(iv, ivStart, used, used + length) - ivLen -= length - } - } - - tmp.fill(0) - return { key: key, iv: iv } -} - -module.exports = EVP_BytesToKey - -},{"md5.js":157,"safe-buffer":221}],131:[function(require,module,exports){ -'use strict'; - -/* eslint no-invalid-this: 1 */ - -var ERROR_MESSAGE = 'Function.prototype.bind called on incompatible '; -var toStr = Object.prototype.toString; -var max = Math.max; -var funcType = '[object Function]'; - -var concatty = function concatty(a, b) { - var arr = []; - - for (var i = 0; i < a.length; i += 1) { - arr[i] = a[i]; - } - for (var j = 0; j < b.length; j += 1) { - arr[j + a.length] = b[j]; - } - - return arr; -}; - -var slicy = function slicy(arrLike, offset) { - var arr = []; - for (var i = offset || 0, j = 0; i < arrLike.length; i += 1, j += 1) { - arr[j] = arrLike[i]; - } - return arr; -}; - -var joiny = function (arr, joiner) { - var str = ''; - for (var i = 0; i < arr.length; i += 1) { - str += arr[i]; - if (i + 1 < arr.length) { - str += joiner; - } - } - return str; -}; - -module.exports = function bind(that) { - var target = this; - if (typeof target !== 'function' || toStr.apply(target) !== funcType) { - throw new TypeError(ERROR_MESSAGE + target); - } - var args = slicy(arguments, 1); - - var bound; - var binder = function () { - if (this instanceof bound) { - var result = target.apply( - this, - concatty(args, arguments) - ); - if (Object(result) === result) { - return result; - } - return this; - } - return target.apply( - that, - concatty(args, arguments) - ); - - }; - - var boundLength = max(0, target.length - args.length); - var boundArgs = []; - for (var i = 0; i < boundLength; i++) { - boundArgs[i] = '$' + i; - } - - bound = Function('binder', 'return function (' + joiny(boundArgs, ',') + '){ return binder.apply(this,arguments); }')(binder); - - if (target.prototype) { - var Empty = function Empty() {}; - Empty.prototype = target.prototype; - bound.prototype = new Empty(); - Empty.prototype = null; - } - - return bound; -}; - -},{}],132:[function(require,module,exports){ -'use strict'; - -var implementation = require('./implementation'); - -module.exports = Function.prototype.bind || implementation; - -},{"./implementation":131}],133:[function(require,module,exports){ -'use strict'; - -var undefined; - -var $Error = require('es-errors'); -var $EvalError = require('es-errors/eval'); -var $RangeError = require('es-errors/range'); -var $ReferenceError = require('es-errors/ref'); -var $SyntaxError = require('es-errors/syntax'); -var $TypeError = require('es-errors/type'); -var $URIError = require('es-errors/uri'); - -var $Function = Function; - -// eslint-disable-next-line consistent-return -var getEvalledConstructor = function (expressionSyntax) { - try { - return $Function('"use strict"; return (' + expressionSyntax + ').constructor;')(); - } catch (e) {} -}; - -var $gOPD = Object.getOwnPropertyDescriptor; -if ($gOPD) { - try { - $gOPD({}, ''); - } catch (e) { - $gOPD = null; // this is IE 8, which has a broken gOPD - } -} - -var throwTypeError = function () { - throw new $TypeError(); -}; -var ThrowTypeError = $gOPD - ? (function () { - try { - // eslint-disable-next-line no-unused-expressions, no-caller, no-restricted-properties - arguments.callee; // IE 8 does not throw here - return throwTypeError; - } catch (calleeThrows) { - try { - // IE 8 throws on Object.getOwnPropertyDescriptor(arguments, '') - return $gOPD(arguments, 'callee').get; - } catch (gOPDthrows) { - return throwTypeError; - } - } - }()) - : throwTypeError; - -var hasSymbols = require('has-symbols')(); -var hasProto = require('has-proto')(); - -var getProto = Object.getPrototypeOf || ( - hasProto - ? function (x) { return x.__proto__; } // eslint-disable-line no-proto - : null -); - -var needsEval = {}; - -var TypedArray = typeof Uint8Array === 'undefined' || !getProto ? undefined : getProto(Uint8Array); - -var INTRINSICS = { - __proto__: null, - '%AggregateError%': typeof AggregateError === 'undefined' ? undefined : AggregateError, - '%Array%': Array, - '%ArrayBuffer%': typeof ArrayBuffer === 'undefined' ? undefined : ArrayBuffer, - '%ArrayIteratorPrototype%': hasSymbols && getProto ? getProto([][Symbol.iterator]()) : undefined, - '%AsyncFromSyncIteratorPrototype%': undefined, - '%AsyncFunction%': needsEval, - '%AsyncGenerator%': needsEval, - '%AsyncGeneratorFunction%': needsEval, - '%AsyncIteratorPrototype%': needsEval, - '%Atomics%': typeof Atomics === 'undefined' ? undefined : Atomics, - '%BigInt%': typeof BigInt === 'undefined' ? undefined : BigInt, - '%BigInt64Array%': typeof BigInt64Array === 'undefined' ? undefined : BigInt64Array, - '%BigUint64Array%': typeof BigUint64Array === 'undefined' ? undefined : BigUint64Array, - '%Boolean%': Boolean, - '%DataView%': typeof DataView === 'undefined' ? undefined : DataView, - '%Date%': Date, - '%decodeURI%': decodeURI, - '%decodeURIComponent%': decodeURIComponent, - '%encodeURI%': encodeURI, - '%encodeURIComponent%': encodeURIComponent, - '%Error%': $Error, - '%eval%': eval, // eslint-disable-line no-eval - '%EvalError%': $EvalError, - '%Float32Array%': typeof Float32Array === 'undefined' ? undefined : Float32Array, - '%Float64Array%': typeof Float64Array === 'undefined' ? undefined : Float64Array, - '%FinalizationRegistry%': typeof FinalizationRegistry === 'undefined' ? undefined : FinalizationRegistry, - '%Function%': $Function, - '%GeneratorFunction%': needsEval, - '%Int8Array%': typeof Int8Array === 'undefined' ? undefined : Int8Array, - '%Int16Array%': typeof Int16Array === 'undefined' ? undefined : Int16Array, - '%Int32Array%': typeof Int32Array === 'undefined' ? undefined : Int32Array, - '%isFinite%': isFinite, - '%isNaN%': isNaN, - '%IteratorPrototype%': hasSymbols && getProto ? getProto(getProto([][Symbol.iterator]())) : undefined, - '%JSON%': typeof JSON === 'object' ? JSON : undefined, - '%Map%': typeof Map === 'undefined' ? undefined : Map, - '%MapIteratorPrototype%': typeof Map === 'undefined' || !hasSymbols || !getProto ? undefined : getProto(new Map()[Symbol.iterator]()), - '%Math%': Math, - '%Number%': Number, - '%Object%': Object, - '%parseFloat%': parseFloat, - '%parseInt%': parseInt, - '%Promise%': typeof Promise === 'undefined' ? undefined : Promise, - '%Proxy%': typeof Proxy === 'undefined' ? undefined : Proxy, - '%RangeError%': $RangeError, - '%ReferenceError%': $ReferenceError, - '%Reflect%': typeof Reflect === 'undefined' ? undefined : Reflect, - '%RegExp%': RegExp, - '%Set%': typeof Set === 'undefined' ? undefined : Set, - '%SetIteratorPrototype%': typeof Set === 'undefined' || !hasSymbols || !getProto ? undefined : getProto(new Set()[Symbol.iterator]()), - '%SharedArrayBuffer%': typeof SharedArrayBuffer === 'undefined' ? undefined : SharedArrayBuffer, - '%String%': String, - '%StringIteratorPrototype%': hasSymbols && getProto ? getProto(''[Symbol.iterator]()) : undefined, - '%Symbol%': hasSymbols ? Symbol : undefined, - '%SyntaxError%': $SyntaxError, - '%ThrowTypeError%': ThrowTypeError, - '%TypedArray%': TypedArray, - '%TypeError%': $TypeError, - '%Uint8Array%': typeof Uint8Array === 'undefined' ? undefined : Uint8Array, - '%Uint8ClampedArray%': typeof Uint8ClampedArray === 'undefined' ? undefined : Uint8ClampedArray, - '%Uint16Array%': typeof Uint16Array === 'undefined' ? undefined : Uint16Array, - '%Uint32Array%': typeof Uint32Array === 'undefined' ? undefined : Uint32Array, - '%URIError%': $URIError, - '%WeakMap%': typeof WeakMap === 'undefined' ? undefined : WeakMap, - '%WeakRef%': typeof WeakRef === 'undefined' ? undefined : WeakRef, - '%WeakSet%': typeof WeakSet === 'undefined' ? undefined : WeakSet -}; - -if (getProto) { - try { - null.error; // eslint-disable-line no-unused-expressions - } catch (e) { - // https://github.com/tc39/proposal-shadowrealm/pull/384#issuecomment-1364264229 - var errorProto = getProto(getProto(e)); - INTRINSICS['%Error.prototype%'] = errorProto; - } -} - -var doEval = function doEval(name) { - var value; - if (name === '%AsyncFunction%') { - value = getEvalledConstructor('async function () {}'); - } else if (name === '%GeneratorFunction%') { - value = getEvalledConstructor('function* () {}'); - } else if (name === '%AsyncGeneratorFunction%') { - value = getEvalledConstructor('async function* () {}'); - } else if (name === '%AsyncGenerator%') { - var fn = doEval('%AsyncGeneratorFunction%'); - if (fn) { - value = fn.prototype; - } - } else if (name === '%AsyncIteratorPrototype%') { - var gen = doEval('%AsyncGenerator%'); - if (gen && getProto) { - value = getProto(gen.prototype); - } - } - - INTRINSICS[name] = value; - - return value; -}; - -var LEGACY_ALIASES = { - __proto__: null, - '%ArrayBufferPrototype%': ['ArrayBuffer', 'prototype'], - '%ArrayPrototype%': ['Array', 'prototype'], - '%ArrayProto_entries%': ['Array', 'prototype', 'entries'], - '%ArrayProto_forEach%': ['Array', 'prototype', 'forEach'], - '%ArrayProto_keys%': ['Array', 'prototype', 'keys'], - '%ArrayProto_values%': ['Array', 'prototype', 'values'], - '%AsyncFunctionPrototype%': ['AsyncFunction', 'prototype'], - '%AsyncGenerator%': ['AsyncGeneratorFunction', 'prototype'], - '%AsyncGeneratorPrototype%': ['AsyncGeneratorFunction', 'prototype', 'prototype'], - '%BooleanPrototype%': ['Boolean', 'prototype'], - '%DataViewPrototype%': ['DataView', 'prototype'], - '%DatePrototype%': ['Date', 'prototype'], - '%ErrorPrototype%': ['Error', 'prototype'], - '%EvalErrorPrototype%': ['EvalError', 'prototype'], - '%Float32ArrayPrototype%': ['Float32Array', 'prototype'], - '%Float64ArrayPrototype%': ['Float64Array', 'prototype'], - '%FunctionPrototype%': ['Function', 'prototype'], - '%Generator%': ['GeneratorFunction', 'prototype'], - '%GeneratorPrototype%': ['GeneratorFunction', 'prototype', 'prototype'], - '%Int8ArrayPrototype%': ['Int8Array', 'prototype'], - '%Int16ArrayPrototype%': ['Int16Array', 'prototype'], - '%Int32ArrayPrototype%': ['Int32Array', 'prototype'], - '%JSONParse%': ['JSON', 'parse'], - '%JSONStringify%': ['JSON', 'stringify'], - '%MapPrototype%': ['Map', 'prototype'], - '%NumberPrototype%': ['Number', 'prototype'], - '%ObjectPrototype%': ['Object', 'prototype'], - '%ObjProto_toString%': ['Object', 'prototype', 'toString'], - '%ObjProto_valueOf%': ['Object', 'prototype', 'valueOf'], - '%PromisePrototype%': ['Promise', 'prototype'], - '%PromiseProto_then%': ['Promise', 'prototype', 'then'], - '%Promise_all%': ['Promise', 'all'], - '%Promise_reject%': ['Promise', 'reject'], - '%Promise_resolve%': ['Promise', 'resolve'], - '%RangeErrorPrototype%': ['RangeError', 'prototype'], - '%ReferenceErrorPrototype%': ['ReferenceError', 'prototype'], - '%RegExpPrototype%': ['RegExp', 'prototype'], - '%SetPrototype%': ['Set', 'prototype'], - '%SharedArrayBufferPrototype%': ['SharedArrayBuffer', 'prototype'], - '%StringPrototype%': ['String', 'prototype'], - '%SymbolPrototype%': ['Symbol', 'prototype'], - '%SyntaxErrorPrototype%': ['SyntaxError', 'prototype'], - '%TypedArrayPrototype%': ['TypedArray', 'prototype'], - '%TypeErrorPrototype%': ['TypeError', 'prototype'], - '%Uint8ArrayPrototype%': ['Uint8Array', 'prototype'], - '%Uint8ClampedArrayPrototype%': ['Uint8ClampedArray', 'prototype'], - '%Uint16ArrayPrototype%': ['Uint16Array', 'prototype'], - '%Uint32ArrayPrototype%': ['Uint32Array', 'prototype'], - '%URIErrorPrototype%': ['URIError', 'prototype'], - '%WeakMapPrototype%': ['WeakMap', 'prototype'], - '%WeakSetPrototype%': ['WeakSet', 'prototype'] -}; - -var bind = require('function-bind'); -var hasOwn = require('hasown'); -var $concat = bind.call(Function.call, Array.prototype.concat); -var $spliceApply = bind.call(Function.apply, Array.prototype.splice); -var $replace = bind.call(Function.call, String.prototype.replace); -var $strSlice = bind.call(Function.call, String.prototype.slice); -var $exec = bind.call(Function.call, RegExp.prototype.exec); - -/* adapted from https://github.com/lodash/lodash/blob/4.17.15/dist/lodash.js#L6735-L6744 */ -var rePropName = /[^%.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|%$))/g; -var reEscapeChar = /\\(\\)?/g; /** Used to match backslashes in property paths. */ -var stringToPath = function stringToPath(string) { - var first = $strSlice(string, 0, 1); - var last = $strSlice(string, -1); - if (first === '%' && last !== '%') { - throw new $SyntaxError('invalid intrinsic syntax, expected closing `%`'); - } else if (last === '%' && first !== '%') { - throw new $SyntaxError('invalid intrinsic syntax, expected opening `%`'); - } - var result = []; - $replace(string, rePropName, function (match, number, quote, subString) { - result[result.length] = quote ? $replace(subString, reEscapeChar, '$1') : number || match; - }); - return result; -}; -/* end adaptation */ - -var getBaseIntrinsic = function getBaseIntrinsic(name, allowMissing) { - var intrinsicName = name; - var alias; - if (hasOwn(LEGACY_ALIASES, intrinsicName)) { - alias = LEGACY_ALIASES[intrinsicName]; - intrinsicName = '%' + alias[0] + '%'; - } - - if (hasOwn(INTRINSICS, intrinsicName)) { - var value = INTRINSICS[intrinsicName]; - if (value === needsEval) { - value = doEval(intrinsicName); - } - if (typeof value === 'undefined' && !allowMissing) { - throw new $TypeError('intrinsic ' + name + ' exists, but is not available. Please file an issue!'); - } - - return { - alias: alias, - name: intrinsicName, - value: value - }; - } - - throw new $SyntaxError('intrinsic ' + name + ' does not exist!'); -}; - -module.exports = function GetIntrinsic(name, allowMissing) { - if (typeof name !== 'string' || name.length === 0) { - throw new $TypeError('intrinsic name must be a non-empty string'); - } - if (arguments.length > 1 && typeof allowMissing !== 'boolean') { - throw new $TypeError('"allowMissing" argument must be a boolean'); - } - - if ($exec(/^%?[^%]*%?$/, name) === null) { - throw new $SyntaxError('`%` may not be present anywhere but at the beginning and end of the intrinsic name'); - } - var parts = stringToPath(name); - var intrinsicBaseName = parts.length > 0 ? parts[0] : ''; - - var intrinsic = getBaseIntrinsic('%' + intrinsicBaseName + '%', allowMissing); - var intrinsicRealName = intrinsic.name; - var value = intrinsic.value; - var skipFurtherCaching = false; - - var alias = intrinsic.alias; - if (alias) { - intrinsicBaseName = alias[0]; - $spliceApply(parts, $concat([0, 1], alias)); - } - - for (var i = 1, isOwn = true; i < parts.length; i += 1) { - var part = parts[i]; - var first = $strSlice(part, 0, 1); - var last = $strSlice(part, -1); - if ( - ( - (first === '"' || first === "'" || first === '`') - || (last === '"' || last === "'" || last === '`') - ) - && first !== last - ) { - throw new $SyntaxError('property names with quotes must have matching quotes'); - } - if (part === 'constructor' || !isOwn) { - skipFurtherCaching = true; - } - - intrinsicBaseName += '.' + part; - intrinsicRealName = '%' + intrinsicBaseName + '%'; - - if (hasOwn(INTRINSICS, intrinsicRealName)) { - value = INTRINSICS[intrinsicRealName]; - } else if (value != null) { - if (!(part in value)) { - if (!allowMissing) { - throw new $TypeError('base intrinsic for ' + name + ' exists, but the property is not available.'); - } - return void undefined; - } - if ($gOPD && (i + 1) >= parts.length) { - var desc = $gOPD(value, part); - isOwn = !!desc; - - // By convention, when a data property is converted to an accessor - // property to emulate a data property that does not suffer from - // the override mistake, that accessor's getter is marked with - // an `originalValue` property. Here, when we detect this, we - // uphold the illusion by pretending to see that original data - // property, i.e., returning the value rather than the getter - // itself. - if (isOwn && 'get' in desc && !('originalValue' in desc.get)) { - value = desc.get; - } else { - value = value[part]; - } - } else { - isOwn = hasOwn(value, part); - value = value[part]; - } - - if (isOwn && !skipFurtherCaching) { - INTRINSICS[intrinsicRealName] = value; - } - } - } - return value; -}; - -},{"es-errors":123,"es-errors/eval":122,"es-errors/range":124,"es-errors/ref":125,"es-errors/syntax":126,"es-errors/type":127,"es-errors/uri":128,"function-bind":132,"has-proto":136,"has-symbols":137,"hasown":152}],134:[function(require,module,exports){ -'use strict'; - -var GetIntrinsic = require('get-intrinsic'); - -var $gOPD = GetIntrinsic('%Object.getOwnPropertyDescriptor%', true); - -if ($gOPD) { - try { - $gOPD([], 'length'); - } catch (e) { - // IE 8 has a broken gOPD - $gOPD = null; - } -} - -module.exports = $gOPD; - -},{"get-intrinsic":133}],135:[function(require,module,exports){ -'use strict'; - -var $defineProperty = require('es-define-property'); - -var hasPropertyDescriptors = function hasPropertyDescriptors() { - return !!$defineProperty; -}; - -hasPropertyDescriptors.hasArrayLengthDefineBug = function hasArrayLengthDefineBug() { - // node v0.6 has a bug where array lengths can be Set but not Defined - if (!$defineProperty) { - return null; - } - try { - return $defineProperty([], 'length', { value: 1 }).length !== 1; - } catch (e) { - // In Firefox 4-22, defining length on an array throws an exception. - return true; - } -}; - -module.exports = hasPropertyDescriptors; - -},{"es-define-property":121}],136:[function(require,module,exports){ -'use strict'; - -var test = { - __proto__: null, - foo: {} -}; - -var $Object = Object; - -/** @type {import('.')} */ -module.exports = function hasProto() { - // @ts-expect-error: TS errors on an inherited property for some reason - return { __proto__: test }.foo === test.foo - && !(test instanceof $Object); -}; - -},{}],137:[function(require,module,exports){ -'use strict'; - -var origSymbol = typeof Symbol !== 'undefined' && Symbol; -var hasSymbolSham = require('./shams'); - -module.exports = function hasNativeSymbols() { - if (typeof origSymbol !== 'function') { return false; } - if (typeof Symbol !== 'function') { return false; } - if (typeof origSymbol('foo') !== 'symbol') { return false; } - if (typeof Symbol('bar') !== 'symbol') { return false; } - - return hasSymbolSham(); -}; - -},{"./shams":138}],138:[function(require,module,exports){ -'use strict'; - -/* eslint complexity: [2, 18], max-statements: [2, 33] */ -module.exports = function hasSymbols() { - if (typeof Symbol !== 'function' || typeof Object.getOwnPropertySymbols !== 'function') { return false; } - if (typeof Symbol.iterator === 'symbol') { return true; } - - var obj = {}; - var sym = Symbol('test'); - var symObj = Object(sym); - if (typeof sym === 'string') { return false; } - - if (Object.prototype.toString.call(sym) !== '[object Symbol]') { return false; } - if (Object.prototype.toString.call(symObj) !== '[object Symbol]') { return false; } - - // temp disabled per https://github.com/ljharb/object.assign/issues/17 - // if (sym instanceof Symbol) { return false; } - // temp disabled per https://github.com/WebReflection/get-own-property-symbols/issues/4 - // if (!(symObj instanceof Symbol)) { return false; } - - // if (typeof Symbol.prototype.toString !== 'function') { return false; } - // if (String(sym) !== Symbol.prototype.toString.call(sym)) { return false; } - - var symVal = 42; - obj[sym] = symVal; - for (sym in obj) { return false; } // eslint-disable-line no-restricted-syntax, no-unreachable-loop - if (typeof Object.keys === 'function' && Object.keys(obj).length !== 0) { return false; } - - if (typeof Object.getOwnPropertyNames === 'function' && Object.getOwnPropertyNames(obj).length !== 0) { return false; } - - var syms = Object.getOwnPropertySymbols(obj); - if (syms.length !== 1 || syms[0] !== sym) { return false; } - - if (!Object.prototype.propertyIsEnumerable.call(obj, sym)) { return false; } - - if (typeof Object.getOwnPropertyDescriptor === 'function') { - var descriptor = Object.getOwnPropertyDescriptor(obj, sym); - if (descriptor.value !== symVal || descriptor.enumerable !== true) { return false; } - } - - return true; -}; - -},{}],139:[function(require,module,exports){ -'use strict' -var Buffer = require('safe-buffer').Buffer -var Transform = require('stream').Transform -var inherits = require('inherits') - -function throwIfNotStringOrBuffer (val, prefix) { - if (!Buffer.isBuffer(val) && typeof val !== 'string') { - throw new TypeError(prefix + ' must be a string or a buffer') - } -} - -function HashBase (blockSize) { - Transform.call(this) - - this._block = Buffer.allocUnsafe(blockSize) - this._blockSize = blockSize - this._blockOffset = 0 - this._length = [0, 0, 0, 0] - - this._finalized = false -} - -inherits(HashBase, Transform) - -HashBase.prototype._transform = function (chunk, encoding, callback) { - var error = null - try { - this.update(chunk, encoding) - } catch (err) { - error = err - } - - callback(error) -} - -HashBase.prototype._flush = function (callback) { - var error = null - try { - this.push(this.digest()) - } catch (err) { - error = err - } - - callback(error) -} - -HashBase.prototype.update = function (data, encoding) { - throwIfNotStringOrBuffer(data, 'Data') - if (this._finalized) throw new Error('Digest already called') - if (!Buffer.isBuffer(data)) data = Buffer.from(data, encoding) - - // consume data - var block = this._block - var offset = 0 - while (this._blockOffset + data.length - offset >= this._blockSize) { - for (var i = this._blockOffset; i < this._blockSize;) block[i++] = data[offset++] - this._update() - this._blockOffset = 0 - } - while (offset < data.length) block[this._blockOffset++] = data[offset++] - - // update length - for (var j = 0, carry = data.length * 8; carry > 0; ++j) { - this._length[j] += carry - carry = (this._length[j] / 0x0100000000) | 0 - if (carry > 0) this._length[j] -= 0x0100000000 * carry - } - - return this -} - -HashBase.prototype._update = function () { - throw new Error('_update is not implemented') -} - -HashBase.prototype.digest = function (encoding) { - if (this._finalized) throw new Error('Digest already called') - this._finalized = true - - var digest = this._digest() - if (encoding !== undefined) digest = digest.toString(encoding) - - // reset state - this._block.fill(0) - this._blockOffset = 0 - for (var i = 0; i < 4; ++i) this._length[i] = 0 - - return digest -} - -HashBase.prototype._digest = function () { - throw new Error('_digest is not implemented') -} - -module.exports = HashBase - -},{"inherits":155,"safe-buffer":221,"stream":232}],140:[function(require,module,exports){ -var hash = exports; - -hash.utils = require('./hash/utils'); -hash.common = require('./hash/common'); -hash.sha = require('./hash/sha'); -hash.ripemd = require('./hash/ripemd'); -hash.hmac = require('./hash/hmac'); - -// Proxy hash functions to the main object -hash.sha1 = hash.sha.sha1; -hash.sha256 = hash.sha.sha256; -hash.sha224 = hash.sha.sha224; -hash.sha384 = hash.sha.sha384; -hash.sha512 = hash.sha.sha512; -hash.ripemd160 = hash.ripemd.ripemd160; - -},{"./hash/common":141,"./hash/hmac":142,"./hash/ripemd":143,"./hash/sha":144,"./hash/utils":151}],141:[function(require,module,exports){ -'use strict'; - -var utils = require('./utils'); -var assert = require('minimalistic-assert'); - -function BlockHash() { - this.pending = null; - this.pendingTotal = 0; - this.blockSize = this.constructor.blockSize; - this.outSize = this.constructor.outSize; - this.hmacStrength = this.constructor.hmacStrength; - this.padLength = this.constructor.padLength / 8; - this.endian = 'big'; - - this._delta8 = this.blockSize / 8; - this._delta32 = this.blockSize / 32; -} -exports.BlockHash = BlockHash; - -BlockHash.prototype.update = function update(msg, enc) { - // Convert message to array, pad it, and join into 32bit blocks - msg = utils.toArray(msg, enc); - if (!this.pending) - this.pending = msg; - else - this.pending = this.pending.concat(msg); - this.pendingTotal += msg.length; - - // Enough data, try updating - if (this.pending.length >= this._delta8) { - msg = this.pending; - - // Process pending data in blocks - var r = msg.length % this._delta8; - this.pending = msg.slice(msg.length - r, msg.length); - if (this.pending.length === 0) - this.pending = null; - - msg = utils.join32(msg, 0, msg.length - r, this.endian); - for (var i = 0; i < msg.length; i += this._delta32) - this._update(msg, i, i + this._delta32); - } - - return this; -}; - -BlockHash.prototype.digest = function digest(enc) { - this.update(this._pad()); - assert(this.pending === null); - - return this._digest(enc); -}; - -BlockHash.prototype._pad = function pad() { - var len = this.pendingTotal; - var bytes = this._delta8; - var k = bytes - ((len + this.padLength) % bytes); - var res = new Array(k + this.padLength); - res[0] = 0x80; - for (var i = 1; i < k; i++) - res[i] = 0; - - // Append length - len <<= 3; - if (this.endian === 'big') { - for (var t = 8; t < this.padLength; t++) - res[i++] = 0; - - res[i++] = 0; - res[i++] = 0; - res[i++] = 0; - res[i++] = 0; - res[i++] = (len >>> 24) & 0xff; - res[i++] = (len >>> 16) & 0xff; - res[i++] = (len >>> 8) & 0xff; - res[i++] = len & 0xff; - } else { - res[i++] = len & 0xff; - res[i++] = (len >>> 8) & 0xff; - res[i++] = (len >>> 16) & 0xff; - res[i++] = (len >>> 24) & 0xff; - res[i++] = 0; - res[i++] = 0; - res[i++] = 0; - res[i++] = 0; - - for (t = 8; t < this.padLength; t++) - res[i++] = 0; - } - - return res; -}; - -},{"./utils":151,"minimalistic-assert":160}],142:[function(require,module,exports){ -'use strict'; - -var utils = require('./utils'); -var assert = require('minimalistic-assert'); - -function Hmac(hash, key, enc) { - if (!(this instanceof Hmac)) - return new Hmac(hash, key, enc); - this.Hash = hash; - this.blockSize = hash.blockSize / 8; - this.outSize = hash.outSize / 8; - this.inner = null; - this.outer = null; - - this._init(utils.toArray(key, enc)); -} -module.exports = Hmac; - -Hmac.prototype._init = function init(key) { - // Shorten key, if needed - if (key.length > this.blockSize) - key = new this.Hash().update(key).digest(); - assert(key.length <= this.blockSize); - - // Add padding to key - for (var i = key.length; i < this.blockSize; i++) - key.push(0); - - for (i = 0; i < key.length; i++) - key[i] ^= 0x36; - this.inner = new this.Hash().update(key); - - // 0x36 ^ 0x5c = 0x6a - for (i = 0; i < key.length; i++) - key[i] ^= 0x6a; - this.outer = new this.Hash().update(key); -}; - -Hmac.prototype.update = function update(msg, enc) { - this.inner.update(msg, enc); - return this; -}; - -Hmac.prototype.digest = function digest(enc) { - this.outer.update(this.inner.digest()); - return this.outer.digest(enc); -}; - -},{"./utils":151,"minimalistic-assert":160}],143:[function(require,module,exports){ -'use strict'; - -var utils = require('./utils'); -var common = require('./common'); - -var rotl32 = utils.rotl32; -var sum32 = utils.sum32; -var sum32_3 = utils.sum32_3; -var sum32_4 = utils.sum32_4; -var BlockHash = common.BlockHash; - -function RIPEMD160() { - if (!(this instanceof RIPEMD160)) - return new RIPEMD160(); - - BlockHash.call(this); - - this.h = [ 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 ]; - this.endian = 'little'; -} -utils.inherits(RIPEMD160, BlockHash); -exports.ripemd160 = RIPEMD160; - -RIPEMD160.blockSize = 512; -RIPEMD160.outSize = 160; -RIPEMD160.hmacStrength = 192; -RIPEMD160.padLength = 64; - -RIPEMD160.prototype._update = function update(msg, start) { - var A = this.h[0]; - var B = this.h[1]; - var C = this.h[2]; - var D = this.h[3]; - var E = this.h[4]; - var Ah = A; - var Bh = B; - var Ch = C; - var Dh = D; - var Eh = E; - for (var j = 0; j < 80; j++) { - var T = sum32( - rotl32( - sum32_4(A, f(j, B, C, D), msg[r[j] + start], K(j)), - s[j]), - E); - A = E; - E = D; - D = rotl32(C, 10); - C = B; - B = T; - T = sum32( - rotl32( - sum32_4(Ah, f(79 - j, Bh, Ch, Dh), msg[rh[j] + start], Kh(j)), - sh[j]), - Eh); - Ah = Eh; - Eh = Dh; - Dh = rotl32(Ch, 10); - Ch = Bh; - Bh = T; - } - T = sum32_3(this.h[1], C, Dh); - this.h[1] = sum32_3(this.h[2], D, Eh); - this.h[2] = sum32_3(this.h[3], E, Ah); - this.h[3] = sum32_3(this.h[4], A, Bh); - this.h[4] = sum32_3(this.h[0], B, Ch); - this.h[0] = T; -}; - -RIPEMD160.prototype._digest = function digest(enc) { - if (enc === 'hex') - return utils.toHex32(this.h, 'little'); - else - return utils.split32(this.h, 'little'); -}; - -function f(j, x, y, z) { - if (j <= 15) - return x ^ y ^ z; - else if (j <= 31) - return (x & y) | ((~x) & z); - else if (j <= 47) - return (x | (~y)) ^ z; - else if (j <= 63) - return (x & z) | (y & (~z)); - else - return x ^ (y | (~z)); -} - -function K(j) { - if (j <= 15) - return 0x00000000; - else if (j <= 31) - return 0x5a827999; - else if (j <= 47) - return 0x6ed9eba1; - else if (j <= 63) - return 0x8f1bbcdc; - else - return 0xa953fd4e; -} - -function Kh(j) { - if (j <= 15) - return 0x50a28be6; - else if (j <= 31) - return 0x5c4dd124; - else if (j <= 47) - return 0x6d703ef3; - else if (j <= 63) - return 0x7a6d76e9; - else - return 0x00000000; -} - -var r = [ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, - 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, - 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, - 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13 -]; - -var rh = [ - 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, - 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, - 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, - 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, - 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11 -]; - -var s = [ - 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, - 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, - 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, - 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, - 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6 -]; - -var sh = [ - 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, - 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, - 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, - 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, - 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11 -]; - -},{"./common":141,"./utils":151}],144:[function(require,module,exports){ -'use strict'; - -exports.sha1 = require('./sha/1'); -exports.sha224 = require('./sha/224'); -exports.sha256 = require('./sha/256'); -exports.sha384 = require('./sha/384'); -exports.sha512 = require('./sha/512'); - -},{"./sha/1":145,"./sha/224":146,"./sha/256":147,"./sha/384":148,"./sha/512":149}],145:[function(require,module,exports){ -'use strict'; - -var utils = require('../utils'); -var common = require('../common'); -var shaCommon = require('./common'); - -var rotl32 = utils.rotl32; -var sum32 = utils.sum32; -var sum32_5 = utils.sum32_5; -var ft_1 = shaCommon.ft_1; -var BlockHash = common.BlockHash; - -var sha1_K = [ - 0x5A827999, 0x6ED9EBA1, - 0x8F1BBCDC, 0xCA62C1D6 -]; - -function SHA1() { - if (!(this instanceof SHA1)) - return new SHA1(); - - BlockHash.call(this); - this.h = [ - 0x67452301, 0xefcdab89, 0x98badcfe, - 0x10325476, 0xc3d2e1f0 ]; - this.W = new Array(80); -} - -utils.inherits(SHA1, BlockHash); -module.exports = SHA1; - -SHA1.blockSize = 512; -SHA1.outSize = 160; -SHA1.hmacStrength = 80; -SHA1.padLength = 64; - -SHA1.prototype._update = function _update(msg, start) { - var W = this.W; - - for (var i = 0; i < 16; i++) - W[i] = msg[start + i]; - - for(; i < W.length; i++) - W[i] = rotl32(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1); - - var a = this.h[0]; - var b = this.h[1]; - var c = this.h[2]; - var d = this.h[3]; - var e = this.h[4]; - - for (i = 0; i < W.length; i++) { - var s = ~~(i / 20); - var t = sum32_5(rotl32(a, 5), ft_1(s, b, c, d), e, W[i], sha1_K[s]); - e = d; - d = c; - c = rotl32(b, 30); - b = a; - a = t; - } - - this.h[0] = sum32(this.h[0], a); - this.h[1] = sum32(this.h[1], b); - this.h[2] = sum32(this.h[2], c); - this.h[3] = sum32(this.h[3], d); - this.h[4] = sum32(this.h[4], e); -}; - -SHA1.prototype._digest = function digest(enc) { - if (enc === 'hex') - return utils.toHex32(this.h, 'big'); - else - return utils.split32(this.h, 'big'); -}; - -},{"../common":141,"../utils":151,"./common":150}],146:[function(require,module,exports){ -'use strict'; - -var utils = require('../utils'); -var SHA256 = require('./256'); - -function SHA224() { - if (!(this instanceof SHA224)) - return new SHA224(); - - SHA256.call(this); - this.h = [ - 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, - 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4 ]; -} -utils.inherits(SHA224, SHA256); -module.exports = SHA224; - -SHA224.blockSize = 512; -SHA224.outSize = 224; -SHA224.hmacStrength = 192; -SHA224.padLength = 64; - -SHA224.prototype._digest = function digest(enc) { - // Just truncate output - if (enc === 'hex') - return utils.toHex32(this.h.slice(0, 7), 'big'); - else - return utils.split32(this.h.slice(0, 7), 'big'); -}; - - -},{"../utils":151,"./256":147}],147:[function(require,module,exports){ -'use strict'; - -var utils = require('../utils'); -var common = require('../common'); -var shaCommon = require('./common'); -var assert = require('minimalistic-assert'); - -var sum32 = utils.sum32; -var sum32_4 = utils.sum32_4; -var sum32_5 = utils.sum32_5; -var ch32 = shaCommon.ch32; -var maj32 = shaCommon.maj32; -var s0_256 = shaCommon.s0_256; -var s1_256 = shaCommon.s1_256; -var g0_256 = shaCommon.g0_256; -var g1_256 = shaCommon.g1_256; - -var BlockHash = common.BlockHash; - -var sha256_K = [ - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, - 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, - 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, - 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, - 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, - 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 -]; - -function SHA256() { - if (!(this instanceof SHA256)) - return new SHA256(); - - BlockHash.call(this); - this.h = [ - 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, - 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 - ]; - this.k = sha256_K; - this.W = new Array(64); -} -utils.inherits(SHA256, BlockHash); -module.exports = SHA256; - -SHA256.blockSize = 512; -SHA256.outSize = 256; -SHA256.hmacStrength = 192; -SHA256.padLength = 64; - -SHA256.prototype._update = function _update(msg, start) { - var W = this.W; - - for (var i = 0; i < 16; i++) - W[i] = msg[start + i]; - for (; i < W.length; i++) - W[i] = sum32_4(g1_256(W[i - 2]), W[i - 7], g0_256(W[i - 15]), W[i - 16]); - - var a = this.h[0]; - var b = this.h[1]; - var c = this.h[2]; - var d = this.h[3]; - var e = this.h[4]; - var f = this.h[5]; - var g = this.h[6]; - var h = this.h[7]; - - assert(this.k.length === W.length); - for (i = 0; i < W.length; i++) { - var T1 = sum32_5(h, s1_256(e), ch32(e, f, g), this.k[i], W[i]); - var T2 = sum32(s0_256(a), maj32(a, b, c)); - h = g; - g = f; - f = e; - e = sum32(d, T1); - d = c; - c = b; - b = a; - a = sum32(T1, T2); - } - - this.h[0] = sum32(this.h[0], a); - this.h[1] = sum32(this.h[1], b); - this.h[2] = sum32(this.h[2], c); - this.h[3] = sum32(this.h[3], d); - this.h[4] = sum32(this.h[4], e); - this.h[5] = sum32(this.h[5], f); - this.h[6] = sum32(this.h[6], g); - this.h[7] = sum32(this.h[7], h); -}; - -SHA256.prototype._digest = function digest(enc) { - if (enc === 'hex') - return utils.toHex32(this.h, 'big'); - else - return utils.split32(this.h, 'big'); -}; - -},{"../common":141,"../utils":151,"./common":150,"minimalistic-assert":160}],148:[function(require,module,exports){ -'use strict'; - -var utils = require('../utils'); - -var SHA512 = require('./512'); - -function SHA384() { - if (!(this instanceof SHA384)) - return new SHA384(); - - SHA512.call(this); - this.h = [ - 0xcbbb9d5d, 0xc1059ed8, - 0x629a292a, 0x367cd507, - 0x9159015a, 0x3070dd17, - 0x152fecd8, 0xf70e5939, - 0x67332667, 0xffc00b31, - 0x8eb44a87, 0x68581511, - 0xdb0c2e0d, 0x64f98fa7, - 0x47b5481d, 0xbefa4fa4 ]; -} -utils.inherits(SHA384, SHA512); -module.exports = SHA384; - -SHA384.blockSize = 1024; -SHA384.outSize = 384; -SHA384.hmacStrength = 192; -SHA384.padLength = 128; - -SHA384.prototype._digest = function digest(enc) { - if (enc === 'hex') - return utils.toHex32(this.h.slice(0, 12), 'big'); - else - return utils.split32(this.h.slice(0, 12), 'big'); -}; - -},{"../utils":151,"./512":149}],149:[function(require,module,exports){ -'use strict'; - -var utils = require('../utils'); -var common = require('../common'); -var assert = require('minimalistic-assert'); - -var rotr64_hi = utils.rotr64_hi; -var rotr64_lo = utils.rotr64_lo; -var shr64_hi = utils.shr64_hi; -var shr64_lo = utils.shr64_lo; -var sum64 = utils.sum64; -var sum64_hi = utils.sum64_hi; -var sum64_lo = utils.sum64_lo; -var sum64_4_hi = utils.sum64_4_hi; -var sum64_4_lo = utils.sum64_4_lo; -var sum64_5_hi = utils.sum64_5_hi; -var sum64_5_lo = utils.sum64_5_lo; - -var BlockHash = common.BlockHash; - -var sha512_K = [ - 0x428a2f98, 0xd728ae22, 0x71374491, 0x23ef65cd, - 0xb5c0fbcf, 0xec4d3b2f, 0xe9b5dba5, 0x8189dbbc, - 0x3956c25b, 0xf348b538, 0x59f111f1, 0xb605d019, - 0x923f82a4, 0xaf194f9b, 0xab1c5ed5, 0xda6d8118, - 0xd807aa98, 0xa3030242, 0x12835b01, 0x45706fbe, - 0x243185be, 0x4ee4b28c, 0x550c7dc3, 0xd5ffb4e2, - 0x72be5d74, 0xf27b896f, 0x80deb1fe, 0x3b1696b1, - 0x9bdc06a7, 0x25c71235, 0xc19bf174, 0xcf692694, - 0xe49b69c1, 0x9ef14ad2, 0xefbe4786, 0x384f25e3, - 0x0fc19dc6, 0x8b8cd5b5, 0x240ca1cc, 0x77ac9c65, - 0x2de92c6f, 0x592b0275, 0x4a7484aa, 0x6ea6e483, - 0x5cb0a9dc, 0xbd41fbd4, 0x76f988da, 0x831153b5, - 0x983e5152, 0xee66dfab, 0xa831c66d, 0x2db43210, - 0xb00327c8, 0x98fb213f, 0xbf597fc7, 0xbeef0ee4, - 0xc6e00bf3, 0x3da88fc2, 0xd5a79147, 0x930aa725, - 0x06ca6351, 0xe003826f, 0x14292967, 0x0a0e6e70, - 0x27b70a85, 0x46d22ffc, 0x2e1b2138, 0x5c26c926, - 0x4d2c6dfc, 0x5ac42aed, 0x53380d13, 0x9d95b3df, - 0x650a7354, 0x8baf63de, 0x766a0abb, 0x3c77b2a8, - 0x81c2c92e, 0x47edaee6, 0x92722c85, 0x1482353b, - 0xa2bfe8a1, 0x4cf10364, 0xa81a664b, 0xbc423001, - 0xc24b8b70, 0xd0f89791, 0xc76c51a3, 0x0654be30, - 0xd192e819, 0xd6ef5218, 0xd6990624, 0x5565a910, - 0xf40e3585, 0x5771202a, 0x106aa070, 0x32bbd1b8, - 0x19a4c116, 0xb8d2d0c8, 0x1e376c08, 0x5141ab53, - 0x2748774c, 0xdf8eeb99, 0x34b0bcb5, 0xe19b48a8, - 0x391c0cb3, 0xc5c95a63, 0x4ed8aa4a, 0xe3418acb, - 0x5b9cca4f, 0x7763e373, 0x682e6ff3, 0xd6b2b8a3, - 0x748f82ee, 0x5defb2fc, 0x78a5636f, 0x43172f60, - 0x84c87814, 0xa1f0ab72, 0x8cc70208, 0x1a6439ec, - 0x90befffa, 0x23631e28, 0xa4506ceb, 0xde82bde9, - 0xbef9a3f7, 0xb2c67915, 0xc67178f2, 0xe372532b, - 0xca273ece, 0xea26619c, 0xd186b8c7, 0x21c0c207, - 0xeada7dd6, 0xcde0eb1e, 0xf57d4f7f, 0xee6ed178, - 0x06f067aa, 0x72176fba, 0x0a637dc5, 0xa2c898a6, - 0x113f9804, 0xbef90dae, 0x1b710b35, 0x131c471b, - 0x28db77f5, 0x23047d84, 0x32caab7b, 0x40c72493, - 0x3c9ebe0a, 0x15c9bebc, 0x431d67c4, 0x9c100d4c, - 0x4cc5d4be, 0xcb3e42b6, 0x597f299c, 0xfc657e2a, - 0x5fcb6fab, 0x3ad6faec, 0x6c44198c, 0x4a475817 -]; - -function SHA512() { - if (!(this instanceof SHA512)) - return new SHA512(); - - BlockHash.call(this); - this.h = [ - 0x6a09e667, 0xf3bcc908, - 0xbb67ae85, 0x84caa73b, - 0x3c6ef372, 0xfe94f82b, - 0xa54ff53a, 0x5f1d36f1, - 0x510e527f, 0xade682d1, - 0x9b05688c, 0x2b3e6c1f, - 0x1f83d9ab, 0xfb41bd6b, - 0x5be0cd19, 0x137e2179 ]; - this.k = sha512_K; - this.W = new Array(160); -} -utils.inherits(SHA512, BlockHash); -module.exports = SHA512; - -SHA512.blockSize = 1024; -SHA512.outSize = 512; -SHA512.hmacStrength = 192; -SHA512.padLength = 128; - -SHA512.prototype._prepareBlock = function _prepareBlock(msg, start) { - var W = this.W; - - // 32 x 32bit words - for (var i = 0; i < 32; i++) - W[i] = msg[start + i]; - for (; i < W.length; i += 2) { - var c0_hi = g1_512_hi(W[i - 4], W[i - 3]); // i - 2 - var c0_lo = g1_512_lo(W[i - 4], W[i - 3]); - var c1_hi = W[i - 14]; // i - 7 - var c1_lo = W[i - 13]; - var c2_hi = g0_512_hi(W[i - 30], W[i - 29]); // i - 15 - var c2_lo = g0_512_lo(W[i - 30], W[i - 29]); - var c3_hi = W[i - 32]; // i - 16 - var c3_lo = W[i - 31]; - - W[i] = sum64_4_hi( - c0_hi, c0_lo, - c1_hi, c1_lo, - c2_hi, c2_lo, - c3_hi, c3_lo); - W[i + 1] = sum64_4_lo( - c0_hi, c0_lo, - c1_hi, c1_lo, - c2_hi, c2_lo, - c3_hi, c3_lo); - } -}; - -SHA512.prototype._update = function _update(msg, start) { - this._prepareBlock(msg, start); - - var W = this.W; - - var ah = this.h[0]; - var al = this.h[1]; - var bh = this.h[2]; - var bl = this.h[3]; - var ch = this.h[4]; - var cl = this.h[5]; - var dh = this.h[6]; - var dl = this.h[7]; - var eh = this.h[8]; - var el = this.h[9]; - var fh = this.h[10]; - var fl = this.h[11]; - var gh = this.h[12]; - var gl = this.h[13]; - var hh = this.h[14]; - var hl = this.h[15]; - - assert(this.k.length === W.length); - for (var i = 0; i < W.length; i += 2) { - var c0_hi = hh; - var c0_lo = hl; - var c1_hi = s1_512_hi(eh, el); - var c1_lo = s1_512_lo(eh, el); - var c2_hi = ch64_hi(eh, el, fh, fl, gh, gl); - var c2_lo = ch64_lo(eh, el, fh, fl, gh, gl); - var c3_hi = this.k[i]; - var c3_lo = this.k[i + 1]; - var c4_hi = W[i]; - var c4_lo = W[i + 1]; - - var T1_hi = sum64_5_hi( - c0_hi, c0_lo, - c1_hi, c1_lo, - c2_hi, c2_lo, - c3_hi, c3_lo, - c4_hi, c4_lo); - var T1_lo = sum64_5_lo( - c0_hi, c0_lo, - c1_hi, c1_lo, - c2_hi, c2_lo, - c3_hi, c3_lo, - c4_hi, c4_lo); - - c0_hi = s0_512_hi(ah, al); - c0_lo = s0_512_lo(ah, al); - c1_hi = maj64_hi(ah, al, bh, bl, ch, cl); - c1_lo = maj64_lo(ah, al, bh, bl, ch, cl); - - var T2_hi = sum64_hi(c0_hi, c0_lo, c1_hi, c1_lo); - var T2_lo = sum64_lo(c0_hi, c0_lo, c1_hi, c1_lo); - - hh = gh; - hl = gl; - - gh = fh; - gl = fl; - - fh = eh; - fl = el; - - eh = sum64_hi(dh, dl, T1_hi, T1_lo); - el = sum64_lo(dl, dl, T1_hi, T1_lo); - - dh = ch; - dl = cl; - - ch = bh; - cl = bl; - - bh = ah; - bl = al; - - ah = sum64_hi(T1_hi, T1_lo, T2_hi, T2_lo); - al = sum64_lo(T1_hi, T1_lo, T2_hi, T2_lo); - } - - sum64(this.h, 0, ah, al); - sum64(this.h, 2, bh, bl); - sum64(this.h, 4, ch, cl); - sum64(this.h, 6, dh, dl); - sum64(this.h, 8, eh, el); - sum64(this.h, 10, fh, fl); - sum64(this.h, 12, gh, gl); - sum64(this.h, 14, hh, hl); -}; - -SHA512.prototype._digest = function digest(enc) { - if (enc === 'hex') - return utils.toHex32(this.h, 'big'); - else - return utils.split32(this.h, 'big'); -}; - -function ch64_hi(xh, xl, yh, yl, zh) { - var r = (xh & yh) ^ ((~xh) & zh); - if (r < 0) - r += 0x100000000; - return r; -} - -function ch64_lo(xh, xl, yh, yl, zh, zl) { - var r = (xl & yl) ^ ((~xl) & zl); - if (r < 0) - r += 0x100000000; - return r; -} - -function maj64_hi(xh, xl, yh, yl, zh) { - var r = (xh & yh) ^ (xh & zh) ^ (yh & zh); - if (r < 0) - r += 0x100000000; - return r; -} - -function maj64_lo(xh, xl, yh, yl, zh, zl) { - var r = (xl & yl) ^ (xl & zl) ^ (yl & zl); - if (r < 0) - r += 0x100000000; - return r; -} - -function s0_512_hi(xh, xl) { - var c0_hi = rotr64_hi(xh, xl, 28); - var c1_hi = rotr64_hi(xl, xh, 2); // 34 - var c2_hi = rotr64_hi(xl, xh, 7); // 39 - - var r = c0_hi ^ c1_hi ^ c2_hi; - if (r < 0) - r += 0x100000000; - return r; -} - -function s0_512_lo(xh, xl) { - var c0_lo = rotr64_lo(xh, xl, 28); - var c1_lo = rotr64_lo(xl, xh, 2); // 34 - var c2_lo = rotr64_lo(xl, xh, 7); // 39 - - var r = c0_lo ^ c1_lo ^ c2_lo; - if (r < 0) - r += 0x100000000; - return r; -} - -function s1_512_hi(xh, xl) { - var c0_hi = rotr64_hi(xh, xl, 14); - var c1_hi = rotr64_hi(xh, xl, 18); - var c2_hi = rotr64_hi(xl, xh, 9); // 41 - - var r = c0_hi ^ c1_hi ^ c2_hi; - if (r < 0) - r += 0x100000000; - return r; -} - -function s1_512_lo(xh, xl) { - var c0_lo = rotr64_lo(xh, xl, 14); - var c1_lo = rotr64_lo(xh, xl, 18); - var c2_lo = rotr64_lo(xl, xh, 9); // 41 - - var r = c0_lo ^ c1_lo ^ c2_lo; - if (r < 0) - r += 0x100000000; - return r; -} - -function g0_512_hi(xh, xl) { - var c0_hi = rotr64_hi(xh, xl, 1); - var c1_hi = rotr64_hi(xh, xl, 8); - var c2_hi = shr64_hi(xh, xl, 7); - - var r = c0_hi ^ c1_hi ^ c2_hi; - if (r < 0) - r += 0x100000000; - return r; -} - -function g0_512_lo(xh, xl) { - var c0_lo = rotr64_lo(xh, xl, 1); - var c1_lo = rotr64_lo(xh, xl, 8); - var c2_lo = shr64_lo(xh, xl, 7); - - var r = c0_lo ^ c1_lo ^ c2_lo; - if (r < 0) - r += 0x100000000; - return r; -} - -function g1_512_hi(xh, xl) { - var c0_hi = rotr64_hi(xh, xl, 19); - var c1_hi = rotr64_hi(xl, xh, 29); // 61 - var c2_hi = shr64_hi(xh, xl, 6); - - var r = c0_hi ^ c1_hi ^ c2_hi; - if (r < 0) - r += 0x100000000; - return r; -} - -function g1_512_lo(xh, xl) { - var c0_lo = rotr64_lo(xh, xl, 19); - var c1_lo = rotr64_lo(xl, xh, 29); // 61 - var c2_lo = shr64_lo(xh, xl, 6); - - var r = c0_lo ^ c1_lo ^ c2_lo; - if (r < 0) - r += 0x100000000; - return r; -} - -},{"../common":141,"../utils":151,"minimalistic-assert":160}],150:[function(require,module,exports){ -'use strict'; - -var utils = require('../utils'); -var rotr32 = utils.rotr32; - -function ft_1(s, x, y, z) { - if (s === 0) - return ch32(x, y, z); - if (s === 1 || s === 3) - return p32(x, y, z); - if (s === 2) - return maj32(x, y, z); -} -exports.ft_1 = ft_1; - -function ch32(x, y, z) { - return (x & y) ^ ((~x) & z); -} -exports.ch32 = ch32; - -function maj32(x, y, z) { - return (x & y) ^ (x & z) ^ (y & z); -} -exports.maj32 = maj32; - -function p32(x, y, z) { - return x ^ y ^ z; -} -exports.p32 = p32; - -function s0_256(x) { - return rotr32(x, 2) ^ rotr32(x, 13) ^ rotr32(x, 22); -} -exports.s0_256 = s0_256; - -function s1_256(x) { - return rotr32(x, 6) ^ rotr32(x, 11) ^ rotr32(x, 25); -} -exports.s1_256 = s1_256; - -function g0_256(x) { - return rotr32(x, 7) ^ rotr32(x, 18) ^ (x >>> 3); -} -exports.g0_256 = g0_256; - -function g1_256(x) { - return rotr32(x, 17) ^ rotr32(x, 19) ^ (x >>> 10); -} -exports.g1_256 = g1_256; - -},{"../utils":151}],151:[function(require,module,exports){ -'use strict'; - -var assert = require('minimalistic-assert'); -var inherits = require('inherits'); - -exports.inherits = inherits; - -function isSurrogatePair(msg, i) { - if ((msg.charCodeAt(i) & 0xFC00) !== 0xD800) { - return false; - } - if (i < 0 || i + 1 >= msg.length) { - return false; - } - return (msg.charCodeAt(i + 1) & 0xFC00) === 0xDC00; -} - -function toArray(msg, enc) { - if (Array.isArray(msg)) - return msg.slice(); - if (!msg) - return []; - var res = []; - if (typeof msg === 'string') { - if (!enc) { - // Inspired by stringToUtf8ByteArray() in closure-library by Google - // https://github.com/google/closure-library/blob/8598d87242af59aac233270742c8984e2b2bdbe0/closure/goog/crypt/crypt.js#L117-L143 - // Apache License 2.0 - // https://github.com/google/closure-library/blob/master/LICENSE - var p = 0; - for (var i = 0; i < msg.length; i++) { - var c = msg.charCodeAt(i); - if (c < 128) { - res[p++] = c; - } else if (c < 2048) { - res[p++] = (c >> 6) | 192; - res[p++] = (c & 63) | 128; - } else if (isSurrogatePair(msg, i)) { - c = 0x10000 + ((c & 0x03FF) << 10) + (msg.charCodeAt(++i) & 0x03FF); - res[p++] = (c >> 18) | 240; - res[p++] = ((c >> 12) & 63) | 128; - res[p++] = ((c >> 6) & 63) | 128; - res[p++] = (c & 63) | 128; - } else { - res[p++] = (c >> 12) | 224; - res[p++] = ((c >> 6) & 63) | 128; - res[p++] = (c & 63) | 128; - } - } - } else if (enc === 'hex') { - msg = msg.replace(/[^a-z0-9]+/ig, ''); - if (msg.length % 2 !== 0) - msg = '0' + msg; - for (i = 0; i < msg.length; i += 2) - res.push(parseInt(msg[i] + msg[i + 1], 16)); - } - } else { - for (i = 0; i < msg.length; i++) - res[i] = msg[i] | 0; - } - return res; -} -exports.toArray = toArray; - -function toHex(msg) { - var res = ''; - for (var i = 0; i < msg.length; i++) - res += zero2(msg[i].toString(16)); - return res; -} -exports.toHex = toHex; - -function htonl(w) { - var res = (w >>> 24) | - ((w >>> 8) & 0xff00) | - ((w << 8) & 0xff0000) | - ((w & 0xff) << 24); - return res >>> 0; -} -exports.htonl = htonl; - -function toHex32(msg, endian) { - var res = ''; - for (var i = 0; i < msg.length; i++) { - var w = msg[i]; - if (endian === 'little') - w = htonl(w); - res += zero8(w.toString(16)); - } - return res; -} -exports.toHex32 = toHex32; - -function zero2(word) { - if (word.length === 1) - return '0' + word; - else - return word; -} -exports.zero2 = zero2; - -function zero8(word) { - if (word.length === 7) - return '0' + word; - else if (word.length === 6) - return '00' + word; - else if (word.length === 5) - return '000' + word; - else if (word.length === 4) - return '0000' + word; - else if (word.length === 3) - return '00000' + word; - else if (word.length === 2) - return '000000' + word; - else if (word.length === 1) - return '0000000' + word; - else - return word; -} -exports.zero8 = zero8; - -function join32(msg, start, end, endian) { - var len = end - start; - assert(len % 4 === 0); - var res = new Array(len / 4); - for (var i = 0, k = start; i < res.length; i++, k += 4) { - var w; - if (endian === 'big') - w = (msg[k] << 24) | (msg[k + 1] << 16) | (msg[k + 2] << 8) | msg[k + 3]; - else - w = (msg[k + 3] << 24) | (msg[k + 2] << 16) | (msg[k + 1] << 8) | msg[k]; - res[i] = w >>> 0; - } - return res; -} -exports.join32 = join32; - -function split32(msg, endian) { - var res = new Array(msg.length * 4); - for (var i = 0, k = 0; i < msg.length; i++, k += 4) { - var m = msg[i]; - if (endian === 'big') { - res[k] = m >>> 24; - res[k + 1] = (m >>> 16) & 0xff; - res[k + 2] = (m >>> 8) & 0xff; - res[k + 3] = m & 0xff; - } else { - res[k + 3] = m >>> 24; - res[k + 2] = (m >>> 16) & 0xff; - res[k + 1] = (m >>> 8) & 0xff; - res[k] = m & 0xff; - } - } - return res; -} -exports.split32 = split32; - -function rotr32(w, b) { - return (w >>> b) | (w << (32 - b)); -} -exports.rotr32 = rotr32; - -function rotl32(w, b) { - return (w << b) | (w >>> (32 - b)); -} -exports.rotl32 = rotl32; - -function sum32(a, b) { - return (a + b) >>> 0; -} -exports.sum32 = sum32; - -function sum32_3(a, b, c) { - return (a + b + c) >>> 0; -} -exports.sum32_3 = sum32_3; - -function sum32_4(a, b, c, d) { - return (a + b + c + d) >>> 0; -} -exports.sum32_4 = sum32_4; - -function sum32_5(a, b, c, d, e) { - return (a + b + c + d + e) >>> 0; -} -exports.sum32_5 = sum32_5; - -function sum64(buf, pos, ah, al) { - var bh = buf[pos]; - var bl = buf[pos + 1]; - - var lo = (al + bl) >>> 0; - var hi = (lo < al ? 1 : 0) + ah + bh; - buf[pos] = hi >>> 0; - buf[pos + 1] = lo; -} -exports.sum64 = sum64; - -function sum64_hi(ah, al, bh, bl) { - var lo = (al + bl) >>> 0; - var hi = (lo < al ? 1 : 0) + ah + bh; - return hi >>> 0; -} -exports.sum64_hi = sum64_hi; - -function sum64_lo(ah, al, bh, bl) { - var lo = al + bl; - return lo >>> 0; -} -exports.sum64_lo = sum64_lo; - -function sum64_4_hi(ah, al, bh, bl, ch, cl, dh, dl) { - var carry = 0; - var lo = al; - lo = (lo + bl) >>> 0; - carry += lo < al ? 1 : 0; - lo = (lo + cl) >>> 0; - carry += lo < cl ? 1 : 0; - lo = (lo + dl) >>> 0; - carry += lo < dl ? 1 : 0; - - var hi = ah + bh + ch + dh + carry; - return hi >>> 0; -} -exports.sum64_4_hi = sum64_4_hi; - -function sum64_4_lo(ah, al, bh, bl, ch, cl, dh, dl) { - var lo = al + bl + cl + dl; - return lo >>> 0; -} -exports.sum64_4_lo = sum64_4_lo; - -function sum64_5_hi(ah, al, bh, bl, ch, cl, dh, dl, eh, el) { - var carry = 0; - var lo = al; - lo = (lo + bl) >>> 0; - carry += lo < al ? 1 : 0; - lo = (lo + cl) >>> 0; - carry += lo < cl ? 1 : 0; - lo = (lo + dl) >>> 0; - carry += lo < dl ? 1 : 0; - lo = (lo + el) >>> 0; - carry += lo < el ? 1 : 0; - - var hi = ah + bh + ch + dh + eh + carry; - return hi >>> 0; -} -exports.sum64_5_hi = sum64_5_hi; - -function sum64_5_lo(ah, al, bh, bl, ch, cl, dh, dl, eh, el) { - var lo = al + bl + cl + dl + el; - - return lo >>> 0; -} -exports.sum64_5_lo = sum64_5_lo; - -function rotr64_hi(ah, al, num) { - var r = (al << (32 - num)) | (ah >>> num); - return r >>> 0; -} -exports.rotr64_hi = rotr64_hi; - -function rotr64_lo(ah, al, num) { - var r = (ah << (32 - num)) | (al >>> num); - return r >>> 0; -} -exports.rotr64_lo = rotr64_lo; - -function shr64_hi(ah, al, num) { - return ah >>> num; -} -exports.shr64_hi = shr64_hi; - -function shr64_lo(ah, al, num) { - var r = (ah << (32 - num)) | (al >>> num); - return r >>> 0; -} -exports.shr64_lo = shr64_lo; - -},{"inherits":155,"minimalistic-assert":160}],152:[function(require,module,exports){ -'use strict'; - -var call = Function.prototype.call; -var $hasOwn = Object.prototype.hasOwnProperty; -var bind = require('function-bind'); - -/** @type {import('.')} */ -module.exports = bind.call(call, $hasOwn); - -},{"function-bind":132}],153:[function(require,module,exports){ -'use strict'; - -var hash = require('hash.js'); -var utils = require('minimalistic-crypto-utils'); -var assert = require('minimalistic-assert'); - -function HmacDRBG(options) { - if (!(this instanceof HmacDRBG)) - return new HmacDRBG(options); - this.hash = options.hash; - this.predResist = !!options.predResist; - - this.outLen = this.hash.outSize; - this.minEntropy = options.minEntropy || this.hash.hmacStrength; - - this._reseed = null; - this.reseedInterval = null; - this.K = null; - this.V = null; - - var entropy = utils.toArray(options.entropy, options.entropyEnc || 'hex'); - var nonce = utils.toArray(options.nonce, options.nonceEnc || 'hex'); - var pers = utils.toArray(options.pers, options.persEnc || 'hex'); - assert(entropy.length >= (this.minEntropy / 8), - 'Not enough entropy. Minimum is: ' + this.minEntropy + ' bits'); - this._init(entropy, nonce, pers); -} -module.exports = HmacDRBG; - -HmacDRBG.prototype._init = function init(entropy, nonce, pers) { - var seed = entropy.concat(nonce).concat(pers); - - this.K = new Array(this.outLen / 8); - this.V = new Array(this.outLen / 8); - for (var i = 0; i < this.V.length; i++) { - this.K[i] = 0x00; - this.V[i] = 0x01; - } - - this._update(seed); - this._reseed = 1; - this.reseedInterval = 0x1000000000000; // 2^48 -}; - -HmacDRBG.prototype._hmac = function hmac() { - return new hash.hmac(this.hash, this.K); -}; - -HmacDRBG.prototype._update = function update(seed) { - var kmac = this._hmac() - .update(this.V) - .update([ 0x00 ]); - if (seed) - kmac = kmac.update(seed); - this.K = kmac.digest(); - this.V = this._hmac().update(this.V).digest(); - if (!seed) - return; - - this.K = this._hmac() - .update(this.V) - .update([ 0x01 ]) - .update(seed) - .digest(); - this.V = this._hmac().update(this.V).digest(); -}; - -HmacDRBG.prototype.reseed = function reseed(entropy, entropyEnc, add, addEnc) { - // Optional entropy enc - if (typeof entropyEnc !== 'string') { - addEnc = add; - add = entropyEnc; - entropyEnc = null; - } - - entropy = utils.toArray(entropy, entropyEnc); - add = utils.toArray(add, addEnc); - - assert(entropy.length >= (this.minEntropy / 8), - 'Not enough entropy. Minimum is: ' + this.minEntropy + ' bits'); - - this._update(entropy.concat(add || [])); - this._reseed = 1; -}; - -HmacDRBG.prototype.generate = function generate(len, enc, add, addEnc) { - if (this._reseed > this.reseedInterval) - throw new Error('Reseed is required'); - - // Optional encoding - if (typeof enc !== 'string') { - addEnc = add; - add = enc; - enc = null; - } - - // Optional additional data - if (add) { - add = utils.toArray(add, addEnc || 'hex'); - this._update(add); - } - - var temp = []; - while (temp.length < len) { - this.V = this._hmac().update(this.V).digest(); - temp = temp.concat(this.V); - } - - var res = temp.slice(0, len); - this._update(add); - this._reseed++; - return utils.encode(res, enc); -}; - -},{"hash.js":140,"minimalistic-assert":160,"minimalistic-crypto-utils":161}],154:[function(require,module,exports){ -/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh */ -exports.read = function (buffer, offset, isLE, mLen, nBytes) { - var e, m - var eLen = (nBytes * 8) - mLen - 1 - var eMax = (1 << eLen) - 1 - var eBias = eMax >> 1 - var nBits = -7 - var i = isLE ? (nBytes - 1) : 0 - var d = isLE ? -1 : 1 - var s = buffer[offset + i] - - i += d - - e = s & ((1 << (-nBits)) - 1) - s >>= (-nBits) - nBits += eLen - for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {} - - m = e & ((1 << (-nBits)) - 1) - e >>= (-nBits) - nBits += mLen - for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {} - - if (e === 0) { - e = 1 - eBias - } else if (e === eMax) { - return m ? NaN : ((s ? -1 : 1) * Infinity) - } else { - m = m + Math.pow(2, mLen) - e = e - eBias - } - return (s ? -1 : 1) * m * Math.pow(2, e - mLen) -} - -exports.write = function (buffer, value, offset, isLE, mLen, nBytes) { - var e, m, c - var eLen = (nBytes * 8) - mLen - 1 - var eMax = (1 << eLen) - 1 - var eBias = eMax >> 1 - var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0) - var i = isLE ? 0 : (nBytes - 1) - var d = isLE ? 1 : -1 - var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0 - - value = Math.abs(value) - - if (isNaN(value) || value === Infinity) { - m = isNaN(value) ? 1 : 0 - e = eMax - } else { - e = Math.floor(Math.log(value) / Math.LN2) - if (value * (c = Math.pow(2, -e)) < 1) { - e-- - c *= 2 - } - if (e + eBias >= 1) { - value += rt / c - } else { - value += rt * Math.pow(2, 1 - eBias) - } - if (value * c >= 2) { - e++ - c /= 2 - } - - if (e + eBias >= eMax) { - m = 0 - e = eMax - } else if (e + eBias >= 1) { - m = ((value * c) - 1) * Math.pow(2, mLen) - e = e + eBias - } else { - m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen) - e = 0 - } - } - - for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {} - - e = (e << mLen) | m - eLen += mLen - for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {} - - buffer[offset + i - d] |= s * 128 -} - -},{}],155:[function(require,module,exports){ -if (typeof Object.create === 'function') { - // implementation from standard node.js 'util' module - module.exports = function inherits(ctor, superCtor) { - if (superCtor) { - ctor.super_ = superCtor - ctor.prototype = Object.create(superCtor.prototype, { - constructor: { - value: ctor, - enumerable: false, - writable: true, - configurable: true - } - }) - } - }; -} else { - // old school shim for old browsers - module.exports = function inherits(ctor, superCtor) { - if (superCtor) { - ctor.super_ = superCtor - var TempCtor = function () {} - TempCtor.prototype = superCtor.prototype - ctor.prototype = new TempCtor() - ctor.prototype.constructor = ctor - } - } -} - -},{}],156:[function(require,module,exports){ -var toString = {}.toString; - -module.exports = Array.isArray || function (arr) { - return toString.call(arr) == '[object Array]'; -}; - -},{}],157:[function(require,module,exports){ -'use strict' -var inherits = require('inherits') -var HashBase = require('hash-base') -var Buffer = require('safe-buffer').Buffer - -var ARRAY16 = new Array(16) - -function MD5 () { - HashBase.call(this, 64) - - // state - this._a = 0x67452301 - this._b = 0xefcdab89 - this._c = 0x98badcfe - this._d = 0x10325476 -} - -inherits(MD5, HashBase) - -MD5.prototype._update = function () { - var M = ARRAY16 - for (var i = 0; i < 16; ++i) M[i] = this._block.readInt32LE(i * 4) - - var a = this._a - var b = this._b - var c = this._c - var d = this._d - - a = fnF(a, b, c, d, M[0], 0xd76aa478, 7) - d = fnF(d, a, b, c, M[1], 0xe8c7b756, 12) - c = fnF(c, d, a, b, M[2], 0x242070db, 17) - b = fnF(b, c, d, a, M[3], 0xc1bdceee, 22) - a = fnF(a, b, c, d, M[4], 0xf57c0faf, 7) - d = fnF(d, a, b, c, M[5], 0x4787c62a, 12) - c = fnF(c, d, a, b, M[6], 0xa8304613, 17) - b = fnF(b, c, d, a, M[7], 0xfd469501, 22) - a = fnF(a, b, c, d, M[8], 0x698098d8, 7) - d = fnF(d, a, b, c, M[9], 0x8b44f7af, 12) - c = fnF(c, d, a, b, M[10], 0xffff5bb1, 17) - b = fnF(b, c, d, a, M[11], 0x895cd7be, 22) - a = fnF(a, b, c, d, M[12], 0x6b901122, 7) - d = fnF(d, a, b, c, M[13], 0xfd987193, 12) - c = fnF(c, d, a, b, M[14], 0xa679438e, 17) - b = fnF(b, c, d, a, M[15], 0x49b40821, 22) - - a = fnG(a, b, c, d, M[1], 0xf61e2562, 5) - d = fnG(d, a, b, c, M[6], 0xc040b340, 9) - c = fnG(c, d, a, b, M[11], 0x265e5a51, 14) - b = fnG(b, c, d, a, M[0], 0xe9b6c7aa, 20) - a = fnG(a, b, c, d, M[5], 0xd62f105d, 5) - d = fnG(d, a, b, c, M[10], 0x02441453, 9) - c = fnG(c, d, a, b, M[15], 0xd8a1e681, 14) - b = fnG(b, c, d, a, M[4], 0xe7d3fbc8, 20) - a = fnG(a, b, c, d, M[9], 0x21e1cde6, 5) - d = fnG(d, a, b, c, M[14], 0xc33707d6, 9) - c = fnG(c, d, a, b, M[3], 0xf4d50d87, 14) - b = fnG(b, c, d, a, M[8], 0x455a14ed, 20) - a = fnG(a, b, c, d, M[13], 0xa9e3e905, 5) - d = fnG(d, a, b, c, M[2], 0xfcefa3f8, 9) - c = fnG(c, d, a, b, M[7], 0x676f02d9, 14) - b = fnG(b, c, d, a, M[12], 0x8d2a4c8a, 20) - - a = fnH(a, b, c, d, M[5], 0xfffa3942, 4) - d = fnH(d, a, b, c, M[8], 0x8771f681, 11) - c = fnH(c, d, a, b, M[11], 0x6d9d6122, 16) - b = fnH(b, c, d, a, M[14], 0xfde5380c, 23) - a = fnH(a, b, c, d, M[1], 0xa4beea44, 4) - d = fnH(d, a, b, c, M[4], 0x4bdecfa9, 11) - c = fnH(c, d, a, b, M[7], 0xf6bb4b60, 16) - b = fnH(b, c, d, a, M[10], 0xbebfbc70, 23) - a = fnH(a, b, c, d, M[13], 0x289b7ec6, 4) - d = fnH(d, a, b, c, M[0], 0xeaa127fa, 11) - c = fnH(c, d, a, b, M[3], 0xd4ef3085, 16) - b = fnH(b, c, d, a, M[6], 0x04881d05, 23) - a = fnH(a, b, c, d, M[9], 0xd9d4d039, 4) - d = fnH(d, a, b, c, M[12], 0xe6db99e5, 11) - c = fnH(c, d, a, b, M[15], 0x1fa27cf8, 16) - b = fnH(b, c, d, a, M[2], 0xc4ac5665, 23) - - a = fnI(a, b, c, d, M[0], 0xf4292244, 6) - d = fnI(d, a, b, c, M[7], 0x432aff97, 10) - c = fnI(c, d, a, b, M[14], 0xab9423a7, 15) - b = fnI(b, c, d, a, M[5], 0xfc93a039, 21) - a = fnI(a, b, c, d, M[12], 0x655b59c3, 6) - d = fnI(d, a, b, c, M[3], 0x8f0ccc92, 10) - c = fnI(c, d, a, b, M[10], 0xffeff47d, 15) - b = fnI(b, c, d, a, M[1], 0x85845dd1, 21) - a = fnI(a, b, c, d, M[8], 0x6fa87e4f, 6) - d = fnI(d, a, b, c, M[15], 0xfe2ce6e0, 10) - c = fnI(c, d, a, b, M[6], 0xa3014314, 15) - b = fnI(b, c, d, a, M[13], 0x4e0811a1, 21) - a = fnI(a, b, c, d, M[4], 0xf7537e82, 6) - d = fnI(d, a, b, c, M[11], 0xbd3af235, 10) - c = fnI(c, d, a, b, M[2], 0x2ad7d2bb, 15) - b = fnI(b, c, d, a, M[9], 0xeb86d391, 21) - - this._a = (this._a + a) | 0 - this._b = (this._b + b) | 0 - this._c = (this._c + c) | 0 - this._d = (this._d + d) | 0 -} - -MD5.prototype._digest = function () { - // create padding and handle blocks - this._block[this._blockOffset++] = 0x80 - if (this._blockOffset > 56) { - this._block.fill(0, this._blockOffset, 64) - this._update() - this._blockOffset = 0 - } - - this._block.fill(0, this._blockOffset, 56) - this._block.writeUInt32LE(this._length[0], 56) - this._block.writeUInt32LE(this._length[1], 60) - this._update() - - // produce result - var buffer = Buffer.allocUnsafe(16) - buffer.writeInt32LE(this._a, 0) - buffer.writeInt32LE(this._b, 4) - buffer.writeInt32LE(this._c, 8) - buffer.writeInt32LE(this._d, 12) - return buffer -} - -function rotl (x, n) { - return (x << n) | (x >>> (32 - n)) -} - -function fnF (a, b, c, d, m, k, s) { - return (rotl((a + ((b & c) | ((~b) & d)) + m + k) | 0, s) + b) | 0 -} - -function fnG (a, b, c, d, m, k, s) { - return (rotl((a + ((b & d) | (c & (~d))) + m + k) | 0, s) + b) | 0 -} - -function fnH (a, b, c, d, m, k, s) { - return (rotl((a + (b ^ c ^ d) + m + k) | 0, s) + b) | 0 -} - -function fnI (a, b, c, d, m, k, s) { - return (rotl((a + ((c ^ (b | (~d)))) + m + k) | 0, s) + b) | 0 -} - -module.exports = MD5 - -},{"hash-base":139,"inherits":155,"safe-buffer":221}],158:[function(require,module,exports){ -var bn = require('bn.js'); -var brorand = require('brorand'); - -function MillerRabin(rand) { - this.rand = rand || new brorand.Rand(); -} -module.exports = MillerRabin; - -MillerRabin.create = function create(rand) { - return new MillerRabin(rand); -}; - -MillerRabin.prototype._randbelow = function _randbelow(n) { - var len = n.bitLength(); - var min_bytes = Math.ceil(len / 8); - - // Generage random bytes until a number less than n is found. - // This ensures that 0..n-1 have an equal probability of being selected. - do - var a = new bn(this.rand.generate(min_bytes)); - while (a.cmp(n) >= 0); - - return a; -}; - -MillerRabin.prototype._randrange = function _randrange(start, stop) { - // Generate a random number greater than or equal to start and less than stop. - var size = stop.sub(start); - return start.add(this._randbelow(size)); -}; - -MillerRabin.prototype.test = function test(n, k, cb) { - var len = n.bitLength(); - var red = bn.mont(n); - var rone = new bn(1).toRed(red); - - if (!k) - k = Math.max(1, (len / 48) | 0); - - // Find d and s, (n - 1) = (2 ^ s) * d; - var n1 = n.subn(1); - for (var s = 0; !n1.testn(s); s++) {} - var d = n.shrn(s); - - var rn1 = n1.toRed(red); - - var prime = true; - for (; k > 0; k--) { - var a = this._randrange(new bn(2), n1); - if (cb) - cb(a); - - var x = a.toRed(red).redPow(d); - if (x.cmp(rone) === 0 || x.cmp(rn1) === 0) - continue; - - for (var i = 1; i < s; i++) { - x = x.redSqr(); - - if (x.cmp(rone) === 0) - return false; - if (x.cmp(rn1) === 0) - break; - } - - if (i === s) - return false; - } - - return prime; -}; - -MillerRabin.prototype.getDivisor = function getDivisor(n, k) { - var len = n.bitLength(); - var red = bn.mont(n); - var rone = new bn(1).toRed(red); - - if (!k) - k = Math.max(1, (len / 48) | 0); - - // Find d and s, (n - 1) = (2 ^ s) * d; - var n1 = n.subn(1); - for (var s = 0; !n1.testn(s); s++) {} - var d = n.shrn(s); - - var rn1 = n1.toRed(red); - - for (; k > 0; k--) { - var a = this._randrange(new bn(2), n1); - - var g = n.gcd(a); - if (g.cmpn(1) !== 0) - return g; - - var x = a.toRed(red).redPow(d); - if (x.cmp(rone) === 0 || x.cmp(rn1) === 0) - continue; - - for (var i = 1; i < s; i++) { - x = x.redSqr(); - - if (x.cmp(rone) === 0) - return x.fromRed().subn(1).gcd(n); - if (x.cmp(rn1) === 0) - break; - } - - if (i === s) { - x = x.redSqr(); - return x.fromRed().subn(1).gcd(n); - } - } - - return false; -}; - -},{"bn.js":159,"brorand":33}],159:[function(require,module,exports){ -arguments[4][20][0].apply(exports,arguments) -},{"buffer":34,"dup":20}],160:[function(require,module,exports){ -module.exports = assert; - -function assert(val, msg) { - if (!val) - throw new Error(msg || 'Assertion failed'); -} - -assert.equal = function assertEqual(l, r, msg) { - if (l != r) - throw new Error(msg || ('Assertion failed: ' + l + ' != ' + r)); -}; - -},{}],161:[function(require,module,exports){ -'use strict'; - -var utils = exports; - -function toArray(msg, enc) { - if (Array.isArray(msg)) - return msg.slice(); - if (!msg) - return []; - var res = []; - if (typeof msg !== 'string') { - for (var i = 0; i < msg.length; i++) - res[i] = msg[i] | 0; - return res; - } - if (enc === 'hex') { - msg = msg.replace(/[^a-z0-9]+/ig, ''); - if (msg.length % 2 !== 0) - msg = '0' + msg; - for (var i = 0; i < msg.length; i += 2) - res.push(parseInt(msg[i] + msg[i + 1], 16)); - } else { - for (var i = 0; i < msg.length; i++) { - var c = msg.charCodeAt(i); - var hi = c >> 8; - var lo = c & 0xff; - if (hi) - res.push(hi, lo); - else - res.push(lo); - } - } - return res; -} -utils.toArray = toArray; - -function zero2(word) { - if (word.length === 1) - return '0' + word; - else - return word; -} -utils.zero2 = zero2; - -function toHex(msg) { - var res = ''; - for (var i = 0; i < msg.length; i++) - res += zero2(msg[i].toString(16)); - return res; -} -utils.toHex = toHex; - -utils.encode = function encode(arr, enc) { - if (enc === 'hex') - return toHex(arr); - else - return arr; -}; - -},{}],162:[function(require,module,exports){ -module.exports = require("./lib/hkdf"); -},{"./lib/hkdf":163}],163:[function(require,module,exports){ -(function (Buffer){(function (){ -// -// a straightforward implementation of HKDF -// -// https://tools.ietf.org/html/rfc5869 -// - -var crypto = require("crypto"); - -function zeros(length) { - var buf = new Buffer(length); - - buf.fill(0); - - return buf.toString(); -} -// imk is initial keying material -function HKDF(hashAlg, salt, ikm) { - this.hashAlg = hashAlg; - - // create the hash alg to see if it exists and get its length - var hash = crypto.createHash(this.hashAlg); - this.hashLength = hash.digest().length; - - this.salt = salt || zeros(this.hashLength); - this.ikm = ikm; - - // now we compute the PRK - var hmac = crypto.createHmac(this.hashAlg, this.salt); - hmac.update(this.ikm); - this.prk = hmac.digest(); -} - -HKDF.prototype = { - derive: function(info, size) { - var prev = new Buffer(0); - var output; - var buffers = []; - var num_blocks = Math.ceil(size / this.hashLength); - info = new Buffer(info); - - for (var i=0; i -1) { - this.$options.signingSchemeOptions = { - hash: signingScheme[0] - }; - this.$options.signingScheme = DEFAULT_SIGNING_SCHEME; - } else { - this.$options.signingScheme = signingScheme[0]; - this.$options.signingSchemeOptions = { - hash: null - }; - } - } else { - this.$options.signingSchemeOptions = { - hash: signingScheme[1] - }; - this.$options.signingScheme = signingScheme[0]; - } - } else if (_.isObject(options.signingScheme)) { - this.$options.signingScheme = options.signingScheme.scheme || DEFAULT_SIGNING_SCHEME; - this.$options.signingSchemeOptions = _.omit(options.signingScheme, 'scheme'); - } - - if (!schemes.isSignature(this.$options.signingScheme)) { - throw Error('Unsupported signing scheme'); - } - - if (this.$options.signingSchemeOptions.hash && - SUPPORTED_HASH_ALGORITHMS[this.$options.environment].indexOf(this.$options.signingSchemeOptions.hash) === -1) { - throw Error('Unsupported hashing algorithm for ' + this.$options.environment + ' environment'); - } - } - - if (options.encryptionScheme) { - if (_.isString(options.encryptionScheme)) { - this.$options.encryptionScheme = options.encryptionScheme.toLowerCase(); - this.$options.encryptionSchemeOptions = {}; - } else if (_.isObject(options.encryptionScheme)) { - this.$options.encryptionScheme = options.encryptionScheme.scheme || DEFAULT_ENCRYPTION_SCHEME; - this.$options.encryptionSchemeOptions = _.omit(options.encryptionScheme, 'scheme'); - } - - if (!schemes.isEncryption(this.$options.encryptionScheme)) { - throw Error('Unsupported encryption scheme'); - } - - if (this.$options.encryptionSchemeOptions.hash && - SUPPORTED_HASH_ALGORITHMS[this.$options.environment].indexOf(this.$options.encryptionSchemeOptions.hash) === -1) { - throw Error('Unsupported hashing algorithm for ' + this.$options.environment + ' environment'); - } - } - - this.keyPair.setOptions(this.$options); - }; - - /** - * Generate private/public keys pair - * - * @param bits {int} length key in bits. Default 2048. - * @param exp {int} public exponent. Default 65537. - * @returns {NodeRSA} - */ - NodeRSA.prototype.generateKeyPair = function (bits, exp) { - bits = bits || 2048; - exp = exp || 65537; - - if (bits % 8 !== 0) { - throw Error('Key size must be a multiple of 8.'); - } - - this.keyPair.generate(bits, exp.toString(16)); - this.$cache = {}; - return this; - }; - - /** - * Importing key - * @param keyData {string|buffer|Object} - * @param format {string} - */ - NodeRSA.prototype.importKey = function (keyData, format) { - if (!keyData) { - throw Error("Empty key given"); - } - - if (format) { - format = EXPORT_FORMAT_ALIASES[format] || format; - } - - if (!formats.detectAndImport(this.keyPair, keyData, format) && format === undefined) { - throw Error("Key format must be specified"); - } - - this.$cache = {}; - - return this; - }; - - /** - * Exporting key - * @param [format] {string} - */ - NodeRSA.prototype.exportKey = function (format) { - format = format || DEFAULT_EXPORT_FORMAT; - format = EXPORT_FORMAT_ALIASES[format] || format; - - if (!this.$cache[format]) { - this.$cache[format] = formats.detectAndExport(this.keyPair, format); - } - - return this.$cache[format]; - }; - - /** - * Check if key pair contains private key - */ - NodeRSA.prototype.isPrivate = function () { - return this.keyPair.isPrivate(); - }; - - /** - * Check if key pair contains public key - * @param [strict] {boolean} - public key only, return false if have private exponent - */ - NodeRSA.prototype.isPublic = function (strict) { - return this.keyPair.isPublic(strict); - }; - - /** - * Check if key pair doesn't contains any data - */ - NodeRSA.prototype.isEmpty = function (strict) { - return !(this.keyPair.n || this.keyPair.e || this.keyPair.d); - }; - - /** - * Encrypting data method with public key - * - * @param buffer {string|number|object|array|Buffer} - data for encrypting. Object and array will convert to JSON string. - * @param encoding {string} - optional. Encoding for output result, may be 'buffer', 'binary', 'hex' or 'base64'. Default 'buffer'. - * @param source_encoding {string} - optional. Encoding for given string. Default utf8. - * @returns {string|Buffer} - */ - NodeRSA.prototype.encrypt = function (buffer, encoding, source_encoding) { - return this.$$encryptKey(false, buffer, encoding, source_encoding); - }; - - /** - * Decrypting data method with private key - * - * @param buffer {Buffer} - buffer for decrypting - * @param encoding - encoding for result string, can also take 'json' or 'buffer' for the automatic conversion of this type - * @returns {Buffer|object|string} - */ - NodeRSA.prototype.decrypt = function (buffer, encoding) { - return this.$$decryptKey(false, buffer, encoding); - }; - - /** - * Encrypting data method with private key - * - * Parameters same as `encrypt` method - */ - NodeRSA.prototype.encryptPrivate = function (buffer, encoding, source_encoding) { - return this.$$encryptKey(true, buffer, encoding, source_encoding); - }; - - /** - * Decrypting data method with public key - * - * Parameters same as `decrypt` method - */ - NodeRSA.prototype.decryptPublic = function (buffer, encoding) { - return this.$$decryptKey(true, buffer, encoding); - }; - - /** - * Encrypting data method with custom key - */ - NodeRSA.prototype.$$encryptKey = function (usePrivate, buffer, encoding, source_encoding) { - try { - var res = this.keyPair.encrypt(this.$getDataForEncrypt(buffer, source_encoding), usePrivate); - - if (encoding == 'buffer' || !encoding) { - return res; - } else { - return res.toString(encoding); - } - } catch (e) { - throw Error('Error during encryption. Original error: ' + e); - } - }; - - /** - * Decrypting data method with custom key - */ - NodeRSA.prototype.$$decryptKey = function (usePublic, buffer, encoding) { - try { - buffer = _.isString(buffer) ? Buffer.from(buffer, 'base64') : buffer; - var res = this.keyPair.decrypt(buffer, usePublic); - - if (res === null) { - throw Error('Key decrypt method returns null.'); - } - - return this.$getDecryptedData(res, encoding); - } catch (e) { - throw Error('Error during decryption (probably incorrect key). Original error: ' + e); - } - }; - - /** - * Signing data - * - * @param buffer {string|number|object|array|Buffer} - data for signing. Object and array will convert to JSON string. - * @param encoding {string} - optional. Encoding for output result, may be 'buffer', 'binary', 'hex' or 'base64'. Default 'buffer'. - * @param source_encoding {string} - optional. Encoding for given string. Default utf8. - * @returns {string|Buffer} - */ - NodeRSA.prototype.sign = function (buffer, encoding, source_encoding) { - if (!this.isPrivate()) { - throw Error("This is not private key"); - } - - var res = this.keyPair.sign(this.$getDataForEncrypt(buffer, source_encoding)); - - if (encoding && encoding != 'buffer') { - res = res.toString(encoding); - } - - return res; - }; - - /** - * Verifying signed data - * - * @param buffer - signed data - * @param signature - * @param source_encoding {string} - optional. Encoding for given string. Default utf8. - * @param signature_encoding - optional. Encoding of given signature. May be 'buffer', 'binary', 'hex' or 'base64'. Default 'buffer'. - * @returns {*} - */ - NodeRSA.prototype.verify = function (buffer, signature, source_encoding, signature_encoding) { - if (!this.isPublic()) { - throw Error("This is not public key"); - } - signature_encoding = (!signature_encoding || signature_encoding == 'buffer' ? null : signature_encoding); - return this.keyPair.verify(this.$getDataForEncrypt(buffer, source_encoding), signature, signature_encoding); - }; - - /** - * Returns key size in bits - * @returns {int} - */ - NodeRSA.prototype.getKeySize = function () { - return this.keyPair.keySize; - }; - - /** - * Returns max message length in bytes (for 1 chunk) depending on current encryption scheme - * @returns {int} - */ - NodeRSA.prototype.getMaxMessageSize = function () { - return this.keyPair.maxMessageLength; - }; - - /** - * Preparing given data for encrypting/signing. Just make new/return Buffer object. - * - * @param buffer {string|number|object|array|Buffer} - data for encrypting. Object and array will convert to JSON string. - * @param encoding {string} - optional. Encoding for given string. Default utf8. - * @returns {Buffer} - */ - NodeRSA.prototype.$getDataForEncrypt = function (buffer, encoding) { - if (_.isString(buffer) || _.isNumber(buffer)) { - return Buffer.from('' + buffer, encoding || 'utf8'); - } else if (Buffer.isBuffer(buffer)) { - return buffer; - } else if (_.isObject(buffer)) { - return Buffer.from(JSON.stringify(buffer)); - } else { - throw Error("Unexpected data type"); - } - }; - - /** - * - * @param buffer {Buffer} - decrypted data. - * @param encoding - optional. Encoding for result output. May be 'buffer', 'json' or any of Node.js Buffer supported encoding. - * @returns {*} - */ - NodeRSA.prototype.$getDecryptedData = function (buffer, encoding) { - encoding = encoding || 'buffer'; - - if (encoding == 'buffer') { - return buffer; - } else if (encoding == 'json') { - return JSON.parse(buffer.toString()); - } else { - return buffer.toString(encoding); - } - }; - - return NodeRSA; -})(); - -}).call(this)}).call(this,require("buffer").Buffer) -},{"./formats/formats.js":170,"./libs/rsa.js":175,"./schemes/schemes.js":179,"./utils":180,"asn1":26,"buffer":63,"constants":78,"crypto":91}],165:[function(require,module,exports){ -var crypt = require('crypto'); - -module.exports = { - getEngine: function (keyPair, options) { - var engine = require('./js.js'); - if (options.environment === 'node') { - if (typeof crypt.publicEncrypt === 'function' && typeof crypt.privateDecrypt === 'function') { - if (typeof crypt.privateEncrypt === 'function' && typeof crypt.publicDecrypt === 'function') { - engine = require('./io.js'); - } else { - engine = require('./node12.js'); - } - } - } - return engine(keyPair, options); - } -}; -},{"./io.js":166,"./js.js":167,"./node12.js":168,"crypto":91}],166:[function(require,module,exports){ -var crypto = require('crypto'); -var constants = require('constants'); -var schemes = require('../schemes/schemes.js'); - -module.exports = function (keyPair, options) { - var pkcs1Scheme = schemes.pkcs1.makeScheme(keyPair, options); - - return { - encrypt: function (buffer, usePrivate) { - var padding; - if (usePrivate) { - padding = constants.RSA_PKCS1_PADDING; - if (options.encryptionSchemeOptions && options.encryptionSchemeOptions.padding) { - padding = options.encryptionSchemeOptions.padding; - } - return crypto.privateEncrypt({ - key: options.rsaUtils.exportKey('private'), - padding: padding - }, buffer); - } else { - padding = constants.RSA_PKCS1_OAEP_PADDING; - if (options.encryptionScheme === 'pkcs1') { - padding = constants.RSA_PKCS1_PADDING; - } - if (options.encryptionSchemeOptions && options.encryptionSchemeOptions.padding) { - padding = options.encryptionSchemeOptions.padding; - } - - var data = buffer; - if (padding === constants.RSA_NO_PADDING) { - data = pkcs1Scheme.pkcs0pad(buffer); - } - - return crypto.publicEncrypt({ - key: options.rsaUtils.exportKey('public'), - padding: padding - }, data); - } - }, - - decrypt: function (buffer, usePublic) { - var padding; - if (usePublic) { - padding = constants.RSA_PKCS1_PADDING; - if (options.encryptionSchemeOptions && options.encryptionSchemeOptions.padding) { - padding = options.encryptionSchemeOptions.padding; - } - return crypto.publicDecrypt({ - key: options.rsaUtils.exportKey('public'), - padding: padding - }, buffer); - } else { - padding = constants.RSA_PKCS1_OAEP_PADDING; - if (options.encryptionScheme === 'pkcs1') { - padding = constants.RSA_PKCS1_PADDING; - } - if (options.encryptionSchemeOptions && options.encryptionSchemeOptions.padding) { - padding = options.encryptionSchemeOptions.padding; - } - var res = crypto.privateDecrypt({ - key: options.rsaUtils.exportKey('private'), - padding: padding - }, buffer); - - if (padding === constants.RSA_NO_PADDING) { - return pkcs1Scheme.pkcs0unpad(res); - } - return res; - } - } - }; -}; -},{"../schemes/schemes.js":179,"constants":78,"crypto":91}],167:[function(require,module,exports){ -var BigInteger = require('../libs/jsbn.js'); -var schemes = require('../schemes/schemes.js'); - -module.exports = function (keyPair, options) { - var pkcs1Scheme = schemes.pkcs1.makeScheme(keyPair, options); - - return { - encrypt: function (buffer, usePrivate) { - var m, c; - if (usePrivate) { - /* Type 1: zeros padding for private key encrypt */ - m = new BigInteger(pkcs1Scheme.encPad(buffer, {type: 1})); - c = keyPair.$doPrivate(m); - } else { - m = new BigInteger(keyPair.encryptionScheme.encPad(buffer)); - c = keyPair.$doPublic(m); - } - return c.toBuffer(keyPair.encryptedDataLength); - }, - - decrypt: function (buffer, usePublic) { - var m, c = new BigInteger(buffer); - - if (usePublic) { - m = keyPair.$doPublic(c); - /* Type 1: zeros padding for private key decrypt */ - return pkcs1Scheme.encUnPad(m.toBuffer(keyPair.encryptedDataLength), {type: 1}); - } else { - m = keyPair.$doPrivate(c); - return keyPair.encryptionScheme.encUnPad(m.toBuffer(keyPair.encryptedDataLength)); - } - } - }; -}; -},{"../libs/jsbn.js":174,"../schemes/schemes.js":179}],168:[function(require,module,exports){ -var crypto = require('crypto'); -var constants = require('constants'); -var schemes = require('../schemes/schemes.js'); - -module.exports = function (keyPair, options) { - var jsEngine = require('./js.js')(keyPair, options); - var pkcs1Scheme = schemes.pkcs1.makeScheme(keyPair, options); - - return { - encrypt: function (buffer, usePrivate) { - if (usePrivate) { - return jsEngine.encrypt(buffer, usePrivate); - } - var padding = constants.RSA_PKCS1_OAEP_PADDING; - if (options.encryptionScheme === 'pkcs1') { - padding = constants.RSA_PKCS1_PADDING; - } - if (options.encryptionSchemeOptions && options.encryptionSchemeOptions.padding) { - padding = options.encryptionSchemeOptions.padding; - } - - var data = buffer; - if (padding === constants.RSA_NO_PADDING) { - data = pkcs1Scheme.pkcs0pad(buffer); - } - - return crypto.publicEncrypt({ - key: options.rsaUtils.exportKey('public'), - padding: padding - }, data); - }, - - decrypt: function (buffer, usePublic) { - if (usePublic) { - return jsEngine.decrypt(buffer, usePublic); - } - var padding = constants.RSA_PKCS1_OAEP_PADDING; - if (options.encryptionScheme === 'pkcs1') { - padding = constants.RSA_PKCS1_PADDING; - } - if (options.encryptionSchemeOptions && options.encryptionSchemeOptions.padding) { - padding = options.encryptionSchemeOptions.padding; - } - - var res = crypto.privateDecrypt({ - key: options.rsaUtils.exportKey('private'), - padding: padding - }, buffer); - - if (padding === constants.RSA_NO_PADDING) { - return pkcs1Scheme.pkcs0unpad(res); - } - return res; - } - }; -}; -},{"../schemes/schemes.js":179,"./js.js":167,"constants":78,"crypto":91}],169:[function(require,module,exports){ -var _ = require('../utils')._; -var utils = require('../utils'); - -module.exports = { - privateExport: function (key, options) { - return { - n: key.n.toBuffer(), - e: key.e, - d: key.d.toBuffer(), - p: key.p.toBuffer(), - q: key.q.toBuffer(), - dmp1: key.dmp1.toBuffer(), - dmq1: key.dmq1.toBuffer(), - coeff: key.coeff.toBuffer() - }; - }, - - privateImport: function (key, data, options) { - if (data.n && data.e && data.d && data.p && data.q && data.dmp1 && data.dmq1 && data.coeff) { - key.setPrivate( - data.n, - data.e, - data.d, - data.p, - data.q, - data.dmp1, - data.dmq1, - data.coeff - ); - } else { - throw Error("Invalid key data"); - } - }, - - publicExport: function (key, options) { - return { - n: key.n.toBuffer(), - e: key.e - }; - }, - - publicImport: function (key, data, options) { - if (data.n && data.e) { - key.setPublic( - data.n, - data.e - ); - } else { - throw Error("Invalid key data"); - } - }, - - /** - * Trying autodetect and import key - * @param key - * @param data - */ - autoImport: function (key, data) { - if (data.n && data.e) { - if (data.d && data.p && data.q && data.dmp1 && data.dmq1 && data.coeff) { - module.exports.privateImport(key, data); - return true; - } else { - module.exports.publicImport(key, data); - return true; - } - } - - return false; - } -}; - -},{"../utils":180}],170:[function(require,module,exports){ -var _ = require('../utils')._; - -function formatParse(format) { - format = format.split('-'); - var keyType = 'private'; - var keyOpt = {type: 'default'}; - - for (var i = 1; i < format.length; i++) { - if (format[i]) { - switch (format[i]) { - case 'public': - keyType = format[i]; - break; - case 'private': - keyType = format[i]; - break; - case 'pem': - keyOpt.type = format[i]; - break; - case 'der': - keyOpt.type = format[i]; - break; - } - } - } - - return {scheme: format[0], keyType: keyType, keyOpt: keyOpt}; -} - -module.exports = { - pkcs1: require('./pkcs1'), - pkcs8: require('./pkcs8'), - components: require('./components'), - openssh: require('./openssh'), - - isPrivateExport: function (format) { - return module.exports[format] && typeof module.exports[format].privateExport === 'function'; - }, - - isPrivateImport: function (format) { - return module.exports[format] && typeof module.exports[format].privateImport === 'function'; - }, - - isPublicExport: function (format) { - return module.exports[format] && typeof module.exports[format].publicExport === 'function'; - }, - - isPublicImport: function (format) { - return module.exports[format] && typeof module.exports[format].publicImport === 'function'; - }, - - detectAndImport: function (key, data, format) { - if (format === undefined) { - for (var scheme in module.exports) { - if (typeof module.exports[scheme].autoImport === 'function' && module.exports[scheme].autoImport(key, data)) { - return true; - } - } - } else if (format) { - var fmt = formatParse(format); - - if (module.exports[fmt.scheme]) { - if (fmt.keyType === 'private') { - module.exports[fmt.scheme].privateImport(key, data, fmt.keyOpt); - } else { - module.exports[fmt.scheme].publicImport(key, data, fmt.keyOpt); - } - } else { - throw Error('Unsupported key format'); - } - } - - return false; - }, - - detectAndExport: function (key, format) { - if (format) { - var fmt = formatParse(format); - - if (module.exports[fmt.scheme]) { - if (fmt.keyType === 'private') { - if (!key.isPrivate()) { - throw Error("This is not private key"); - } - return module.exports[fmt.scheme].privateExport(key, fmt.keyOpt); - } else { - if (!key.isPublic()) { - throw Error("This is not public key"); - } - return module.exports[fmt.scheme].publicExport(key, fmt.keyOpt); - } - } else { - throw Error('Unsupported key format'); - } - } - } -}; -},{"../utils":180,"./components":169,"./openssh":171,"./pkcs1":172,"./pkcs8":173}],171:[function(require,module,exports){ -(function (Buffer){(function (){ -var _ = require("../utils")._; -var utils = require("../utils"); -var BigInteger = require("../libs/jsbn"); - -const PRIVATE_OPENING_BOUNDARY = "-----BEGIN OPENSSH PRIVATE KEY-----"; -const PRIVATE_CLOSING_BOUNDARY = "-----END OPENSSH PRIVATE KEY-----"; - -module.exports = { - privateExport: function (key, options) { - const nbuf = key.n.toBuffer(); - - let ebuf = Buffer.alloc(4) - ebuf.writeUInt32BE(key.e, 0); - //Slice leading zeroes - while (ebuf[0] === 0) ebuf = ebuf.slice(1); - - const dbuf = key.d.toBuffer(); - const coeffbuf = key.coeff.toBuffer(); - const pbuf = key.p.toBuffer(); - const qbuf = key.q.toBuffer(); - let commentbuf; - if (typeof key.sshcomment !== "undefined") { - commentbuf = Buffer.from(key.sshcomment); - } else { - commentbuf = Buffer.from([]); - } - - const pubkeyLength = - 11 + // 32bit length, 'ssh-rsa' - 4 + ebuf.byteLength + - 4 + nbuf.byteLength; - - const privateKeyLength = - 8 + //64bit unused checksum - 11 + // 32bit length, 'ssh-rsa' - 4 + nbuf.byteLength + - 4 + ebuf.byteLength + - 4 + dbuf.byteLength + - 4 + coeffbuf.byteLength + - 4 + pbuf.byteLength + - 4 + qbuf.byteLength + - 4 + commentbuf.byteLength; - - let length = - 15 + //openssh-key-v1,0x00, - 16 + // 2*(32bit length, 'none') - 4 + // 32bit length, empty string - 4 + // 32bit number of keys - 4 + // 32bit pubkey length - pubkeyLength + - 4 + //32bit private+checksum+comment+padding length - privateKeyLength; - - const paddingLength = Math.ceil(privateKeyLength / 8) * 8 - privateKeyLength; - length += paddingLength; - - const buf = Buffer.alloc(length); - const writer = {buf: buf, off: 0}; - buf.write("openssh-key-v1", "utf8"); - buf.writeUInt8(0, 14); - writer.off += 15; - - writeOpenSSHKeyString(writer, Buffer.from("none")); - writeOpenSSHKeyString(writer, Buffer.from("none")); - writeOpenSSHKeyString(writer, Buffer.from("")); - - writer.off = writer.buf.writeUInt32BE(1, writer.off); - writer.off = writer.buf.writeUInt32BE(pubkeyLength, writer.off); - - writeOpenSSHKeyString(writer, Buffer.from("ssh-rsa")); - writeOpenSSHKeyString(writer, ebuf); - writeOpenSSHKeyString(writer, nbuf); - - writer.off = writer.buf.writeUInt32BE( - length - 47 - pubkeyLength, - writer.off - ); - writer.off += 8; - - writeOpenSSHKeyString(writer, Buffer.from("ssh-rsa")); - writeOpenSSHKeyString(writer, nbuf); - writeOpenSSHKeyString(writer, ebuf); - writeOpenSSHKeyString(writer, dbuf); - writeOpenSSHKeyString(writer, coeffbuf); - writeOpenSSHKeyString(writer, pbuf); - writeOpenSSHKeyString(writer, qbuf); - writeOpenSSHKeyString(writer, commentbuf); - - let pad = 0x01; - while (writer.off < length) { - writer.off = writer.buf.writeUInt8(pad++, writer.off); - } - - if (options.type === "der") { - return writer.buf - } else { - return PRIVATE_OPENING_BOUNDARY + "\n" + utils.linebrk(buf.toString("base64"), 70) + "\n" + PRIVATE_CLOSING_BOUNDARY + "\n"; - } - }, - - privateImport: function (key, data, options) { - options = options || {}; - var buffer; - - if (options.type !== "der") { - if (Buffer.isBuffer(data)) { - data = data.toString("utf8"); - } - - if (_.isString(data)) { - var pem = utils.trimSurroundingText(data, PRIVATE_OPENING_BOUNDARY, PRIVATE_CLOSING_BOUNDARY) - .replace(/\s+|\n\r|\n|\r$/gm, ""); - buffer = Buffer.from(pem, "base64"); - } else { - throw Error("Unsupported key format"); - } - } else if (Buffer.isBuffer(data)) { - buffer = data; - } else { - throw Error("Unsupported key format"); - } - - const reader = {buf: buffer, off: 0}; - - if (buffer.slice(0, 14).toString("ascii") !== "openssh-key-v1") - throw "Invalid file format."; - - reader.off += 15; - - //ciphername - if (readOpenSSHKeyString(reader).toString("ascii") !== "none") - throw Error("Unsupported key type"); - //kdfname - if (readOpenSSHKeyString(reader).toString("ascii") !== "none") - throw Error("Unsupported key type"); - //kdf - if (readOpenSSHKeyString(reader).toString("ascii") !== "") - throw Error("Unsupported key type"); - //keynum - reader.off += 4; - - //sshpublength - reader.off += 4; - - //keytype - if (readOpenSSHKeyString(reader).toString("ascii") !== "ssh-rsa") - throw Error("Unsupported key type"); - readOpenSSHKeyString(reader); - readOpenSSHKeyString(reader); - - reader.off += 12; - if (readOpenSSHKeyString(reader).toString("ascii") !== "ssh-rsa") - throw Error("Unsupported key type"); - - const n = readOpenSSHKeyString(reader); - const e = readOpenSSHKeyString(reader); - const d = readOpenSSHKeyString(reader); - const coeff = readOpenSSHKeyString(reader); - const p = readOpenSSHKeyString(reader); - const q = readOpenSSHKeyString(reader); - - //Calculate missing values - const dint = new BigInteger(d); - const qint = new BigInteger(q); - const pint = new BigInteger(p); - const dp = dint.mod(pint.subtract(BigInteger.ONE)); - const dq = dint.mod(qint.subtract(BigInteger.ONE)); - - key.setPrivate( - n, // modulus - e, // publicExponent - d, // privateExponent - p, // prime1 - q, // prime2 - dp.toBuffer(), // exponent1 -- d mod (p1) - dq.toBuffer(), // exponent2 -- d mod (q-1) - coeff // coefficient -- (inverse of q) mod p - ); - - key.sshcomment = readOpenSSHKeyString(reader).toString("ascii"); - }, - - publicExport: function (key, options) { - let ebuf = Buffer.alloc(4) - ebuf.writeUInt32BE(key.e, 0); - //Slice leading zeroes - while (ebuf[0] === 0) ebuf = ebuf.slice(1); - const nbuf = key.n.toBuffer(); - const buf = Buffer.alloc( - ebuf.byteLength + 4 + - nbuf.byteLength + 4 + - "ssh-rsa".length + 4 - ); - - const writer = {buf: buf, off: 0}; - writeOpenSSHKeyString(writer, Buffer.from("ssh-rsa")); - writeOpenSSHKeyString(writer, ebuf); - writeOpenSSHKeyString(writer, nbuf); - - let comment = key.sshcomment || ""; - - if (options.type === "der") { - return writer.buf - } else { - return "ssh-rsa " + buf.toString("base64") + " " + comment + "\n"; - } - }, - - publicImport: function (key, data, options) { - options = options || {}; - var buffer; - - if (options.type !== "der") { - if (Buffer.isBuffer(data)) { - data = data.toString("utf8"); - } - - if (_.isString(data)) { - if (data.substring(0, 8) !== "ssh-rsa ") - throw Error("Unsupported key format"); - let pemEnd = data.indexOf(" ", 8); - - //Handle keys with no comment - if (pemEnd === -1) { - pemEnd = data.length; - } else { - key.sshcomment = data.substring(pemEnd + 1) - .replace(/\s+|\n\r|\n|\r$/gm, ""); - } - - const pem = data.substring(8, pemEnd) - .replace(/\s+|\n\r|\n|\r$/gm, ""); - buffer = Buffer.from(pem, "base64"); - } else { - throw Error("Unsupported key format"); - } - } else if (Buffer.isBuffer(data)) { - buffer = data; - } else { - throw Error("Unsupported key format"); - } - - const reader = {buf: buffer, off: 0}; - - const type = readOpenSSHKeyString(reader).toString("ascii"); - - if (type !== "ssh-rsa") - throw Error("Invalid key type: " + type); - - const e = readOpenSSHKeyString(reader); - const n = readOpenSSHKeyString(reader); - - key.setPublic( - n, - e - ); - }, - - /** - * Trying autodetect and import key - * @param key - * @param data - */ - autoImport: function (key, data) { - // [\S\s]* matches zero or more of any character - if (/^[\S\s]*-----BEGIN OPENSSH PRIVATE KEY-----\s*(?=(([A-Za-z0-9+/=]+\s*)+))\1-----END OPENSSH PRIVATE KEY-----[\S\s]*$/g.test(data)) { - module.exports.privateImport(key, data); - return true; - } - - if (/^[\S\s]*ssh-rsa \s*(?=(([A-Za-z0-9+/=]+\s*)+))\1[\S\s]*$/g.test(data)) { - module.exports.publicImport(key, data); - return true; - } - - return false; - } -}; - -function readOpenSSHKeyString(reader) { - const len = reader.buf.readInt32BE(reader.off); - reader.off += 4; - const res = reader.buf.slice(reader.off, reader.off + len); - reader.off += len; - return res; -} - -function writeOpenSSHKeyString(writer, data) { - writer.buf.writeInt32BE(data.byteLength, writer.off); - writer.off += 4; - writer.off += data.copy(writer.buf, writer.off); -} -}).call(this)}).call(this,require("buffer").Buffer) -},{"../libs/jsbn":174,"../utils":180,"buffer":63}],172:[function(require,module,exports){ -(function (Buffer){(function (){ -var ber = require('asn1').Ber; -var _ = require('../utils')._; -var utils = require('../utils'); - -const PRIVATE_OPENING_BOUNDARY = '-----BEGIN RSA PRIVATE KEY-----'; -const PRIVATE_CLOSING_BOUNDARY = '-----END RSA PRIVATE KEY-----'; - -const PUBLIC_OPENING_BOUNDARY = '-----BEGIN RSA PUBLIC KEY-----'; -const PUBLIC_CLOSING_BOUNDARY = '-----END RSA PUBLIC KEY-----'; - -module.exports = { - privateExport: function (key, options) { - options = options || {}; - - var n = key.n.toBuffer(); - var d = key.d.toBuffer(); - var p = key.p.toBuffer(); - var q = key.q.toBuffer(); - var dmp1 = key.dmp1.toBuffer(); - var dmq1 = key.dmq1.toBuffer(); - var coeff = key.coeff.toBuffer(); - - var length = n.length + d.length + p.length + q.length + dmp1.length + dmq1.length + coeff.length + 512; // magic - var writer = new ber.Writer({size: length}); - - writer.startSequence(); - writer.writeInt(0); - writer.writeBuffer(n, 2); - writer.writeInt(key.e); - writer.writeBuffer(d, 2); - writer.writeBuffer(p, 2); - writer.writeBuffer(q, 2); - writer.writeBuffer(dmp1, 2); - writer.writeBuffer(dmq1, 2); - writer.writeBuffer(coeff, 2); - writer.endSequence(); - - if (options.type === 'der') { - return writer.buffer; - } else { - return PRIVATE_OPENING_BOUNDARY + '\n' + utils.linebrk(writer.buffer.toString('base64'), 64) + '\n' + PRIVATE_CLOSING_BOUNDARY; - } - }, - - privateImport: function (key, data, options) { - options = options || {}; - var buffer; - - if (options.type !== 'der') { - if (Buffer.isBuffer(data)) { - data = data.toString('utf8'); - } - - if (_.isString(data)) { - var pem = utils.trimSurroundingText(data, PRIVATE_OPENING_BOUNDARY, PRIVATE_CLOSING_BOUNDARY) - .replace(/\s+|\n\r|\n|\r$/gm, ''); - buffer = Buffer.from(pem, 'base64'); - } else { - throw Error('Unsupported key format'); - } - } else if (Buffer.isBuffer(data)) { - buffer = data; - } else { - throw Error('Unsupported key format'); - } - - var reader = new ber.Reader(buffer); - reader.readSequence(); - reader.readString(2, true); // just zero - key.setPrivate( - reader.readString(2, true), // modulus - reader.readString(2, true), // publicExponent - reader.readString(2, true), // privateExponent - reader.readString(2, true), // prime1 - reader.readString(2, true), // prime2 - reader.readString(2, true), // exponent1 -- d mod (p1) - reader.readString(2, true), // exponent2 -- d mod (q-1) - reader.readString(2, true) // coefficient -- (inverse of q) mod p - ); - }, - - publicExport: function (key, options) { - options = options || {}; - - var n = key.n.toBuffer(); - var length = n.length + 512; // magic - - var bodyWriter = new ber.Writer({size: length}); - bodyWriter.startSequence(); - bodyWriter.writeBuffer(n, 2); - bodyWriter.writeInt(key.e); - bodyWriter.endSequence(); - - if (options.type === 'der') { - return bodyWriter.buffer; - } else { - return PUBLIC_OPENING_BOUNDARY + '\n' + utils.linebrk(bodyWriter.buffer.toString('base64'), 64) + '\n' + PUBLIC_CLOSING_BOUNDARY; - } - }, - - publicImport: function (key, data, options) { - options = options || {}; - var buffer; - - if (options.type !== 'der') { - if (Buffer.isBuffer(data)) { - data = data.toString('utf8'); - } - - if (_.isString(data)) { - var pem = utils.trimSurroundingText(data, PUBLIC_OPENING_BOUNDARY, PUBLIC_CLOSING_BOUNDARY) - .replace(/\s+|\n\r|\n|\r$/gm, ''); - buffer = Buffer.from(pem, 'base64'); - } - } else if (Buffer.isBuffer(data)) { - buffer = data; - } else { - throw Error('Unsupported key format'); - } - - var body = new ber.Reader(buffer); - body.readSequence(); - key.setPublic( - body.readString(0x02, true), // modulus - body.readString(0x02, true) // publicExponent - ); - }, - - /** - * Trying autodetect and import key - * @param key - * @param data - */ - autoImport: function (key, data) { - // [\S\s]* matches zero or more of any character - if (/^[\S\s]*-----BEGIN RSA PRIVATE KEY-----\s*(?=(([A-Za-z0-9+/=]+\s*)+))\1-----END RSA PRIVATE KEY-----[\S\s]*$/g.test(data)) { - module.exports.privateImport(key, data); - return true; - } - - if (/^[\S\s]*-----BEGIN RSA PUBLIC KEY-----\s*(?=(([A-Za-z0-9+/=]+\s*)+))\1-----END RSA PUBLIC KEY-----[\S\s]*$/g.test(data)) { - module.exports.publicImport(key, data); - return true; - } - - return false; - } -}; -}).call(this)}).call(this,require("buffer").Buffer) -},{"../utils":180,"asn1":26,"buffer":63}],173:[function(require,module,exports){ -(function (Buffer){(function (){ -var ber = require('asn1').Ber; -var _ = require('../utils')._; -var PUBLIC_RSA_OID = '1.2.840.113549.1.1.1'; -var utils = require('../utils'); - -const PRIVATE_OPENING_BOUNDARY = '-----BEGIN PRIVATE KEY-----'; -const PRIVATE_CLOSING_BOUNDARY = '-----END PRIVATE KEY-----'; - -const PUBLIC_OPENING_BOUNDARY = '-----BEGIN PUBLIC KEY-----'; -const PUBLIC_CLOSING_BOUNDARY = '-----END PUBLIC KEY-----'; - -module.exports = { - privateExport: function (key, options) { - options = options || {}; - - var n = key.n.toBuffer(); - var d = key.d.toBuffer(); - var p = key.p.toBuffer(); - var q = key.q.toBuffer(); - var dmp1 = key.dmp1.toBuffer(); - var dmq1 = key.dmq1.toBuffer(); - var coeff = key.coeff.toBuffer(); - - var length = n.length + d.length + p.length + q.length + dmp1.length + dmq1.length + coeff.length + 512; // magic - var bodyWriter = new ber.Writer({size: length}); - - bodyWriter.startSequence(); - bodyWriter.writeInt(0); - bodyWriter.writeBuffer(n, 2); - bodyWriter.writeInt(key.e); - bodyWriter.writeBuffer(d, 2); - bodyWriter.writeBuffer(p, 2); - bodyWriter.writeBuffer(q, 2); - bodyWriter.writeBuffer(dmp1, 2); - bodyWriter.writeBuffer(dmq1, 2); - bodyWriter.writeBuffer(coeff, 2); - bodyWriter.endSequence(); - - var writer = new ber.Writer({size: length}); - writer.startSequence(); - writer.writeInt(0); - writer.startSequence(); - writer.writeOID(PUBLIC_RSA_OID); - writer.writeNull(); - writer.endSequence(); - writer.writeBuffer(bodyWriter.buffer, 4); - writer.endSequence(); - - if (options.type === 'der') { - return writer.buffer; - } else { - return PRIVATE_OPENING_BOUNDARY + '\n' + utils.linebrk(writer.buffer.toString('base64'), 64) + '\n' + PRIVATE_CLOSING_BOUNDARY; - } - }, - - privateImport: function (key, data, options) { - options = options || {}; - var buffer; - - if (options.type !== 'der') { - if (Buffer.isBuffer(data)) { - data = data.toString('utf8'); - } - - if (_.isString(data)) { - var pem = utils.trimSurroundingText(data, PRIVATE_OPENING_BOUNDARY, PRIVATE_CLOSING_BOUNDARY) - .replace('-----END PRIVATE KEY-----', '') - .replace(/\s+|\n\r|\n|\r$/gm, ''); - buffer = Buffer.from(pem, 'base64'); - } else { - throw Error('Unsupported key format'); - } - } else if (Buffer.isBuffer(data)) { - buffer = data; - } else { - throw Error('Unsupported key format'); - } - - var reader = new ber.Reader(buffer); - reader.readSequence(); - reader.readInt(0); - var header = new ber.Reader(reader.readString(0x30, true)); - - if (header.readOID(0x06, true) !== PUBLIC_RSA_OID) { - throw Error('Invalid Public key format'); - } - - var body = new ber.Reader(reader.readString(0x04, true)); - body.readSequence(); - body.readString(2, true); // just zero - key.setPrivate( - body.readString(2, true), // modulus - body.readString(2, true), // publicExponent - body.readString(2, true), // privateExponent - body.readString(2, true), // prime1 - body.readString(2, true), // prime2 - body.readString(2, true), // exponent1 -- d mod (p1) - body.readString(2, true), // exponent2 -- d mod (q-1) - body.readString(2, true) // coefficient -- (inverse of q) mod p - ); - }, - - publicExport: function (key, options) { - options = options || {}; - - var n = key.n.toBuffer(); - var length = n.length + 512; // magic - - var bodyWriter = new ber.Writer({size: length}); - bodyWriter.writeByte(0); - bodyWriter.startSequence(); - bodyWriter.writeBuffer(n, 2); - bodyWriter.writeInt(key.e); - bodyWriter.endSequence(); - - var writer = new ber.Writer({size: length}); - writer.startSequence(); - writer.startSequence(); - writer.writeOID(PUBLIC_RSA_OID); - writer.writeNull(); - writer.endSequence(); - writer.writeBuffer(bodyWriter.buffer, 3); - writer.endSequence(); - - if (options.type === 'der') { - return writer.buffer; - } else { - return PUBLIC_OPENING_BOUNDARY + '\n' + utils.linebrk(writer.buffer.toString('base64'), 64) + '\n' + PUBLIC_CLOSING_BOUNDARY; - } - }, - - publicImport: function (key, data, options) { - options = options || {}; - var buffer; - - if (options.type !== 'der') { - if (Buffer.isBuffer(data)) { - data = data.toString('utf8'); - } - - if (_.isString(data)) { - var pem = utils.trimSurroundingText(data, PUBLIC_OPENING_BOUNDARY, PUBLIC_CLOSING_BOUNDARY) - .replace(/\s+|\n\r|\n|\r$/gm, ''); - buffer = Buffer.from(pem, 'base64'); - } - } else if (Buffer.isBuffer(data)) { - buffer = data; - } else { - throw Error('Unsupported key format'); - } - - var reader = new ber.Reader(buffer); - reader.readSequence(); - var header = new ber.Reader(reader.readString(0x30, true)); - - if (header.readOID(0x06, true) !== PUBLIC_RSA_OID) { - throw Error('Invalid Public key format'); - } - - var body = new ber.Reader(reader.readString(0x03, true)); - body.readByte(); - body.readSequence(); - key.setPublic( - body.readString(0x02, true), // modulus - body.readString(0x02, true) // publicExponent - ); - }, - - /** - * Trying autodetect and import key - * @param key - * @param data - */ - autoImport: function (key, data) { - if (/^[\S\s]*-----BEGIN PRIVATE KEY-----\s*(?=(([A-Za-z0-9+/=]+\s*)+))\1-----END PRIVATE KEY-----[\S\s]*$/g.test(data)) { - module.exports.privateImport(key, data); - return true; - } - - if (/^[\S\s]*-----BEGIN PUBLIC KEY-----\s*(?=(([A-Za-z0-9+/=]+\s*)+))\1-----END PUBLIC KEY-----[\S\s]*$/g.test(data)) { - module.exports.publicImport(key, data); - return true; - } - - return false; - } -}; - -}).call(this)}).call(this,require("buffer").Buffer) -},{"../utils":180,"asn1":26,"buffer":63}],174:[function(require,module,exports){ -(function (Buffer){(function (){ -/* - * Basic JavaScript BN library - subset useful for RSA encryption. - * - * Copyright (c) 2003-2005 Tom Wu - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, - * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY - * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. - * - * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL, - * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER - * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF - * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT - * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * In addition, the following condition applies: - * - * All redistributions must retain an intact copy of this copyright notice - * and disclaimer. - */ - -/* - * Added Node.js Buffers support - * 2014 rzcoder - */ - -var crypt = require('crypto'); -var _ = require('../utils')._; - -// Bits per digit -var dbits; - -// JavaScript engine analysis -var canary = 0xdeadbeefcafe; -var j_lm = ((canary & 0xffffff) == 0xefcafe); - -// (public) Constructor -function BigInteger(a, b) { - if (a != null) { - if ("number" == typeof a) { - this.fromNumber(a, b); - } else if (Buffer.isBuffer(a)) { - this.fromBuffer(a); - } else if (b == null && "string" != typeof a) { - this.fromByteArray(a); - } else { - this.fromString(a, b); - } - } -} - -// return new, unset BigInteger -function nbi() { - return new BigInteger(null); -} - -// am: Compute w_j += (x*this_i), propagate carries, -// c is initial carry, returns final carry. -// c < 3*dvalue, x < 2*dvalue, this_i < dvalue -// We need to select the fastest one that works in this environment. - -// am1: use a single mult and divide to get the high bits, -// max digit bits should be 26 because -// max internal value = 2*dvalue^2-2*dvalue (< 2^53) -function am1(i, x, w, j, c, n) { - while (--n >= 0) { - var v = x * this[i++] + w[j] + c; - c = Math.floor(v / 0x4000000); - w[j++] = v & 0x3ffffff; - } - return c; -} -// am2 avoids a big mult-and-extract completely. -// Max digit bits should be <= 30 because we do bitwise ops -// on values up to 2*hdvalue^2-hdvalue-1 (< 2^31) -function am2(i, x, w, j, c, n) { - var xl = x & 0x7fff, xh = x >> 15; - while (--n >= 0) { - var l = this[i] & 0x7fff; - var h = this[i++] >> 15; - var m = xh * l + h * xl; - l = xl * l + ((m & 0x7fff) << 15) + w[j] + (c & 0x3fffffff); - c = (l >>> 30) + (m >>> 15) + xh * h + (c >>> 30); - w[j++] = l & 0x3fffffff; - } - return c; -} -// Alternately, set max digit bits to 28 since some -// browsers slow down when dealing with 32-bit numbers. -function am3(i, x, w, j, c, n) { - var xl = x & 0x3fff, xh = x >> 14; - while (--n >= 0) { - var l = this[i] & 0x3fff; - var h = this[i++] >> 14; - var m = xh * l + h * xl; - l = xl * l + ((m & 0x3fff) << 14) + w[j] + c; - c = (l >> 28) + (m >> 14) + xh * h; - w[j++] = l & 0xfffffff; - } - return c; -} - -// We need to select the fastest one that works in this environment. -//if (j_lm && (navigator.appName == "Microsoft Internet Explorer")) { -// BigInteger.prototype.am = am2; -// dbits = 30; -//} else if (j_lm && (navigator.appName != "Netscape")) { -// BigInteger.prototype.am = am1; -// dbits = 26; -//} else { // Mozilla/Netscape seems to prefer am3 -// BigInteger.prototype.am = am3; -// dbits = 28; -//} - -// For node.js, we pick am3 with max dbits to 28. -BigInteger.prototype.am = am3; -dbits = 28; - -BigInteger.prototype.DB = dbits; -BigInteger.prototype.DM = ((1 << dbits) - 1); -BigInteger.prototype.DV = (1 << dbits); - -var BI_FP = 52; -BigInteger.prototype.FV = Math.pow(2, BI_FP); -BigInteger.prototype.F1 = BI_FP - dbits; -BigInteger.prototype.F2 = 2 * dbits - BI_FP; - -// Digit conversions -var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz"; -var BI_RC = new Array(); -var rr, vv; -rr = "0".charCodeAt(0); -for (vv = 0; vv <= 9; ++vv) BI_RC[rr++] = vv; -rr = "a".charCodeAt(0); -for (vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv; -rr = "A".charCodeAt(0); -for (vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv; - -function int2char(n) { - return BI_RM.charAt(n); -} -function intAt(s, i) { - var c = BI_RC[s.charCodeAt(i)]; - return (c == null) ? -1 : c; -} - -// (protected) copy this to r -function bnpCopyTo(r) { - for (var i = this.t - 1; i >= 0; --i) r[i] = this[i]; - r.t = this.t; - r.s = this.s; -} - -// (protected) set from integer value x, -DV <= x < DV -function bnpFromInt(x) { - this.t = 1; - this.s = (x < 0) ? -1 : 0; - if (x > 0) this[0] = x; - else if (x < -1) this[0] = x + DV; - else this.t = 0; -} - -// return bigint initialized to value -function nbv(i) { - var r = nbi(); - r.fromInt(i); - return r; -} - -// (protected) set from string and radix -function bnpFromString(data, radix, unsigned) { - var k; - switch (radix) { - case 2: - k = 1; - break; - case 4: - k = 2; - break; - case 8: - k = 3; - break; - case 16: - k = 4; - break; - case 32: - k = 5; - break; - case 256: - k = 8; - break; - default: - this.fromRadix(data, radix); - return; - } - - this.t = 0; - this.s = 0; - - var i = data.length; - var mi = false; - var sh = 0; - - while (--i >= 0) { - var x = (k == 8) ? data[i] & 0xff : intAt(data, i); - if (x < 0) { - if (data.charAt(i) == "-") mi = true; - continue; - } - mi = false; - if (sh === 0) - this[this.t++] = x; - else if (sh + k > this.DB) { - this[this.t - 1] |= (x & ((1 << (this.DB - sh)) - 1)) << sh; - this[this.t++] = (x >> (this.DB - sh)); - } - else - this[this.t - 1] |= x << sh; - sh += k; - if (sh >= this.DB) sh -= this.DB; - } - if ((!unsigned) && k == 8 && (data[0] & 0x80) != 0) { - this.s = -1; - if (sh > 0) this[this.t - 1] |= ((1 << (this.DB - sh)) - 1) << sh; - } - this.clamp(); - if (mi) BigInteger.ZERO.subTo(this, this); -} - -function bnpFromByteArray(a, unsigned) { - this.fromString(a, 256, unsigned) -} - -function bnpFromBuffer(a) { - this.fromString(a, 256, true) -} - -// (protected) clamp off excess high words -function bnpClamp() { - var c = this.s & this.DM; - while (this.t > 0 && this[this.t - 1] == c) --this.t; -} - -// (public) return string representation in given radix -function bnToString(b) { - if (this.s < 0) return "-" + this.negate().toString(b); - var k; - if (b == 16) k = 4; - else if (b == 8) k = 3; - else if (b == 2) k = 1; - else if (b == 32) k = 5; - else if (b == 4) k = 2; - else return this.toRadix(b); - var km = (1 << k) - 1, d, m = false, r = "", i = this.t; - var p = this.DB - (i * this.DB) % k; - if (i-- > 0) { - if (p < this.DB && (d = this[i] >> p) > 0) { - m = true; - r = int2char(d); - } - while (i >= 0) { - if (p < k) { - d = (this[i] & ((1 << p) - 1)) << (k - p); - d |= this[--i] >> (p += this.DB - k); - } - else { - d = (this[i] >> (p -= k)) & km; - if (p <= 0) { - p += this.DB; - --i; - } - } - if (d > 0) m = true; - if (m) r += int2char(d); - } - } - return m ? r : "0"; -} - -// (public) -this -function bnNegate() { - var r = nbi(); - BigInteger.ZERO.subTo(this, r); - return r; -} - -// (public) |this| -function bnAbs() { - return (this.s < 0) ? this.negate() : this; -} - -// (public) return + if this > a, - if this < a, 0 if equal -function bnCompareTo(a) { - var r = this.s - a.s; - if (r != 0) return r; - var i = this.t; - r = i - a.t; - if (r != 0) return (this.s < 0) ? -r : r; - while (--i >= 0) if ((r = this[i] - a[i]) != 0) return r; - return 0; -} - -// returns bit length of the integer x -function nbits(x) { - var r = 1, t; - if ((t = x >>> 16) != 0) { - x = t; - r += 16; - } - if ((t = x >> 8) != 0) { - x = t; - r += 8; - } - if ((t = x >> 4) != 0) { - x = t; - r += 4; - } - if ((t = x >> 2) != 0) { - x = t; - r += 2; - } - if ((t = x >> 1) != 0) { - x = t; - r += 1; - } - return r; -} - -// (public) return the number of bits in "this" -function bnBitLength() { - if (this.t <= 0) return 0; - return this.DB * (this.t - 1) + nbits(this[this.t - 1] ^ (this.s & this.DM)); -} - -// (protected) r = this << n*DB -function bnpDLShiftTo(n, r) { - var i; - for (i = this.t - 1; i >= 0; --i) r[i + n] = this[i]; - for (i = n - 1; i >= 0; --i) r[i] = 0; - r.t = this.t + n; - r.s = this.s; -} - -// (protected) r = this >> n*DB -function bnpDRShiftTo(n, r) { - for (var i = n; i < this.t; ++i) r[i - n] = this[i]; - r.t = Math.max(this.t - n, 0); - r.s = this.s; -} - -// (protected) r = this << n -function bnpLShiftTo(n, r) { - var bs = n % this.DB; - var cbs = this.DB - bs; - var bm = (1 << cbs) - 1; - var ds = Math.floor(n / this.DB), c = (this.s << bs) & this.DM, i; - for (i = this.t - 1; i >= 0; --i) { - r[i + ds + 1] = (this[i] >> cbs) | c; - c = (this[i] & bm) << bs; - } - for (i = ds - 1; i >= 0; --i) r[i] = 0; - r[ds] = c; - r.t = this.t + ds + 1; - r.s = this.s; - r.clamp(); -} - -// (protected) r = this >> n -function bnpRShiftTo(n, r) { - r.s = this.s; - var ds = Math.floor(n / this.DB); - if (ds >= this.t) { - r.t = 0; - return; - } - var bs = n % this.DB; - var cbs = this.DB - bs; - var bm = (1 << bs) - 1; - r[0] = this[ds] >> bs; - for (var i = ds + 1; i < this.t; ++i) { - r[i - ds - 1] |= (this[i] & bm) << cbs; - r[i - ds] = this[i] >> bs; - } - if (bs > 0) r[this.t - ds - 1] |= (this.s & bm) << cbs; - r.t = this.t - ds; - r.clamp(); -} - -// (protected) r = this - a -function bnpSubTo(a, r) { - var i = 0, c = 0, m = Math.min(a.t, this.t); - while (i < m) { - c += this[i] - a[i]; - r[i++] = c & this.DM; - c >>= this.DB; - } - if (a.t < this.t) { - c -= a.s; - while (i < this.t) { - c += this[i]; - r[i++] = c & this.DM; - c >>= this.DB; - } - c += this.s; - } - else { - c += this.s; - while (i < a.t) { - c -= a[i]; - r[i++] = c & this.DM; - c >>= this.DB; - } - c -= a.s; - } - r.s = (c < 0) ? -1 : 0; - if (c < -1) r[i++] = this.DV + c; - else if (c > 0) r[i++] = c; - r.t = i; - r.clamp(); -} - -// (protected) r = this * a, r != this,a (HAC 14.12) -// "this" should be the larger one if appropriate. -function bnpMultiplyTo(a, r) { - var x = this.abs(), y = a.abs(); - var i = x.t; - r.t = i + y.t; - while (--i >= 0) r[i] = 0; - for (i = 0; i < y.t; ++i) r[i + x.t] = x.am(0, y[i], r, i, 0, x.t); - r.s = 0; - r.clamp(); - if (this.s != a.s) BigInteger.ZERO.subTo(r, r); -} - -// (protected) r = this^2, r != this (HAC 14.16) -function bnpSquareTo(r) { - var x = this.abs(); - var i = r.t = 2 * x.t; - while (--i >= 0) r[i] = 0; - for (i = 0; i < x.t - 1; ++i) { - var c = x.am(i, x[i], r, 2 * i, 0, 1); - if ((r[i + x.t] += x.am(i + 1, 2 * x[i], r, 2 * i + 1, c, x.t - i - 1)) >= x.DV) { - r[i + x.t] -= x.DV; - r[i + x.t + 1] = 1; - } - } - if (r.t > 0) r[r.t - 1] += x.am(i, x[i], r, 2 * i, 0, 1); - r.s = 0; - r.clamp(); -} - -// (protected) divide this by m, quotient and remainder to q, r (HAC 14.20) -// r != q, this != m. q or r may be null. -function bnpDivRemTo(m, q, r) { - var pm = m.abs(); - if (pm.t <= 0) return; - var pt = this.abs(); - if (pt.t < pm.t) { - if (q != null) q.fromInt(0); - if (r != null) this.copyTo(r); - return; - } - if (r == null) r = nbi(); - var y = nbi(), ts = this.s, ms = m.s; - var nsh = this.DB - nbits(pm[pm.t - 1]); // normalize modulus - if (nsh > 0) { - pm.lShiftTo(nsh, y); - pt.lShiftTo(nsh, r); - } - else { - pm.copyTo(y); - pt.copyTo(r); - } - var ys = y.t; - var y0 = y[ys - 1]; - if (y0 === 0) return; - var yt = y0 * (1 << this.F1) + ((ys > 1) ? y[ys - 2] >> this.F2 : 0); - var d1 = this.FV / yt, d2 = (1 << this.F1) / yt, e = 1 << this.F2; - var i = r.t, j = i - ys, t = (q == null) ? nbi() : q; - y.dlShiftTo(j, t); - if (r.compareTo(t) >= 0) { - r[r.t++] = 1; - r.subTo(t, r); - } - BigInteger.ONE.dlShiftTo(ys, t); - t.subTo(y, y); // "negative" y so we can replace sub with am later - while (y.t < ys) y[y.t++] = 0; - while (--j >= 0) { - // Estimate quotient digit - var qd = (r[--i] == y0) ? this.DM : Math.floor(r[i] * d1 + (r[i - 1] + e) * d2); - if ((r[i] += y.am(0, qd, r, j, 0, ys)) < qd) { // Try it out - y.dlShiftTo(j, t); - r.subTo(t, r); - while (r[i] < --qd) r.subTo(t, r); - } - } - if (q != null) { - r.drShiftTo(ys, q); - if (ts != ms) BigInteger.ZERO.subTo(q, q); - } - r.t = ys; - r.clamp(); - if (nsh > 0) r.rShiftTo(nsh, r); // Denormalize remainder - if (ts < 0) BigInteger.ZERO.subTo(r, r); -} - -// (public) this mod a -function bnMod(a) { - var r = nbi(); - this.abs().divRemTo(a, null, r); - if (this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r, r); - return r; -} - -// Modular reduction using "classic" algorithm -function Classic(m) { - this.m = m; -} -function cConvert(x) { - if (x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m); - else return x; -} -function cRevert(x) { - return x; -} -function cReduce(x) { - x.divRemTo(this.m, null, x); -} -function cMulTo(x, y, r) { - x.multiplyTo(y, r); - this.reduce(r); -} -function cSqrTo(x, r) { - x.squareTo(r); - this.reduce(r); -} - -Classic.prototype.convert = cConvert; -Classic.prototype.revert = cRevert; -Classic.prototype.reduce = cReduce; -Classic.prototype.mulTo = cMulTo; -Classic.prototype.sqrTo = cSqrTo; - -// (protected) return "-1/this % 2^DB"; useful for Mont. reduction -// justification: -// xy == 1 (mod m) -// xy = 1+km -// xy(2-xy) = (1+km)(1-km) -// x[y(2-xy)] = 1-k^2m^2 -// x[y(2-xy)] == 1 (mod m^2) -// if y is 1/x mod m, then y(2-xy) is 1/x mod m^2 -// should reduce x and y(2-xy) by m^2 at each step to keep size bounded. -// JS multiply "overflows" differently from C/C++, so care is needed here. -function bnpInvDigit() { - if (this.t < 1) return 0; - var x = this[0]; - if ((x & 1) === 0) return 0; - var y = x & 3; // y == 1/x mod 2^2 - y = (y * (2 - (x & 0xf) * y)) & 0xf; // y == 1/x mod 2^4 - y = (y * (2 - (x & 0xff) * y)) & 0xff; // y == 1/x mod 2^8 - y = (y * (2 - (((x & 0xffff) * y) & 0xffff))) & 0xffff; // y == 1/x mod 2^16 - // last step - calculate inverse mod DV directly; - // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints - y = (y * (2 - x * y % this.DV)) % this.DV; // y == 1/x mod 2^dbits - // we really want the negative inverse, and -DV < y < DV - return (y > 0) ? this.DV - y : -y; -} - -// Montgomery reduction -function Montgomery(m) { - this.m = m; - this.mp = m.invDigit(); - this.mpl = this.mp & 0x7fff; - this.mph = this.mp >> 15; - this.um = (1 << (m.DB - 15)) - 1; - this.mt2 = 2 * m.t; -} - -// xR mod m -function montConvert(x) { - var r = nbi(); - x.abs().dlShiftTo(this.m.t, r); - r.divRemTo(this.m, null, r); - if (x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r, r); - return r; -} - -// x/R mod m -function montRevert(x) { - var r = nbi(); - x.copyTo(r); - this.reduce(r); - return r; -} - -// x = x/R mod m (HAC 14.32) -function montReduce(x) { - while (x.t <= this.mt2) // pad x so am has enough room later - x[x.t++] = 0; - for (var i = 0; i < this.m.t; ++i) { - // faster way of calculating u0 = x[i]*mp mod DV - var j = x[i] & 0x7fff; - var u0 = (j * this.mpl + (((j * this.mph + (x[i] >> 15) * this.mpl) & this.um) << 15)) & x.DM; - // use am to combine the multiply-shift-add into one call - j = i + this.m.t; - x[j] += this.m.am(0, u0, x, i, 0, this.m.t); - // propagate carry - while (x[j] >= x.DV) { - x[j] -= x.DV; - x[++j]++; - } - } - x.clamp(); - x.drShiftTo(this.m.t, x); - if (x.compareTo(this.m) >= 0) x.subTo(this.m, x); -} - -// r = "x^2/R mod m"; x != r -function montSqrTo(x, r) { - x.squareTo(r); - this.reduce(r); -} - -// r = "xy/R mod m"; x,y != r -function montMulTo(x, y, r) { - x.multiplyTo(y, r); - this.reduce(r); -} - -Montgomery.prototype.convert = montConvert; -Montgomery.prototype.revert = montRevert; -Montgomery.prototype.reduce = montReduce; -Montgomery.prototype.mulTo = montMulTo; -Montgomery.prototype.sqrTo = montSqrTo; - -// (protected) true iff this is even -function bnpIsEven() { - return ((this.t > 0) ? (this[0] & 1) : this.s) === 0; -} - -// (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79) -function bnpExp(e, z) { - if (e > 0xffffffff || e < 1) return BigInteger.ONE; - var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e) - 1; - g.copyTo(r); - while (--i >= 0) { - z.sqrTo(r, r2); - if ((e & (1 << i)) > 0) z.mulTo(r2, g, r); - else { - var t = r; - r = r2; - r2 = t; - } - } - return z.revert(r); -} - -// (public) this^e % m, 0 <= e < 2^32 -function bnModPowInt(e, m) { - var z; - if (e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m); - return this.exp(e, z); -} - -// Copyright (c) 2005-2009 Tom Wu -// All Rights Reserved. -// See "LICENSE" for details. - -// Extended JavaScript BN functions, required for RSA private ops. - -// Version 1.1: new BigInteger("0", 10) returns "proper" zero -// Version 1.2: square() API, isProbablePrime fix - -//(public) -function bnClone() { - var r = nbi(); - this.copyTo(r); - return r; -} - -//(public) return value as integer -function bnIntValue() { - if (this.s < 0) { - if (this.t == 1) return this[0] - this.DV; - else if (this.t === 0) return -1; - } - else if (this.t == 1) return this[0]; - else if (this.t === 0) return 0; -// assumes 16 < DB < 32 - return ((this[1] & ((1 << (32 - this.DB)) - 1)) << this.DB) | this[0]; -} - -//(public) return value as byte -function bnByteValue() { - return (this.t == 0) ? this.s : (this[0] << 24) >> 24; -} - -//(public) return value as short (assumes DB>=16) -function bnShortValue() { - return (this.t == 0) ? this.s : (this[0] << 16) >> 16; -} - -//(protected) return x s.t. r^x < DV -function bnpChunkSize(r) { - return Math.floor(Math.LN2 * this.DB / Math.log(r)); -} - -//(public) 0 if this === 0, 1 if this > 0 -function bnSigNum() { - if (this.s < 0) return -1; - else if (this.t <= 0 || (this.t == 1 && this[0] <= 0)) return 0; - else return 1; -} - -//(protected) convert to radix string -function bnpToRadix(b) { - if (b == null) b = 10; - if (this.signum() === 0 || b < 2 || b > 36) return "0"; - var cs = this.chunkSize(b); - var a = Math.pow(b, cs); - var d = nbv(a), y = nbi(), z = nbi(), r = ""; - this.divRemTo(d, y, z); - while (y.signum() > 0) { - r = (a + z.intValue()).toString(b).substr(1) + r; - y.divRemTo(d, y, z); - } - return z.intValue().toString(b) + r; -} - -//(protected) convert from radix string -function bnpFromRadix(s, b) { - this.fromInt(0); - if (b == null) b = 10; - var cs = this.chunkSize(b); - var d = Math.pow(b, cs), mi = false, j = 0, w = 0; - for (var i = 0; i < s.length; ++i) { - var x = intAt(s, i); - if (x < 0) { - if (s.charAt(i) == "-" && this.signum() === 0) mi = true; - continue; - } - w = b * w + x; - if (++j >= cs) { - this.dMultiply(d); - this.dAddOffset(w, 0); - j = 0; - w = 0; - } - } - if (j > 0) { - this.dMultiply(Math.pow(b, j)); - this.dAddOffset(w, 0); - } - if (mi) BigInteger.ZERO.subTo(this, this); -} - -//(protected) alternate constructor -function bnpFromNumber(a, b) { - if ("number" == typeof b) { - // new BigInteger(int,int,RNG) - if (a < 2) this.fromInt(1); - else { - this.fromNumber(a); - if (!this.testBit(a - 1)) // force MSB set - this.bitwiseTo(BigInteger.ONE.shiftLeft(a - 1), op_or, this); - if (this.isEven()) this.dAddOffset(1, 0); // force odd - while (!this.isProbablePrime(b)) { - this.dAddOffset(2, 0); - if (this.bitLength() > a) this.subTo(BigInteger.ONE.shiftLeft(a - 1), this); - } - } - } else { - // new BigInteger(int,RNG) - var x = crypt.randomBytes((a >> 3) + 1) - var t = a & 7; - - if (t > 0) - x[0] &= ((1 << t) - 1); - else - x[0] = 0; - - this.fromByteArray(x); - } -} - -//(public) convert to bigendian byte array -function bnToByteArray() { - var i = this.t, r = new Array(); - r[0] = this.s; - var p = this.DB - (i * this.DB) % 8, d, k = 0; - if (i-- > 0) { - if (p < this.DB && (d = this[i] >> p) != (this.s & this.DM) >> p) - r[k++] = d | (this.s << (this.DB - p)); - while (i >= 0) { - if (p < 8) { - d = (this[i] & ((1 << p) - 1)) << (8 - p); - d |= this[--i] >> (p += this.DB - 8); - } - else { - d = (this[i] >> (p -= 8)) & 0xff; - if (p <= 0) { - p += this.DB; - --i; - } - } - if ((d & 0x80) != 0) d |= -256; - if (k === 0 && (this.s & 0x80) != (d & 0x80)) ++k; - if (k > 0 || d != this.s) r[k++] = d; - } - } - return r; -} - -/** - * return Buffer object - * @param trim {boolean} slice buffer if first element == 0 - * @returns {Buffer} - */ -function bnToBuffer(trimOrSize) { - var res = Buffer.from(this.toByteArray()); - if (trimOrSize === true && res[0] === 0) { - res = res.slice(1); - } else if (_.isNumber(trimOrSize)) { - if (res.length > trimOrSize) { - for (var i = 0; i < res.length - trimOrSize; i++) { - if (res[i] !== 0) { - return null; - } - } - return res.slice(res.length - trimOrSize); - } else if (res.length < trimOrSize) { - var padded = Buffer.alloc(trimOrSize); - padded.fill(0, 0, trimOrSize - res.length); - res.copy(padded, trimOrSize - res.length); - return padded; - } - } - return res; -} - -function bnEquals(a) { - return (this.compareTo(a) == 0); -} -function bnMin(a) { - return (this.compareTo(a) < 0) ? this : a; -} -function bnMax(a) { - return (this.compareTo(a) > 0) ? this : a; -} - -//(protected) r = this op a (bitwise) -function bnpBitwiseTo(a, op, r) { - var i, f, m = Math.min(a.t, this.t); - for (i = 0; i < m; ++i) r[i] = op(this[i], a[i]); - if (a.t < this.t) { - f = a.s & this.DM; - for (i = m; i < this.t; ++i) r[i] = op(this[i], f); - r.t = this.t; - } - else { - f = this.s & this.DM; - for (i = m; i < a.t; ++i) r[i] = op(f, a[i]); - r.t = a.t; - } - r.s = op(this.s, a.s); - r.clamp(); -} - -//(public) this & a -function op_and(x, y) { - return x & y; -} -function bnAnd(a) { - var r = nbi(); - this.bitwiseTo(a, op_and, r); - return r; -} - -//(public) this | a -function op_or(x, y) { - return x | y; -} -function bnOr(a) { - var r = nbi(); - this.bitwiseTo(a, op_or, r); - return r; -} - -//(public) this ^ a -function op_xor(x, y) { - return x ^ y; -} -function bnXor(a) { - var r = nbi(); - this.bitwiseTo(a, op_xor, r); - return r; -} - -//(public) this & ~a -function op_andnot(x, y) { - return x & ~y; -} -function bnAndNot(a) { - var r = nbi(); - this.bitwiseTo(a, op_andnot, r); - return r; -} - -//(public) ~this -function bnNot() { - var r = nbi(); - for (var i = 0; i < this.t; ++i) r[i] = this.DM & ~this[i]; - r.t = this.t; - r.s = ~this.s; - return r; -} - -//(public) this << n -function bnShiftLeft(n) { - var r = nbi(); - if (n < 0) this.rShiftTo(-n, r); else this.lShiftTo(n, r); - return r; -} - -//(public) this >> n -function bnShiftRight(n) { - var r = nbi(); - if (n < 0) this.lShiftTo(-n, r); else this.rShiftTo(n, r); - return r; -} - -//return index of lowest 1-bit in x, x < 2^31 -function lbit(x) { - if (x === 0) return -1; - var r = 0; - if ((x & 0xffff) === 0) { - x >>= 16; - r += 16; - } - if ((x & 0xff) === 0) { - x >>= 8; - r += 8; - } - if ((x & 0xf) === 0) { - x >>= 4; - r += 4; - } - if ((x & 3) === 0) { - x >>= 2; - r += 2; - } - if ((x & 1) === 0) ++r; - return r; -} - -//(public) returns index of lowest 1-bit (or -1 if none) -function bnGetLowestSetBit() { - for (var i = 0; i < this.t; ++i) - if (this[i] != 0) return i * this.DB + lbit(this[i]); - if (this.s < 0) return this.t * this.DB; - return -1; -} - -//return number of 1 bits in x -function cbit(x) { - var r = 0; - while (x != 0) { - x &= x - 1; - ++r; - } - return r; -} - -//(public) return number of set bits -function bnBitCount() { - var r = 0, x = this.s & this.DM; - for (var i = 0; i < this.t; ++i) r += cbit(this[i] ^ x); - return r; -} - -//(public) true iff nth bit is set -function bnTestBit(n) { - var j = Math.floor(n / this.DB); - if (j >= this.t) return (this.s != 0); - return ((this[j] & (1 << (n % this.DB))) != 0); -} - -//(protected) this op (1<>= this.DB; - } - if (a.t < this.t) { - c += a.s; - while (i < this.t) { - c += this[i]; - r[i++] = c & this.DM; - c >>= this.DB; - } - c += this.s; - } - else { - c += this.s; - while (i < a.t) { - c += a[i]; - r[i++] = c & this.DM; - c >>= this.DB; - } - c += a.s; - } - r.s = (c < 0) ? -1 : 0; - if (c > 0) r[i++] = c; - else if (c < -1) r[i++] = this.DV + c; - r.t = i; - r.clamp(); -} - -//(public) this + a -function bnAdd(a) { - var r = nbi(); - this.addTo(a, r); - return r; -} - -//(public) this - a -function bnSubtract(a) { - var r = nbi(); - this.subTo(a, r); - return r; -} - -//(public) this * a -function bnMultiply(a) { - var r = nbi(); - this.multiplyTo(a, r); - return r; -} - -// (public) this^2 -function bnSquare() { - var r = nbi(); - this.squareTo(r); - return r; -} - -//(public) this / a -function bnDivide(a) { - var r = nbi(); - this.divRemTo(a, r, null); - return r; -} - -//(public) this % a -function bnRemainder(a) { - var r = nbi(); - this.divRemTo(a, null, r); - return r; -} - -//(public) [this/a,this%a] -function bnDivideAndRemainder(a) { - var q = nbi(), r = nbi(); - this.divRemTo(a, q, r); - return new Array(q, r); -} - -//(protected) this *= n, this >= 0, 1 < n < DV -function bnpDMultiply(n) { - this[this.t] = this.am(0, n - 1, this, 0, 0, this.t); - ++this.t; - this.clamp(); -} - -//(protected) this += n << w words, this >= 0 -function bnpDAddOffset(n, w) { - if (n === 0) return; - while (this.t <= w) this[this.t++] = 0; - this[w] += n; - while (this[w] >= this.DV) { - this[w] -= this.DV; - if (++w >= this.t) this[this.t++] = 0; - ++this[w]; - } -} - -//A "null" reducer -function NullExp() { -} -function nNop(x) { - return x; -} -function nMulTo(x, y, r) { - x.multiplyTo(y, r); -} -function nSqrTo(x, r) { - x.squareTo(r); -} - -NullExp.prototype.convert = nNop; -NullExp.prototype.revert = nNop; -NullExp.prototype.mulTo = nMulTo; -NullExp.prototype.sqrTo = nSqrTo; - -//(public) this^e -function bnPow(e) { - return this.exp(e, new NullExp()); -} - -//(protected) r = lower n words of "this * a", a.t <= n -//"this" should be the larger one if appropriate. -function bnpMultiplyLowerTo(a, n, r) { - var i = Math.min(this.t + a.t, n); - r.s = 0; // assumes a,this >= 0 - r.t = i; - while (i > 0) r[--i] = 0; - var j; - for (j = r.t - this.t; i < j; ++i) r[i + this.t] = this.am(0, a[i], r, i, 0, this.t); - for (j = Math.min(a.t, n); i < j; ++i) this.am(0, a[i], r, i, 0, n - i); - r.clamp(); -} - -//(protected) r = "this * a" without lower n words, n > 0 -//"this" should be the larger one if appropriate. -function bnpMultiplyUpperTo(a, n, r) { - --n; - var i = r.t = this.t + a.t - n; - r.s = 0; // assumes a,this >= 0 - while (--i >= 0) r[i] = 0; - for (i = Math.max(n - this.t, 0); i < a.t; ++i) - r[this.t + i - n] = this.am(n - i, a[i], r, 0, 0, this.t + i - n); - r.clamp(); - r.drShiftTo(1, r); -} - -//Barrett modular reduction -function Barrett(m) { -// setup Barrett - this.r2 = nbi(); - this.q3 = nbi(); - BigInteger.ONE.dlShiftTo(2 * m.t, this.r2); - this.mu = this.r2.divide(m); - this.m = m; -} - -function barrettConvert(x) { - if (x.s < 0 || x.t > 2 * this.m.t) return x.mod(this.m); - else if (x.compareTo(this.m) < 0) return x; - else { - var r = nbi(); - x.copyTo(r); - this.reduce(r); - return r; - } -} - -function barrettRevert(x) { - return x; -} - -//x = x mod m (HAC 14.42) -function barrettReduce(x) { - x.drShiftTo(this.m.t - 1, this.r2); - if (x.t > this.m.t + 1) { - x.t = this.m.t + 1; - x.clamp(); - } - this.mu.multiplyUpperTo(this.r2, this.m.t + 1, this.q3); - this.m.multiplyLowerTo(this.q3, this.m.t + 1, this.r2); - while (x.compareTo(this.r2) < 0) x.dAddOffset(1, this.m.t + 1); - x.subTo(this.r2, x); - while (x.compareTo(this.m) >= 0) x.subTo(this.m, x); -} - -//r = x^2 mod m; x != r -function barrettSqrTo(x, r) { - x.squareTo(r); - this.reduce(r); -} - -//r = x*y mod m; x,y != r -function barrettMulTo(x, y, r) { - x.multiplyTo(y, r); - this.reduce(r); -} - -Barrett.prototype.convert = barrettConvert; -Barrett.prototype.revert = barrettRevert; -Barrett.prototype.reduce = barrettReduce; -Barrett.prototype.mulTo = barrettMulTo; -Barrett.prototype.sqrTo = barrettSqrTo; - -//(public) this^e % m (HAC 14.85) -function bnModPow(e, m) { - var i = e.bitLength(), k, r = nbv(1), z; - if (i <= 0) return r; - else if (i < 18) k = 1; - else if (i < 48) k = 3; - else if (i < 144) k = 4; - else if (i < 768) k = 5; - else k = 6; - if (i < 8) - z = new Classic(m); - else if (m.isEven()) - z = new Barrett(m); - else - z = new Montgomery(m); - -// precomputation - var g = new Array(), n = 3, k1 = k - 1, km = (1 << k) - 1; - g[1] = z.convert(this); - if (k > 1) { - var g2 = nbi(); - z.sqrTo(g[1], g2); - while (n <= km) { - g[n] = nbi(); - z.mulTo(g2, g[n - 2], g[n]); - n += 2; - } - } - - var j = e.t - 1, w, is1 = true, r2 = nbi(), t; - i = nbits(e[j]) - 1; - while (j >= 0) { - if (i >= k1) w = (e[j] >> (i - k1)) & km; - else { - w = (e[j] & ((1 << (i + 1)) - 1)) << (k1 - i); - if (j > 0) w |= e[j - 1] >> (this.DB + i - k1); - } - - n = k; - while ((w & 1) === 0) { - w >>= 1; - --n; - } - if ((i -= n) < 0) { - i += this.DB; - --j; - } - if (is1) { // ret == 1, don't bother squaring or multiplying it - g[w].copyTo(r); - is1 = false; - } - else { - while (n > 1) { - z.sqrTo(r, r2); - z.sqrTo(r2, r); - n -= 2; - } - if (n > 0) z.sqrTo(r, r2); else { - t = r; - r = r2; - r2 = t; - } - z.mulTo(r2, g[w], r); - } - - while (j >= 0 && (e[j] & (1 << i)) === 0) { - z.sqrTo(r, r2); - t = r; - r = r2; - r2 = t; - if (--i < 0) { - i = this.DB - 1; - --j; - } - } - } - return z.revert(r); -} - -//(public) gcd(this,a) (HAC 14.54) -function bnGCD(a) { - var x = (this.s < 0) ? this.negate() : this.clone(); - var y = (a.s < 0) ? a.negate() : a.clone(); - if (x.compareTo(y) < 0) { - var t = x; - x = y; - y = t; - } - var i = x.getLowestSetBit(), g = y.getLowestSetBit(); - if (g < 0) return x; - if (i < g) g = i; - if (g > 0) { - x.rShiftTo(g, x); - y.rShiftTo(g, y); - } - while (x.signum() > 0) { - if ((i = x.getLowestSetBit()) > 0) x.rShiftTo(i, x); - if ((i = y.getLowestSetBit()) > 0) y.rShiftTo(i, y); - if (x.compareTo(y) >= 0) { - x.subTo(y, x); - x.rShiftTo(1, x); - } - else { - y.subTo(x, y); - y.rShiftTo(1, y); - } - } - if (g > 0) y.lShiftTo(g, y); - return y; -} - -//(protected) this % n, n < 2^26 -function bnpModInt(n) { - if (n <= 0) return 0; - var d = this.DV % n, r = (this.s < 0) ? n - 1 : 0; - if (this.t > 0) - if (d === 0) r = this[0] % n; - else for (var i = this.t - 1; i >= 0; --i) r = (d * r + this[i]) % n; - return r; -} - -//(public) 1/this % m (HAC 14.61) -function bnModInverse(m) { - var ac = m.isEven(); - if ((this.isEven() && ac) || m.signum() === 0) return BigInteger.ZERO; - var u = m.clone(), v = this.clone(); - var a = nbv(1), b = nbv(0), c = nbv(0), d = nbv(1); - while (u.signum() != 0) { - while (u.isEven()) { - u.rShiftTo(1, u); - if (ac) { - if (!a.isEven() || !b.isEven()) { - a.addTo(this, a); - b.subTo(m, b); - } - a.rShiftTo(1, a); - } - else if (!b.isEven()) b.subTo(m, b); - b.rShiftTo(1, b); - } - while (v.isEven()) { - v.rShiftTo(1, v); - if (ac) { - if (!c.isEven() || !d.isEven()) { - c.addTo(this, c); - d.subTo(m, d); - } - c.rShiftTo(1, c); - } - else if (!d.isEven()) d.subTo(m, d); - d.rShiftTo(1, d); - } - if (u.compareTo(v) >= 0) { - u.subTo(v, u); - if (ac) a.subTo(c, a); - b.subTo(d, b); - } - else { - v.subTo(u, v); - if (ac) c.subTo(a, c); - d.subTo(b, d); - } - } - if (v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO; - if (d.compareTo(m) >= 0) return d.subtract(m); - if (d.signum() < 0) d.addTo(m, d); else return d; - if (d.signum() < 0) return d.add(m); else return d; -} - -var lowprimes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997]; -var lplim = (1 << 26) / lowprimes[lowprimes.length - 1]; - -//(public) test primality with certainty >= 1-.5^t -function bnIsProbablePrime(t) { - var i, x = this.abs(); - if (x.t == 1 && x[0] <= lowprimes[lowprimes.length - 1]) { - for (i = 0; i < lowprimes.length; ++i) - if (x[0] == lowprimes[i]) return true; - return false; - } - if (x.isEven()) return false; - i = 1; - while (i < lowprimes.length) { - var m = lowprimes[i], j = i + 1; - while (j < lowprimes.length && m < lplim) m *= lowprimes[j++]; - m = x.modInt(m); - while (i < j) if (m % lowprimes[i++] === 0) return false; - } - return x.millerRabin(t); -} - -//(protected) true if probably prime (HAC 4.24, Miller-Rabin) -function bnpMillerRabin(t) { - var n1 = this.subtract(BigInteger.ONE); - var k = n1.getLowestSetBit(); - if (k <= 0) return false; - var r = n1.shiftRight(k); - t = (t + 1) >> 1; - if (t > lowprimes.length) t = lowprimes.length; - var a = nbi(); - for (var i = 0; i < t; ++i) { - //Pick bases at random, instead of starting at 2 - a.fromInt(lowprimes[Math.floor(Math.random() * lowprimes.length)]); - var y = a.modPow(r, this); - if (y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) { - var j = 1; - while (j++ < k && y.compareTo(n1) != 0) { - y = y.modPowInt(2, this); - if (y.compareTo(BigInteger.ONE) === 0) return false; - } - if (y.compareTo(n1) != 0) return false; - } - } - return true; -} - -// protected -BigInteger.prototype.copyTo = bnpCopyTo; -BigInteger.prototype.fromInt = bnpFromInt; -BigInteger.prototype.fromString = bnpFromString; -BigInteger.prototype.fromByteArray = bnpFromByteArray; -BigInteger.prototype.fromBuffer = bnpFromBuffer; -BigInteger.prototype.clamp = bnpClamp; -BigInteger.prototype.dlShiftTo = bnpDLShiftTo; -BigInteger.prototype.drShiftTo = bnpDRShiftTo; -BigInteger.prototype.lShiftTo = bnpLShiftTo; -BigInteger.prototype.rShiftTo = bnpRShiftTo; -BigInteger.prototype.subTo = bnpSubTo; -BigInteger.prototype.multiplyTo = bnpMultiplyTo; -BigInteger.prototype.squareTo = bnpSquareTo; -BigInteger.prototype.divRemTo = bnpDivRemTo; -BigInteger.prototype.invDigit = bnpInvDigit; -BigInteger.prototype.isEven = bnpIsEven; -BigInteger.prototype.exp = bnpExp; - -BigInteger.prototype.chunkSize = bnpChunkSize; -BigInteger.prototype.toRadix = bnpToRadix; -BigInteger.prototype.fromRadix = bnpFromRadix; -BigInteger.prototype.fromNumber = bnpFromNumber; -BigInteger.prototype.bitwiseTo = bnpBitwiseTo; -BigInteger.prototype.changeBit = bnpChangeBit; -BigInteger.prototype.addTo = bnpAddTo; -BigInteger.prototype.dMultiply = bnpDMultiply; -BigInteger.prototype.dAddOffset = bnpDAddOffset; -BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo; -BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo; -BigInteger.prototype.modInt = bnpModInt; -BigInteger.prototype.millerRabin = bnpMillerRabin; - - -// public -BigInteger.prototype.toString = bnToString; -BigInteger.prototype.negate = bnNegate; -BigInteger.prototype.abs = bnAbs; -BigInteger.prototype.compareTo = bnCompareTo; -BigInteger.prototype.bitLength = bnBitLength; -BigInteger.prototype.mod = bnMod; -BigInteger.prototype.modPowInt = bnModPowInt; - -BigInteger.prototype.clone = bnClone; -BigInteger.prototype.intValue = bnIntValue; -BigInteger.prototype.byteValue = bnByteValue; -BigInteger.prototype.shortValue = bnShortValue; -BigInteger.prototype.signum = bnSigNum; -BigInteger.prototype.toByteArray = bnToByteArray; -BigInteger.prototype.toBuffer = bnToBuffer; -BigInteger.prototype.equals = bnEquals; -BigInteger.prototype.min = bnMin; -BigInteger.prototype.max = bnMax; -BigInteger.prototype.and = bnAnd; -BigInteger.prototype.or = bnOr; -BigInteger.prototype.xor = bnXor; -BigInteger.prototype.andNot = bnAndNot; -BigInteger.prototype.not = bnNot; -BigInteger.prototype.shiftLeft = bnShiftLeft; -BigInteger.prototype.shiftRight = bnShiftRight; -BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit; -BigInteger.prototype.bitCount = bnBitCount; -BigInteger.prototype.testBit = bnTestBit; -BigInteger.prototype.setBit = bnSetBit; -BigInteger.prototype.clearBit = bnClearBit; -BigInteger.prototype.flipBit = bnFlipBit; -BigInteger.prototype.add = bnAdd; -BigInteger.prototype.subtract = bnSubtract; -BigInteger.prototype.multiply = bnMultiply; -BigInteger.prototype.divide = bnDivide; -BigInteger.prototype.remainder = bnRemainder; -BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder; -BigInteger.prototype.modPow = bnModPow; -BigInteger.prototype.modInverse = bnModInverse; -BigInteger.prototype.pow = bnPow; -BigInteger.prototype.gcd = bnGCD; -BigInteger.prototype.isProbablePrime = bnIsProbablePrime; -BigInteger.int2char = int2char; - -// "constants" -BigInteger.ZERO = nbv(0); -BigInteger.ONE = nbv(1); - -// JSBN-specific extension -BigInteger.prototype.square = bnSquare; - -//BigInteger interfaces not implemented in jsbn: - -//BigInteger(int signum, byte[] magnitude) -//double doubleValue() -//float floatValue() -//int hashCode() -//long longValue() -//static BigInteger valueOf(long val) - -module.exports = BigInteger; -}).call(this)}).call(this,require("buffer").Buffer) -},{"../utils":180,"buffer":63,"crypto":91}],175:[function(require,module,exports){ -(function (Buffer){(function (){ -/* - * RSA Encryption / Decryption with PKCS1 v2 Padding. - * - * Copyright (c) 2003-2005 Tom Wu - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, - * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY - * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. - * - * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL, - * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER - * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF - * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT - * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * In addition, the following condition applies: - * - * All redistributions must retain an intact copy of this copyright notice - * and disclaimer. - */ - -/* - * Node.js adaptation - * long message support implementation - * signing/verifying - * - * 2014 rzcoder - */ - -var _ = require('../utils')._; -var crypt = require('crypto'); -var BigInteger = require('./jsbn.js'); -var utils = require('../utils.js'); -var schemes = require('../schemes/schemes.js'); -var encryptEngines = require('../encryptEngines/encryptEngines.js'); - -exports.BigInteger = BigInteger; -module.exports.Key = (function () { - /** - * RSA key constructor - * - * n - modulus - * e - publicExponent - * d - privateExponent - * p - prime1 - * q - prime2 - * dmp1 - exponent1 -- d mod (p1) - * dmq1 - exponent2 -- d mod (q-1) - * coeff - coefficient -- (inverse of q) mod p - */ - function RSAKey() { - this.n = null; - this.e = 0; - this.d = null; - this.p = null; - this.q = null; - this.dmp1 = null; - this.dmq1 = null; - this.coeff = null; - } - - RSAKey.prototype.setOptions = function (options) { - var signingSchemeProvider = schemes[options.signingScheme]; - var encryptionSchemeProvider = schemes[options.encryptionScheme]; - - if (signingSchemeProvider === encryptionSchemeProvider) { - this.signingScheme = this.encryptionScheme = encryptionSchemeProvider.makeScheme(this, options); - } else { - this.encryptionScheme = encryptionSchemeProvider.makeScheme(this, options); - this.signingScheme = signingSchemeProvider.makeScheme(this, options); - } - - this.encryptEngine = encryptEngines.getEngine(this, options); - }; - - /** - * Generate a new random private key B bits long, using public expt E - * @param B - * @param E - */ - RSAKey.prototype.generate = function (B, E) { - var qs = B >> 1; - this.e = parseInt(E, 16); - var ee = new BigInteger(E, 16); - while (true) { - while (true) { - this.p = new BigInteger(B - qs, 1); - if (this.p.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) === 0 && this.p.isProbablePrime(10)) - break; - } - while (true) { - this.q = new BigInteger(qs, 1); - if (this.q.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) === 0 && this.q.isProbablePrime(10)) - break; - } - if (this.p.compareTo(this.q) <= 0) { - var t = this.p; - this.p = this.q; - this.q = t; - } - var p1 = this.p.subtract(BigInteger.ONE); - var q1 = this.q.subtract(BigInteger.ONE); - var phi = p1.multiply(q1); - if (phi.gcd(ee).compareTo(BigInteger.ONE) === 0) { - this.n = this.p.multiply(this.q); - if (this.n.bitLength() < B) { - continue; - } - this.d = ee.modInverse(phi); - this.dmp1 = this.d.mod(p1); - this.dmq1 = this.d.mod(q1); - this.coeff = this.q.modInverse(this.p); - break; - } - } - this.$$recalculateCache(); - }; - - /** - * Set the private key fields N, e, d and CRT params from buffers - * - * @param N - * @param E - * @param D - * @param P - * @param Q - * @param DP - * @param DQ - * @param C - */ - RSAKey.prototype.setPrivate = function (N, E, D, P, Q, DP, DQ, C) { - if (N && E && D && N.length > 0 && (_.isNumber(E) || E.length > 0) && D.length > 0) { - this.n = new BigInteger(N); - this.e = _.isNumber(E) ? E : utils.get32IntFromBuffer(E, 0); - this.d = new BigInteger(D); - - if (P && Q && DP && DQ && C) { - this.p = new BigInteger(P); - this.q = new BigInteger(Q); - this.dmp1 = new BigInteger(DP); - this.dmq1 = new BigInteger(DQ); - this.coeff = new BigInteger(C); - } else { - // TODO: re-calculate any missing CRT params - } - this.$$recalculateCache(); - } else { - throw Error("Invalid RSA private key"); - } - }; - - /** - * Set the public key fields N and e from hex strings - * @param N - * @param E - */ - RSAKey.prototype.setPublic = function (N, E) { - if (N && E && N.length > 0 && (_.isNumber(E) || E.length > 0)) { - this.n = new BigInteger(N); - this.e = _.isNumber(E) ? E : utils.get32IntFromBuffer(E, 0); - this.$$recalculateCache(); - } else { - throw Error("Invalid RSA public key"); - } - }; - - /** - * private - * Perform raw private operation on "x": return x^d (mod n) - * - * @param x - * @returns {*} - */ - RSAKey.prototype.$doPrivate = function (x) { - if (this.p || this.q) { - return x.modPow(this.d, this.n); - } - - // TODO: re-calculate any missing CRT params - var xp = x.mod(this.p).modPow(this.dmp1, this.p); - var xq = x.mod(this.q).modPow(this.dmq1, this.q); - - while (xp.compareTo(xq) < 0) { - xp = xp.add(this.p); - } - return xp.subtract(xq).multiply(this.coeff).mod(this.p).multiply(this.q).add(xq); - }; - - /** - * private - * Perform raw public operation on "x": return x^e (mod n) - * - * @param x - * @returns {*} - */ - RSAKey.prototype.$doPublic = function (x) { - return x.modPowInt(this.e, this.n); - }; - - /** - * Return the PKCS#1 RSA encryption of buffer - * @param buffer {Buffer} - * @returns {Buffer} - */ - RSAKey.prototype.encrypt = function (buffer, usePrivate) { - var buffers = []; - var results = []; - var bufferSize = buffer.length; - var buffersCount = Math.ceil(bufferSize / this.maxMessageLength) || 1; // total buffers count for encrypt - var dividedSize = Math.ceil(bufferSize / buffersCount || 1); // each buffer size - - if (buffersCount == 1) { - buffers.push(buffer); - } else { - for (var bufNum = 0; bufNum < buffersCount; bufNum++) { - buffers.push(buffer.slice(bufNum * dividedSize, (bufNum + 1) * dividedSize)); - } - } - - for (var i = 0; i < buffers.length; i++) { - results.push(this.encryptEngine.encrypt(buffers[i], usePrivate)); - } - - return Buffer.concat(results); - }; - - /** - * Return the PKCS#1 RSA decryption of buffer - * @param buffer {Buffer} - * @returns {Buffer} - */ - RSAKey.prototype.decrypt = function (buffer, usePublic) { - if (buffer.length % this.encryptedDataLength > 0) { - throw Error('Incorrect data or key'); - } - - var result = []; - var offset = 0; - var length = 0; - var buffersCount = buffer.length / this.encryptedDataLength; - - for (var i = 0; i < buffersCount; i++) { - offset = i * this.encryptedDataLength; - length = offset + this.encryptedDataLength; - result.push(this.encryptEngine.decrypt(buffer.slice(offset, Math.min(length, buffer.length)), usePublic)); - } - - return Buffer.concat(result); - }; - - RSAKey.prototype.sign = function (buffer) { - return this.signingScheme.sign.apply(this.signingScheme, arguments); - }; - - RSAKey.prototype.verify = function (buffer, signature, signature_encoding) { - return this.signingScheme.verify.apply(this.signingScheme, arguments); - }; - - /** - * Check if key pair contains private key - */ - RSAKey.prototype.isPrivate = function () { - return this.n && this.e && this.d && true || false; - }; - - /** - * Check if key pair contains public key - * @param strict {boolean} - public key only, return false if have private exponent - */ - RSAKey.prototype.isPublic = function (strict) { - return this.n && this.e && !(strict && this.d) || false; - }; - - Object.defineProperty(RSAKey.prototype, 'keySize', { - get: function () { - return this.cache.keyBitLength; - } - }); - - Object.defineProperty(RSAKey.prototype, 'encryptedDataLength', { - get: function () { - return this.cache.keyByteLength; - } - }); - - Object.defineProperty(RSAKey.prototype, 'maxMessageLength', { - get: function () { - return this.encryptionScheme.maxMessageLength(); - } - }); - - /** - * Caching key data - */ - RSAKey.prototype.$$recalculateCache = function () { - this.cache = this.cache || {}; - // Bit & byte length - this.cache.keyBitLength = this.n.bitLength(); - this.cache.keyByteLength = (this.cache.keyBitLength + 6) >> 3; - }; - - return RSAKey; -})(); - - -}).call(this)}).call(this,require("buffer").Buffer) -},{"../encryptEngines/encryptEngines.js":165,"../schemes/schemes.js":179,"../utils":180,"../utils.js":180,"./jsbn.js":174,"buffer":63,"crypto":91}],176:[function(require,module,exports){ -(function (Buffer){(function (){ -/** - * PKCS_OAEP signature scheme - */ - -var BigInteger = require('../libs/jsbn'); -var crypt = require('crypto'); - -module.exports = { - isEncryption: true, - isSignature: false -}; - -module.exports.digestLength = { - md4: 16, - md5: 16, - ripemd160: 20, - rmd160: 20, - sha1: 20, - sha224: 28, - sha256: 32, - sha384: 48, - sha512: 64 -}; - -var DEFAULT_HASH_FUNCTION = 'sha1'; - -/* - * OAEP Mask Generation Function 1 - * Generates a buffer full of pseudorandom bytes given seed and maskLength. - * Giving the same seed, maskLength, and hashFunction will result in the same exact byte values in the buffer. - * - * https://tools.ietf.org/html/rfc3447#appendix-B.2.1 - * - * Parameters: - * seed [Buffer] The pseudo random seed for this function - * maskLength [int] The length of the output - * hashFunction [String] The hashing function to use. Will accept any valid crypto hash. Default "sha1" - * Supports "sha1" and "sha256". - * To add another algorythm the algorythem must be accepted by crypto.createHash, and then the length of the output of the hash function (the digest) must be added to the digestLength object below. - * Most RSA implementations will be expecting sha1 - */ -module.exports.eme_oaep_mgf1 = function (seed, maskLength, hashFunction) { - hashFunction = hashFunction || DEFAULT_HASH_FUNCTION; - var hLen = module.exports.digestLength[hashFunction]; - var count = Math.ceil(maskLength / hLen); - var T = Buffer.alloc(hLen * count); - var c = Buffer.alloc(4); - for (var i = 0; i < count; ++i) { - var hash = crypt.createHash(hashFunction); - hash.update(seed); - c.writeUInt32BE(i, 0); - hash.update(c); - hash.digest().copy(T, i * hLen); - } - return T.slice(0, maskLength); -}; - -module.exports.makeScheme = function (key, options) { - function Scheme(key, options) { - this.key = key; - this.options = options; - } - - Scheme.prototype.maxMessageLength = function () { - return this.key.encryptedDataLength - 2 * module.exports.digestLength[this.options.encryptionSchemeOptions.hash || DEFAULT_HASH_FUNCTION] - 2; - }; - - /** - * Pad input - * alg: PKCS1_OAEP - * - * https://tools.ietf.org/html/rfc3447#section-7.1.1 - */ - Scheme.prototype.encPad = function (buffer) { - var hash = this.options.encryptionSchemeOptions.hash || DEFAULT_HASH_FUNCTION; - var mgf = this.options.encryptionSchemeOptions.mgf || module.exports.eme_oaep_mgf1; - var label = this.options.encryptionSchemeOptions.label || Buffer.alloc(0); - var emLen = this.key.encryptedDataLength; - - var hLen = module.exports.digestLength[hash]; - - // Make sure we can put message into an encoded message of emLen bytes - if (buffer.length > emLen - 2 * hLen - 2) { - throw new Error("Message is too long to encode into an encoded message with a length of " + emLen + " bytes, increase" + - "emLen to fix this error (minimum value for given parameters and options: " + (emLen - 2 * hLen - 2) + ")"); - } - - var lHash = crypt.createHash(hash); - lHash.update(label); - lHash = lHash.digest(); - - var PS = Buffer.alloc(emLen - buffer.length - 2 * hLen - 1); // Padding "String" - PS.fill(0); // Fill the buffer with octets of 0 - PS[PS.length - 1] = 1; - - var DB = Buffer.concat([lHash, PS, buffer]); - var seed = crypt.randomBytes(hLen); - - // mask = dbMask - var mask = mgf(seed, DB.length, hash); - // XOR DB and dbMask together. - for (var i = 0; i < DB.length; i++) { - DB[i] ^= mask[i]; - } - // DB = maskedDB - - // mask = seedMask - mask = mgf(DB, hLen, hash); - // XOR seed and seedMask together. - for (i = 0; i < seed.length; i++) { - seed[i] ^= mask[i]; - } - // seed = maskedSeed - - var em = Buffer.alloc(1 + seed.length + DB.length); - em[0] = 0; - seed.copy(em, 1); - DB.copy(em, 1 + seed.length); - - return em; - }; - - /** - * Unpad input - * alg: PKCS1_OAEP - * - * Note: This method works within the buffer given and modifies the values. It also returns a slice of the EM as the return Message. - * If the implementation requires that the EM parameter be unmodified then the implementation should pass in a clone of the EM buffer. - * - * https://tools.ietf.org/html/rfc3447#section-7.1.2 - */ - Scheme.prototype.encUnPad = function (buffer) { - var hash = this.options.encryptionSchemeOptions.hash || DEFAULT_HASH_FUNCTION; - var mgf = this.options.encryptionSchemeOptions.mgf || module.exports.eme_oaep_mgf1; - var label = this.options.encryptionSchemeOptions.label || Buffer.alloc(0); - - var hLen = module.exports.digestLength[hash]; - - // Check to see if buffer is a properly encoded OAEP message - if (buffer.length < 2 * hLen + 2) { - throw new Error("Error decoding message, the supplied message is not long enough to be a valid OAEP encoded message"); - } - - var seed = buffer.slice(1, hLen + 1); // seed = maskedSeed - var DB = buffer.slice(1 + hLen); // DB = maskedDB - - var mask = mgf(DB, hLen, hash); // seedMask - // XOR maskedSeed and seedMask together to get the original seed. - for (var i = 0; i < seed.length; i++) { - seed[i] ^= mask[i]; - } - - mask = mgf(seed, DB.length, hash); // dbMask - // XOR DB and dbMask together to get the original data block. - for (i = 0; i < DB.length; i++) { - DB[i] ^= mask[i]; - } - - var lHash = crypt.createHash(hash); - lHash.update(label); - lHash = lHash.digest(); - - var lHashEM = DB.slice(0, hLen); - if (lHashEM.toString("hex") != lHash.toString("hex")) { - throw new Error("Error decoding message, the lHash calculated from the label provided and the lHash in the encrypted data do not match."); - } - - // Filter out padding - i = hLen; - while (DB[i++] === 0 && i < DB.length); - if (DB[i - 1] != 1) { - throw new Error("Error decoding message, there is no padding message separator byte"); - } - - return DB.slice(i); // Message - }; - - return new Scheme(key, options); -}; - -}).call(this)}).call(this,require("buffer").Buffer) -},{"../libs/jsbn":174,"buffer":63,"crypto":91}],177:[function(require,module,exports){ -(function (Buffer){(function (){ -/** - * PKCS1 padding and signature scheme - */ - -var BigInteger = require('../libs/jsbn'); -var crypt = require('crypto'); -var constants = require('constants'); -var SIGN_INFO_HEAD = { - md2: Buffer.from('3020300c06082a864886f70d020205000410', 'hex'), - md5: Buffer.from('3020300c06082a864886f70d020505000410', 'hex'), - sha1: Buffer.from('3021300906052b0e03021a05000414', 'hex'), - sha224: Buffer.from('302d300d06096086480165030402040500041c', 'hex'), - sha256: Buffer.from('3031300d060960864801650304020105000420', 'hex'), - sha384: Buffer.from('3041300d060960864801650304020205000430', 'hex'), - sha512: Buffer.from('3051300d060960864801650304020305000440', 'hex'), - ripemd160: Buffer.from('3021300906052b2403020105000414', 'hex'), - rmd160: Buffer.from('3021300906052b2403020105000414', 'hex') -}; - -var SIGN_ALG_TO_HASH_ALIASES = { - 'ripemd160': 'rmd160' -}; - -var DEFAULT_HASH_FUNCTION = 'sha256'; - -module.exports = { - isEncryption: true, - isSignature: true -}; - -module.exports.makeScheme = function (key, options) { - function Scheme(key, options) { - this.key = key; - this.options = options; - } - - Scheme.prototype.maxMessageLength = function () { - if (this.options.encryptionSchemeOptions && this.options.encryptionSchemeOptions.padding == constants.RSA_NO_PADDING) { - return this.key.encryptedDataLength; - } - return this.key.encryptedDataLength - 11; - }; - - /** - * Pad input Buffer to encryptedDataLength bytes, and return Buffer.from - * alg: PKCS#1 - * @param buffer - * @returns {Buffer} - */ - Scheme.prototype.encPad = function (buffer, options) { - options = options || {}; - var filled; - if (buffer.length > this.key.maxMessageLength) { - throw new Error("Message too long for RSA (n=" + this.key.encryptedDataLength + ", l=" + buffer.length + ")"); - } - if (this.options.encryptionSchemeOptions && this.options.encryptionSchemeOptions.padding == constants.RSA_NO_PADDING) { - //RSA_NO_PADDING treated like JAVA left pad with zero character - filled = Buffer.alloc(this.key.maxMessageLength - buffer.length); - filled.fill(0); - return Buffer.concat([filled, buffer]); - } - - /* Type 1: zeros padding for private key encrypt */ - if (options.type === 1) { - filled = Buffer.alloc(this.key.encryptedDataLength - buffer.length - 1); - filled.fill(0xff, 0, filled.length - 1); - filled[0] = 1; - filled[filled.length - 1] = 0; - - return Buffer.concat([filled, buffer]); - } else { - /* random padding for public key encrypt */ - filled = Buffer.alloc(this.key.encryptedDataLength - buffer.length); - filled[0] = 0; - filled[1] = 2; - var rand = crypt.randomBytes(filled.length - 3); - for (var i = 0; i < rand.length; i++) { - var r = rand[i]; - while (r === 0) { // non-zero only - r = crypt.randomBytes(1)[0]; - } - filled[i + 2] = r; - } - filled[filled.length - 1] = 0; - return Buffer.concat([filled, buffer]); - } - }; - - /** - * Unpad input Buffer and, if valid, return the Buffer object - * alg: PKCS#1 (type 2, random) - * @param buffer - * @returns {Buffer} - */ - Scheme.prototype.encUnPad = function (buffer, options) { - options = options || {}; - var i = 0; - - if (this.options.encryptionSchemeOptions && this.options.encryptionSchemeOptions.padding == constants.RSA_NO_PADDING) { - //RSA_NO_PADDING treated like JAVA left pad with zero character - var unPad; - if (typeof buffer.lastIndexOf == "function") { //patch for old node version - unPad = buffer.slice(buffer.lastIndexOf('\0') + 1, buffer.length); - } else { - unPad = buffer.slice(String.prototype.lastIndexOf.call(buffer, '\0') + 1, buffer.length); - } - return unPad; - } - - if (buffer.length < 4) { - return null; - } - - /* Type 1: zeros padding for private key decrypt */ - if (options.type === 1) { - if (buffer[0] !== 0 || buffer[1] !== 1) { - return null; - } - i = 3; - while (buffer[i] !== 0) { - if (buffer[i] != 0xFF || ++i >= buffer.length) { - return null; - } - } - } else { - /* random padding for public key decrypt */ - if (buffer[0] !== 0 || buffer[1] !== 2) { - return null; - } - i = 3; - while (buffer[i] !== 0) { - if (++i >= buffer.length) { - return null; - } - } - } - return buffer.slice(i + 1, buffer.length); - }; - - Scheme.prototype.sign = function (buffer) { - var hashAlgorithm = this.options.signingSchemeOptions.hash || DEFAULT_HASH_FUNCTION; - if (this.options.environment === 'browser') { - hashAlgorithm = SIGN_ALG_TO_HASH_ALIASES[hashAlgorithm] || hashAlgorithm; - - var hasher = crypt.createHash(hashAlgorithm); - hasher.update(buffer); - var hash = this.pkcs1pad(hasher.digest(), hashAlgorithm); - var res = this.key.$doPrivate(new BigInteger(hash)).toBuffer(this.key.encryptedDataLength); - - return res; - } else { - var signer = crypt.createSign('RSA-' + hashAlgorithm.toUpperCase()); - signer.update(buffer); - return signer.sign(this.options.rsaUtils.exportKey('private')); - } - }; - - Scheme.prototype.verify = function (buffer, signature, signature_encoding) { - if (this.options.encryptionSchemeOptions && this.options.encryptionSchemeOptions.padding == constants.RSA_NO_PADDING) { - //RSA_NO_PADDING has no verify data - return false; - } - var hashAlgorithm = this.options.signingSchemeOptions.hash || DEFAULT_HASH_FUNCTION; - if (this.options.environment === 'browser') { - hashAlgorithm = SIGN_ALG_TO_HASH_ALIASES[hashAlgorithm] || hashAlgorithm; - - if (signature_encoding) { - signature = Buffer.from(signature, signature_encoding); - } - - var hasher = crypt.createHash(hashAlgorithm); - hasher.update(buffer); - var hash = this.pkcs1pad(hasher.digest(), hashAlgorithm); - var m = this.key.$doPublic(new BigInteger(signature)); - - return m.toBuffer().toString('hex') == hash.toString('hex'); - } else { - var verifier = crypt.createVerify('RSA-' + hashAlgorithm.toUpperCase()); - verifier.update(buffer); - return verifier.verify(this.options.rsaUtils.exportKey('public'), signature, signature_encoding); - } - }; - - /** - * PKCS#1 zero pad input buffer to max data length - * @param hashBuf - * @param hashAlgorithm - * @returns {*} - */ - Scheme.prototype.pkcs0pad = function (buffer) { - var filled = Buffer.alloc(this.key.maxMessageLength - buffer.length); - filled.fill(0); - return Buffer.concat([filled, buffer]); - }; - - Scheme.prototype.pkcs0unpad = function (buffer) { - var unPad; - if (typeof buffer.lastIndexOf == "function") { //patch for old node version - unPad = buffer.slice(buffer.lastIndexOf('\0') + 1, buffer.length); - } else { - unPad = buffer.slice(String.prototype.lastIndexOf.call(buffer, '\0') + 1, buffer.length); - } - - return unPad; - }; - - /** - * PKCS#1 pad input buffer to max data length - * @param hashBuf - * @param hashAlgorithm - * @returns {*} - */ - Scheme.prototype.pkcs1pad = function (hashBuf, hashAlgorithm) { - var digest = SIGN_INFO_HEAD[hashAlgorithm]; - if (!digest) { - throw Error('Unsupported hash algorithm'); - } - - var data = Buffer.concat([digest, hashBuf]); - - if (data.length + 10 > this.key.encryptedDataLength) { - throw Error('Key is too short for signing algorithm (' + hashAlgorithm + ')'); - } - - var filled = Buffer.alloc(this.key.encryptedDataLength - data.length - 1); - filled.fill(0xff, 0, filled.length - 1); - filled[0] = 1; - filled[filled.length - 1] = 0; - - var res = Buffer.concat([filled, data]); - - return res; - }; - - return new Scheme(key, options); -}; - - - -}).call(this)}).call(this,require("buffer").Buffer) -},{"../libs/jsbn":174,"buffer":63,"constants":78,"crypto":91}],178:[function(require,module,exports){ -(function (Buffer){(function (){ -/** - * PSS signature scheme - */ - -var BigInteger = require('../libs/jsbn'); -var crypt = require('crypto'); - -module.exports = { - isEncryption: false, - isSignature: true -}; - -var DEFAULT_HASH_FUNCTION = 'sha1'; -var DEFAULT_SALT_LENGTH = 20; - -module.exports.makeScheme = function (key, options) { - var OAEP = require('./schemes').pkcs1_oaep; - - /** - * @param key - * @param options - * options [Object] An object that contains the following keys that specify certain options for encoding. - * └>signingSchemeOptions - * ├>hash [String] Hash function to use when encoding and generating masks. Must be a string accepted by node's crypto.createHash function. (default = "sha1") - * ├>mgf [function] The mask generation function to use when encoding. (default = mgf1SHA1) - * └>sLen [uint] The length of the salt to generate. (default = 20) - * @constructor - */ - function Scheme(key, options) { - this.key = key; - this.options = options; - } - - Scheme.prototype.sign = function (buffer) { - var mHash = crypt.createHash(this.options.signingSchemeOptions.hash || DEFAULT_HASH_FUNCTION); - mHash.update(buffer); - - var encoded = this.emsa_pss_encode(mHash.digest(), this.key.keySize - 1); - return this.key.$doPrivate(new BigInteger(encoded)).toBuffer(this.key.encryptedDataLength); - }; - - Scheme.prototype.verify = function (buffer, signature, signature_encoding) { - if (signature_encoding) { - signature = Buffer.from(signature, signature_encoding); - } - signature = new BigInteger(signature); - - var emLen = Math.ceil((this.key.keySize - 1) / 8); - var m = this.key.$doPublic(signature).toBuffer(emLen); - - var mHash = crypt.createHash(this.options.signingSchemeOptions.hash || DEFAULT_HASH_FUNCTION); - mHash.update(buffer); - - return this.emsa_pss_verify(mHash.digest(), m, this.key.keySize - 1); - }; - - /* - * https://tools.ietf.org/html/rfc3447#section-9.1.1 - * - * mHash [Buffer] Hashed message to encode - * emBits [uint] Maximum length of output in bits. Must be at least 8hLen + 8sLen + 9 (hLen = Hash digest length in bytes | sLen = length of salt in bytes) - * @returns {Buffer} The encoded message - */ - Scheme.prototype.emsa_pss_encode = function (mHash, emBits) { - var hash = this.options.signingSchemeOptions.hash || DEFAULT_HASH_FUNCTION; - var mgf = this.options.signingSchemeOptions.mgf || OAEP.eme_oaep_mgf1; - var sLen = this.options.signingSchemeOptions.saltLength || DEFAULT_SALT_LENGTH; - - var hLen = OAEP.digestLength[hash]; - var emLen = Math.ceil(emBits / 8); - - if (emLen < hLen + sLen + 2) { - throw new Error("Output length passed to emBits(" + emBits + ") is too small for the options " + - "specified(" + hash + ", " + sLen + "). To fix this issue increase the value of emBits. (minimum size: " + - (8 * hLen + 8 * sLen + 9) + ")" - ); - } - - var salt = crypt.randomBytes(sLen); - - var Mapostrophe = Buffer.alloc(8 + hLen + sLen); - Mapostrophe.fill(0, 0, 8); - mHash.copy(Mapostrophe, 8); - salt.copy(Mapostrophe, 8 + mHash.length); - - var H = crypt.createHash(hash); - H.update(Mapostrophe); - H = H.digest(); - - var PS = Buffer.alloc(emLen - salt.length - hLen - 2); - PS.fill(0); - - var DB = Buffer.alloc(PS.length + 1 + salt.length); - PS.copy(DB); - DB[PS.length] = 0x01; - salt.copy(DB, PS.length + 1); - - var dbMask = mgf(H, DB.length, hash); - - // XOR DB and dbMask together - var maskedDB = Buffer.alloc(DB.length); - for (var i = 0; i < dbMask.length; i++) { - maskedDB[i] = DB[i] ^ dbMask[i]; - } - - var bits = 8 * emLen - emBits; - var mask = 255 ^ (255 >> 8 - bits << 8 - bits); - maskedDB[0] = maskedDB[0] & mask; - - var EM = Buffer.alloc(maskedDB.length + H.length + 1); - maskedDB.copy(EM, 0); - H.copy(EM, maskedDB.length); - EM[EM.length - 1] = 0xbc; - - return EM; - }; - - /* - * https://tools.ietf.org/html/rfc3447#section-9.1.2 - * - * mHash [Buffer] Hashed message - * EM [Buffer] Signature - * emBits [uint] Length of EM in bits. Must be at least 8hLen + 8sLen + 9 to be a valid signature. (hLen = Hash digest length in bytes | sLen = length of salt in bytes) - * @returns {Boolean} True if signature(EM) matches message(M) - */ - Scheme.prototype.emsa_pss_verify = function (mHash, EM, emBits) { - var hash = this.options.signingSchemeOptions.hash || DEFAULT_HASH_FUNCTION; - var mgf = this.options.signingSchemeOptions.mgf || OAEP.eme_oaep_mgf1; - var sLen = this.options.signingSchemeOptions.saltLength || DEFAULT_SALT_LENGTH; - - var hLen = OAEP.digestLength[hash]; - var emLen = Math.ceil(emBits / 8); - - if (emLen < hLen + sLen + 2 || EM[EM.length - 1] != 0xbc) { - return false; - } - - var DB = Buffer.alloc(emLen - hLen - 1); - EM.copy(DB, 0, 0, emLen - hLen - 1); - - var mask = 0; - for (var i = 0, bits = 8 * emLen - emBits; i < bits; i++) { - mask |= 1 << (7 - i); - } - - if ((DB[0] & mask) !== 0) { - return false; - } - - var H = EM.slice(emLen - hLen - 1, emLen - 1); - var dbMask = mgf(H, DB.length, hash); - - // Unmask DB - for (i = 0; i < DB.length; i++) { - DB[i] ^= dbMask[i]; - } - - bits = 8 * emLen - emBits; - mask = 255 ^ (255 >> 8 - bits << 8 - bits); - DB[0] = DB[0] & mask; - - // Filter out padding - for (i = 0; DB[i] === 0 && i < DB.length; i++); - if (DB[i] != 1) { - return false; - } - - var salt = DB.slice(DB.length - sLen); - - var Mapostrophe = Buffer.alloc(8 + hLen + sLen); - Mapostrophe.fill(0, 0, 8); - mHash.copy(Mapostrophe, 8); - salt.copy(Mapostrophe, 8 + mHash.length); - - var Hapostrophe = crypt.createHash(hash); - Hapostrophe.update(Mapostrophe); - Hapostrophe = Hapostrophe.digest(); - - return H.toString("hex") === Hapostrophe.toString("hex"); - }; - - return new Scheme(key, options); -}; - -}).call(this)}).call(this,require("buffer").Buffer) -},{"../libs/jsbn":174,"./schemes":179,"buffer":63,"crypto":91}],179:[function(require,module,exports){ -module.exports = { - pkcs1: require('./pkcs1'), - pkcs1_oaep: require('./oaep'), - pss: require('./pss'), - - /** - * Check if scheme has padding methods - * @param scheme {string} - * @returns {Boolean} - */ - isEncryption: function (scheme) { - return module.exports[scheme] && module.exports[scheme].isEncryption; - }, - - /** - * Check if scheme has sign/verify methods - * @param scheme {string} - * @returns {Boolean} - */ - isSignature: function (scheme) { - return module.exports[scheme] && module.exports[scheme].isSignature; - } -}; -},{"./oaep":176,"./pkcs1":177,"./pss":178}],180:[function(require,module,exports){ -(function (process){(function (){ -/* - * Utils functions - * - */ - -var crypt = require('crypto'); - -/** - * Break string str each maxLen symbols - * @param str - * @param maxLen - * @returns {string} - */ -module.exports.linebrk = function (str, maxLen) { - var res = ''; - var i = 0; - while (i + maxLen < str.length) { - res += str.substring(i, i + maxLen) + "\n"; - i += maxLen; - } - return res + str.substring(i, str.length); -}; - -module.exports.detectEnvironment = function () { - if (typeof(window) !== 'undefined' && window && !(process && process.title === 'node')) { - return 'browser'; - } - - return 'node'; -}; - -/** - * Trying get a 32-bit unsigned integer from the partial buffer - * @param buffer - * @param offset - * @returns {Number} - */ -module.exports.get32IntFromBuffer = function (buffer, offset) { - offset = offset || 0; - var size = 0; - if ((size = buffer.length - offset) > 0) { - if (size >= 4) { - return buffer.readUIntBE(offset, size); - } else { - var res = 0; - for (var i = offset + size, d = 0; i > offset; i--, d += 2) { - res += buffer[i - 1] * Math.pow(16, d); - } - return res; - } - } else { - return NaN; - } -}; - -module.exports._ = { - isObject: function (value) { - var type = typeof value; - return !!value && (type == 'object' || type == 'function'); - }, - - isString: function (value) { - return typeof value == 'string' || value instanceof String; - }, - - isNumber: function (value) { - return typeof value == 'number' || !isNaN(parseFloat(value)) && isFinite(value); - }, - - /** - * Returns copy of `obj` without `removeProp` field. - * @param obj - * @param removeProp - * @returns Object - */ - omit: function (obj, removeProp) { - var newObj = {}; - for (var prop in obj) { - if (!obj.hasOwnProperty(prop) || prop === removeProp) { - continue; - } - newObj[prop] = obj[prop]; - } - - return newObj; - } -}; - -/** - * Strips everything around the opening and closing lines, including the lines - * themselves. - */ -module.exports.trimSurroundingText = function (data, opening, closing) { - var trimStartIndex = 0; - var trimEndIndex = data.length; - - var openingBoundaryIndex = data.indexOf(opening); - if (openingBoundaryIndex >= 0) { - trimStartIndex = openingBoundaryIndex + opening.length; - } - - var closingBoundaryIndex = data.indexOf(closing, openingBoundaryIndex); - if (closingBoundaryIndex >= 0) { - trimEndIndex = closingBoundaryIndex; - } - - return data.substring(trimStartIndex, trimEndIndex); -} -}).call(this)}).call(this,require('_process')) -},{"_process":199,"crypto":91}],181:[function(require,module,exports){ -'use strict' - -const stream = require('stream') -const {Buffer} = require('buffer') -const td = new TextDecoder('utf8', {fatal: true, ignoreBOM: true}) - -/** - * @typedef {object} NoFilterOptions - * @property {string|Buffer} [input=null] Input source data. - * @property {BufferEncoding} [inputEncoding=null] Encoding name for input, - * ignored if input is not a String. - * @property {number} [highWaterMark=16384] The maximum number of bytes to - * store in the internal buffer before ceasing to read from the underlying - * resource. Default=16kb, or 16 for objectMode streams. - * @property {BufferEncoding} [encoding=null] If specified, then buffers - * will be decoded to strings using the specified encoding. - * @property {boolean} [objectMode=false] Whether this stream should behave - * as a stream of objects. Meaning that stream.read(n) returns a single - * value instead of a Buffer of size n. - * @property {boolean} [decodeStrings=true] Whether or not to decode - * strings into Buffers before passing them to _write(). - * @property {boolean} [watchPipe=true] Whether to watch for 'pipe' events, - * setting this stream's objectMode based on the objectMode of the input - * stream. - * @property {boolean} [readError=false] If true, when a read() underflows, - * throw an error. - * @property {boolean} [allowHalfOpen=true] If set to false, then the - * stream will automatically end the writable side when the readable side - * ends. - * @property {boolean} [autoDestroy=true] Whether this stream should - * automatically call .destroy() on itself after ending. - * @property {BufferEncoding} [defaultEncoding='utf8'] The default encoding - * that is used when no encoding is specified as an argument to - * stream.write(). - * @property {boolean} [emitClose=true] Whether or not the stream should - * emit 'close' after it has been destroyed. - * @property {number} [readableHighWaterMark] Sets highWaterMark for the - * readable side of the stream. Has no effect if highWaterMark is provided. - * @property {boolean} [readableObjectMode=false] Sets objectMode for - * readable side of the stream. Has no effect if objectMode is true. - * @property {number} [writableHighWaterMark] Sets highWaterMark for the - * writable side of the stream. Has no effect if highWaterMark is provided. - * @property {boolean} [writableObjectMode=false] Sets objectMode for - * writable side of the stream. Has no effect if objectMode is true. - */ - -/** - * NoFilter stream. Can be used to sink or source data to and from - * other node streams. Implemented as the "identity" Transform stream - * (hence the name), but allows for inspecting data that is in-flight. - * - * Allows passing in source data (input, inputEncoding) at creation - * time. Source data can also be passed in the options object. - * - * @example source and sink - * const source = new NoFilter('Zm9v', 'base64') - * source.pipe(process.stdout) - * const sink = new Nofilter() - * // NOTE: 'finish' fires when the input is done writing - * sink.on('finish', () => console.log(n.toString('base64'))) - * process.stdin.pipe(sink) - */ -class NoFilter extends stream.Transform { - /** - * Create an instance of NoFilter. - * - * @param {string|Buffer|BufferEncoding|NoFilterOptions} [input] Source data. - * @param {BufferEncoding|NoFilterOptions} [inputEncoding] Encoding - * name for input, ignored if input is not a String. - * @param {NoFilterOptions} [options] Other options. - */ - constructor(input, inputEncoding, options = {}) { - let inp = null - let inpE = /** @type {BufferEncoding?} */ (null) - switch (typeof input) { - case 'object': - if (Buffer.isBuffer(input)) { - inp = input - } else if (input) { - options = input - } - break - case 'string': - inp = input - break - case 'undefined': - break - default: - throw new TypeError('Invalid input') - } - switch (typeof inputEncoding) { - case 'object': - if (inputEncoding) { - options = inputEncoding - } - break - case 'string': - inpE = /** @type {BufferEncoding} */ (inputEncoding) - break - case 'undefined': - break - default: - throw new TypeError('Invalid inputEncoding') - } - if (!options || typeof options !== 'object') { - throw new TypeError('Invalid options') - } - if (inp == null) { - inp = options.input - } - if (inpE == null) { - inpE = options.inputEncoding - } - delete options.input - delete options.inputEncoding - const watchPipe = options.watchPipe == null ? true : options.watchPipe - delete options.watchPipe - const readError = Boolean(options.readError) - delete options.readError - super(options) - - this.readError = readError - - if (watchPipe) { - this.on('pipe', readable => { - // @ts-ignore: TS2339 (using internal interface) - const om = readable._readableState.objectMode - // @ts-ignore: TS2339 (using internal interface) - if ((this.length > 0) && (om !== this._readableState.objectMode)) { - throw new Error( - 'Do not switch objectMode in the middle of the stream' - ) - } - - // @ts-ignore: TS2339 (using internal interface) - this._readableState.objectMode = om - // @ts-ignore: TS2339 (using internal interface) - this._writableState.objectMode = om - }) - } - - if (inp != null) { - this.end(inp, inpE) - } - } - - /** - * Is the given object a {NoFilter}? - * - * @param {object} obj The object to test. - * @returns {boolean} True if obj is a NoFilter. - */ - static isNoFilter(obj) { - return obj instanceof this - } - - /** - * The same as nf1.compare(nf2). Useful for sorting an Array of NoFilters. - * - * @param {NoFilter} nf1 The first object to compare. - * @param {NoFilter} nf2 The second object to compare. - * @returns {number} -1, 0, 1 for less, equal, greater. - * @throws {TypeError} Arguments not NoFilter instances. - * @example - * const arr = [new NoFilter('1234'), new NoFilter('0123')] - * arr.sort(NoFilter.compare) - */ - static compare(nf1, nf2) { - if (!(nf1 instanceof this)) { - throw new TypeError('Arguments must be NoFilters') - } - if (nf1 === nf2) { - return 0 - } - return nf1.compare(nf2) - } - - /** - * Returns a buffer which is the result of concatenating all the - * NoFilters in the list together. If the list has no items, or if - * the totalLength is 0, then it returns a zero-length buffer. - * - * If length is not provided, it is read from the buffers in the - * list. However, this adds an additional loop to the function, so - * it is faster to provide the length explicitly if you already know it. - * - * @param {Array} list Inputs. Must not be all either in object - * mode, or all not in object mode. - * @param {number} [length=null] Number of bytes or objects to read. - * @returns {Buffer|Array} The concatenated values as an array if in object - * mode, otherwise a Buffer. - * @throws {TypeError} List not array of NoFilters. - */ - static concat(list, length) { - if (!Array.isArray(list)) { - throw new TypeError('list argument must be an Array of NoFilters') - } - if ((list.length === 0) || (length === 0)) { - return Buffer.alloc(0) - } - if ((length == null)) { - length = list.reduce((tot, nf) => { - if (!(nf instanceof NoFilter)) { - throw new TypeError('list argument must be an Array of NoFilters') - } - return tot + nf.length - }, 0) - } - let allBufs = true - let allObjs = true - const bufs = list.map(nf => { - if (!(nf instanceof NoFilter)) { - throw new TypeError('list argument must be an Array of NoFilters') - } - const buf = nf.slice() - if (Buffer.isBuffer(buf)) { - allObjs = false - } else { - allBufs = false - } - return buf - }) - if (allBufs) { - // @ts-ignore: TS2322, tsc can't see the type checking above - return Buffer.concat(bufs, length) - } - if (allObjs) { - return [].concat(...bufs).slice(0, length) - } - // TODO: maybe coalesce buffers, counting bytes, and flatten in arrays - // counting objects? I can't imagine why that would be useful. - throw new Error('Concatenating mixed object and byte streams not supported') - } - - /** - * @ignore - */ - _transform(chunk, encoding, callback) { - // @ts-ignore: TS2339 (using internal interface) - if (!this._readableState.objectMode && !Buffer.isBuffer(chunk)) { - chunk = Buffer.from(chunk, encoding) - } - this.push(chunk) - callback() - } - - /** - * @returns {Buffer[]} The current internal buffers. They are layed out - * end to end. - * @ignore - */ - _bufArray() { - // @ts-ignore: TS2339 (using internal interface) - let bufs = this._readableState.buffer - // HACK: replace with something else one day. This is what I get for - // relying on internals. - if (!Array.isArray(bufs)) { - let b = bufs.head - bufs = [] - while (b != null) { - bufs.push(b.data) - b = b.next - } - } - return bufs - } - - /** - * Pulls some data out of the internal buffer and returns it. - * If there is no data available, then it will return null. - * - * If you pass in a size argument, then it will return that many bytes. If - * size bytes are not available, then it will return null, unless we've - * ended, in which case it will return the data remaining in the buffer. - * - * If you do not specify a size argument, then it will return all the data in - * the internal buffer. - * - * @param {number} [size=null] Number of bytes to read. - * @returns {string|Buffer|null} If no data or not enough data, null. If - * decoding output a string, otherwise a Buffer. - * @throws Error If readError is true and there was underflow. - * @fires NoFilter#read When read from. - */ - read(size) { - const buf = super.read(size) - if (buf != null) { - /** - * Read event. Fired whenever anything is read from the stream. - * - * @event NoFilter#read - * @param {Buffer|string|object} buf What was read. - */ - this.emit('read', buf) - if (this.readError && (buf.length < size)) { - throw new Error(`Read ${buf.length}, wanted ${size}`) - } - } else if (this.readError) { - throw new Error(`No data available, wanted ${size}`) - } - return buf - } - - /** - * Read the full number of bytes asked for, no matter how long it takes. - * Fail if an error occurs in the meantime, or if the stream finishes before - * enough data is available. - * - * Note: This function won't work fully correctly if you are using - * stream-browserify (for example, on the Web). - * - * @param {number} size The number of bytes to read. - * @returns {Promise} A promise for the data read. - */ - readFull(size) { - let onReadable = null - let onFinish = null - let onError = null - return new Promise((resolve, reject) => { - if (this.length >= size) { - resolve(this.read(size)) - return - } - - // Added in Node 12.19. This won't work with stream-browserify yet. - // If it's needed, file a bug, and I'll do a work-around. - if (this.writableFinished) { - // Already finished writing, so no more coming. - reject(new Error(`Stream finished before ${size} bytes were available`)) - return - } - - onReadable = chunk => { - if (this.length >= size) { - resolve(this.read(size)) - } - } - onFinish = () => { - reject(new Error(`Stream finished before ${size} bytes were available`)) - } - onError = reject - this.on('readable', onReadable) - this.on('error', onError) - this.on('finish', onFinish) - }).finally(() => { - if (onReadable) { - this.removeListener('readable', onReadable) - this.removeListener('error', onError) - this.removeListener('finish', onFinish) - } - }) - } - - /** - * Return a promise fulfilled with the full contents, after the 'finish' - * event fires. Errors on the stream cause the promise to be rejected. - * - * @param {Function} [cb=null] Finished/error callback used in *addition* - * to the promise. - * @returns {Promise} Fulfilled when complete. - */ - promise(cb) { - let done = false - return new Promise((resolve, reject) => { - this.on('finish', () => { - const data = this.read() - if ((cb != null) && !done) { - done = true - cb(null, data) - } - resolve(data) - }) - this.on('error', er => { - if ((cb != null) && !done) { - done = true - cb(er) - } - reject(er) - }) - }) - } - - /** - * Returns a number indicating whether this comes before or after or is the - * same as the other NoFilter in sort order. - * - * @param {NoFilter} other The other object to compare. - * @returns {number} -1, 0, 1 for less, equal, greater. - * @throws {TypeError} Arguments must be NoFilters. - */ - compare(other) { - if (!(other instanceof NoFilter)) { - throw new TypeError('Arguments must be NoFilters') - } - if (this === other) { - return 0 - } - - const buf1 = this.slice() - const buf2 = other.slice() - // These will both be buffers because of the check above. - if (Buffer.isBuffer(buf1) && Buffer.isBuffer(buf2)) { - return buf1.compare(buf2) - } - throw new Error('Cannot compare streams in object mode') - } - - /** - * Do these NoFilter's contain the same bytes? Doesn't work if either is - * in object mode. - * - * @param {NoFilter} other Other NoFilter to compare against. - * @returns {boolean} Equal? - */ - equals(other) { - return this.compare(other) === 0 - } - - /** - * Read bytes or objects without consuming them. Useful for diagnostics. - * Note: as a side-effect, concatenates multiple writes together into what - * looks like a single write, so that this concat doesn't have to happen - * multiple times when you're futzing with the same NoFilter. - * - * @param {number} [start=0] Beginning offset. - * @param {number} [end=length] Ending offset. - * @returns {Buffer|Array} If in object mode, an array of objects. Otherwise, - * concatenated array of contents. - */ - slice(start, end) { - // @ts-ignore: TS2339 (using internal interface) - if (this._readableState.objectMode) { - return this._bufArray().slice(start, end) - } - const bufs = this._bufArray() - switch (bufs.length) { - case 0: return Buffer.alloc(0) - case 1: return bufs[0].slice(start, end) - default: { - const b = Buffer.concat(bufs) - // TODO: store the concatented bufs back - // @_readableState.buffer = [b] - return b.slice(start, end) - } - } - } - - /** - * Get a byte by offset. I didn't want to get into metaprogramming - * to give you the `NoFilter[0]` syntax. - * - * @param {number} index The byte to retrieve. - * @returns {number} 0-255. - */ - get(index) { - return this.slice()[index] - } - - /** - * Return an object compatible with Buffer's toJSON implementation, so that - * round-tripping will produce a Buffer. - * - * @returns {string|Array|{type: 'Buffer',data: number[]}} If in object mode, - * the objects. Otherwise, JSON text. - * @example output for 'foo', not in object mode - * ({ - * type: 'Buffer', - * data: [102, 111, 111], - * }) - */ - toJSON() { - const b = this.slice() - if (Buffer.isBuffer(b)) { - return b.toJSON() - } - return b - } - - /** - * Decodes and returns a string from buffer data encoded using the specified - * character set encoding. If encoding is undefined or null, then encoding - * defaults to 'utf8'. The start and end parameters default to 0 and - * NoFilter.length when undefined. - * - * @param {BufferEncoding} [encoding='utf8'] Which to use for decoding? - * @param {number} [start=0] Start offset. - * @param {number} [end=length] End offset. - * @returns {string} String version of the contents. - */ - toString(encoding, start, end) { - const buf = this.slice(start, end) - if (!Buffer.isBuffer(buf)) { - return JSON.stringify(buf) - } - if (!encoding || (encoding === 'utf8')) { - return td.decode(buf) - } - return buf.toString(encoding) - } - - /** - * @ignore - */ - [Symbol.for('nodejs.util.inspect.custom')](depth, options) { - const bufs = this._bufArray() - const hex = bufs.map(b => { - if (Buffer.isBuffer(b)) { - return options.stylize(b.toString('hex'), 'string') - } - return JSON.stringify(b) - }).join(', ') - return `${this.constructor.name} [${hex}]` - } - - /** - * Current readable length, in bytes. - * - * @returns {number} Length of the contents. - */ - get length() { - // @ts-ignore: TS2339 (using internal interface) - return this._readableState.length - } - - /** - * Write a JavaScript BigInt to the stream. Negative numbers will be - * written as their 2's complement version. - * - * @param {bigint} val The value to write. - * @returns {boolean} True on success. - */ - writeBigInt(val) { - let str = val.toString(16) - if (val < 0) { - // Two's complement - // Note: str always starts with '-' here. - const sz = BigInt(Math.floor(str.length / 2)) - const mask = BigInt(1) << (sz * BigInt(8)) - val = mask + val - str = val.toString(16) - } - if (str.length % 2) { - str = `0${str}` - } - return this.push(Buffer.from(str, 'hex')) - } - - /** - * Read a variable-sized JavaScript unsigned BigInt from the stream. - * - * @param {number} [len=null] Number of bytes to read or all remaining - * if null. - * @returns {bigint} A BigInt. - */ - readUBigInt(len) { - const b = this.read(len) - if (!Buffer.isBuffer(b)) { - return null - } - return BigInt(`0x${b.toString('hex')}`) - } - - /** - * Read a variable-sized JavaScript signed BigInt from the stream in 2's - * complement format. - * - * @param {number} [len=null] Number of bytes to read or all remaining - * if null. - * @returns {bigint} A BigInt. - */ - readBigInt(len) { - const b = this.read(len) - if (!Buffer.isBuffer(b)) { - return null - } - let ret = BigInt(`0x${b.toString('hex')}`) - // Negative? - if (b[0] & 0x80) { - // Two's complement - const mask = BigInt(1) << (BigInt(b.length) * BigInt(8)) - ret -= mask - } - return ret - } - - /** - * Write an 8-bit unsigned integer to the stream. Adds 1 byte. - * - * @param {number} value 0..255. - * @returns {boolean} True on success. - */ - writeUInt8(value) { - const b = Buffer.from([value]) - return this.push(b) - } - - /** - * Write a little-endian 16-bit unsigned integer to the stream. Adds - * 2 bytes. - * - * @param {number} value 0..65535. - * @returns {boolean} True on success. - */ - writeUInt16LE(value) { - const b = Buffer.alloc(2) - b.writeUInt16LE(value) - return this.push(b) - } - - /** - * Write a big-endian 16-bit unsigned integer to the stream. Adds - * 2 bytes. - * - * @param {number} value 0..65535. - * @returns {boolean} True on success. - */ - writeUInt16BE(value) { - const b = Buffer.alloc(2) - b.writeUInt16BE(value) - return this.push(b) - } - - /** - * Write a little-endian 32-bit unsigned integer to the stream. Adds - * 4 bytes. - * - * @param {number} value 0..2**32-1. - * @returns {boolean} True on success. - */ - writeUInt32LE(value) { - const b = Buffer.alloc(4) - b.writeUInt32LE(value) - return this.push(b) - } - - /** - * Write a big-endian 32-bit unsigned integer to the stream. Adds - * 4 bytes. - * - * @param {number} value 0..2**32-1. - * @returns {boolean} True on success. - */ - writeUInt32BE(value) { - const b = Buffer.alloc(4) - b.writeUInt32BE(value) - return this.push(b) - } - - /** - * Write a signed 8-bit integer to the stream. Adds 1 byte. - * - * @param {number} value (-128)..127. - * @returns {boolean} True on success. - */ - writeInt8(value) { - const b = Buffer.from([value]) - return this.push(b) - } - - /** - * Write a signed little-endian 16-bit integer to the stream. Adds 2 bytes. - * - * @param {number} value (-32768)..32767. - * @returns {boolean} True on success. - */ - writeInt16LE(value) { - const b = Buffer.alloc(2) - b.writeUInt16LE(value) - return this.push(b) - } - - /** - * Write a signed big-endian 16-bit integer to the stream. Adds 2 bytes. - * - * @param {number} value (-32768)..32767. - * @returns {boolean} True on success. - */ - writeInt16BE(value) { - const b = Buffer.alloc(2) - b.writeUInt16BE(value) - return this.push(b) - } - - /** - * Write a signed little-endian 32-bit integer to the stream. Adds 4 bytes. - * - * @param {number} value (-2**31)..(2**31-1). - * @returns {boolean} True on success. - */ - writeInt32LE(value) { - const b = Buffer.alloc(4) - b.writeUInt32LE(value) - return this.push(b) - } - - /** - * Write a signed big-endian 32-bit integer to the stream. Adds 4 bytes. - * - * @param {number} value (-2**31)..(2**31-1). - * @returns {boolean} True on success. - */ - writeInt32BE(value) { - const b = Buffer.alloc(4) - b.writeUInt32BE(value) - return this.push(b) - } - - /** - * Write a little-endian 32-bit float to the stream. Adds 4 bytes. - * - * @param {number} value 32-bit float. - * @returns {boolean} True on success. - */ - writeFloatLE(value) { - const b = Buffer.alloc(4) - b.writeFloatLE(value) - return this.push(b) - } - - /** - * Write a big-endian 32-bit float to the stream. Adds 4 bytes. - * - * @param {number} value 32-bit float. - * @returns {boolean} True on success. - */ - writeFloatBE(value) { - const b = Buffer.alloc(4) - b.writeFloatBE(value) - return this.push(b) - } - - /** - * Write a little-endian 64-bit double to the stream. Adds 8 bytes. - * - * @param {number} value 64-bit float. - * @returns {boolean} True on success. - */ - writeDoubleLE(value) { - const b = Buffer.alloc(8) - b.writeDoubleLE(value) - return this.push(b) - } - - /** - * Write a big-endian 64-bit float to the stream. Adds 8 bytes. - * - * @param {number} value 64-bit float. - * @returns {boolean} True on success. - */ - writeDoubleBE(value) { - const b = Buffer.alloc(8) - b.writeDoubleBE(value) - return this.push(b) - } - - /** - * Write a signed little-endian 64-bit BigInt to the stream. Adds 8 bytes. - * - * @param {bigint} value BigInt. - * @returns {boolean} True on success. - */ - writeBigInt64LE(value) { - const b = Buffer.alloc(8) - b.writeBigInt64LE(value) - return this.push(b) - } - - /** - * Write a signed big-endian 64-bit BigInt to the stream. Adds 8 bytes. - * - * @param {bigint} value BigInt. - * @returns {boolean} True on success. - */ - writeBigInt64BE(value) { - const b = Buffer.alloc(8) - b.writeBigInt64BE(value) - return this.push(b) - } - - /** - * Write an unsigned little-endian 64-bit BigInt to the stream. Adds 8 bytes. - * - * @param {bigint} value Non-negative BigInt. - * @returns {boolean} True on success. - */ - writeBigUInt64LE(value) { - const b = Buffer.alloc(8) - b.writeBigUInt64LE(value) - return this.push(b) - } - - /** - * Write an unsigned big-endian 64-bit BigInt to the stream. Adds 8 bytes. - * - * @param {bigint} value Non-negative BigInt. - * @returns {boolean} True on success. - */ - writeBigUInt64BE(value) { - const b = Buffer.alloc(8) - b.writeBigUInt64BE(value) - return this.push(b) - } - - /** - * Read an unsigned 8-bit integer from the stream. Consumes 1 byte. - * - * @returns {number} Value read. - */ - readUInt8() { - const b = this.read(1) - if (!Buffer.isBuffer(b)) { - return null - } - return b.readUInt8() - } - - /** - * Read a little-endian unsigned 16-bit integer from the stream. - * Consumes 2 bytes. - * - * @returns {number} Value read. - */ - readUInt16LE() { - const b = this.read(2) - if (!Buffer.isBuffer(b)) { - return null - } - return b.readUInt16LE() - } - - /** - * Read a little-endian unsigned 16-bit integer from the stream. - * Consumes 2 bytes. - * - * @returns {number} Value read. - */ - readUInt16BE() { - const b = this.read(2) - if (!Buffer.isBuffer(b)) { - return null - } - return b.readUInt16BE() - } - - /** - * Read a little-endian unsigned 32-bit integer from the stream. - * Consumes 4 bytes. - * - * @returns {number} Value read. - */ - readUInt32LE() { - const b = this.read(4) - if (!Buffer.isBuffer(b)) { - return null - } - return b.readUInt32LE() - } - - /** - * Read a little-endian unsigned 16-bit integer from the stream. - * Consumes 4 bytes. - * - * @returns {number} Value read. - */ - readUInt32BE() { - const b = this.read(4) - if (!Buffer.isBuffer(b)) { - return null - } - return b.readUInt32BE() - } - - /** - * Read a signed 8-bit integer from the stream. Consumes 1 byte. - * - * @returns {number} Value read. - */ - readInt8() { - const b = this.read(1) - if (!Buffer.isBuffer(b)) { - return null - } - return b.readInt8() - } - - /** - * Read a little-endian signed 16-bit integer from the stream. - * Consumes 2 bytes. - * - * @returns {number} Value read. - */ - readInt16LE() { - const b = this.read(2) - if (!Buffer.isBuffer(b)) { - return null - } - return b.readInt16LE() - } - - /** - * Read a little-endian signed 16-bit integer from the stream. - * Consumes 2 bytes. - * - * @returns {number} Value read. - */ - readInt16BE() { - const b = this.read(2) - if (!Buffer.isBuffer(b)) { - return null - } - return b.readInt16BE() - } - - /** - * Read a little-endian signed 32-bit integer from the stream. - * Consumes 4 bytes. - * - * @returns {number} Value read. - */ - readInt32LE() { - const b = this.read(4) - if (!Buffer.isBuffer(b)) { - return null - } - return b.readInt32LE() - } - - /** - * Read a little-endian signed 16-bit integer from the stream. - * Consumes 4 bytes. - * - * @returns {number} Value read. - */ - readInt32BE() { - const b = this.read(4) - if (!Buffer.isBuffer(b)) { - return null - } - return b.readInt32BE() - } - - /** - * Read a 32-bit little-endian float from the stream. - * Consumes 4 bytes. - * - * @returns {number} Value read. - */ - readFloatLE() { - const b = this.read(4) - if (!Buffer.isBuffer(b)) { - return null - } - return b.readFloatLE() - } - - /** - * Read a 32-bit big-endian float from the stream. - * Consumes 4 bytes. - * - * @returns {number} Value read. - */ - readFloatBE() { - const b = this.read(4) - if (!Buffer.isBuffer(b)) { - return null - } - return b.readFloatBE() - } - - /** - * Read a 64-bit little-endian float from the stream. - * Consumes 8 bytes. - * - * @returns {number} Value read. - */ - readDoubleLE() { - const b = this.read(8) - if (!Buffer.isBuffer(b)) { - return null - } - return b.readDoubleLE() - } - - /** - * Read a 64-bit big-endian float from the stream. - * Consumes 8 bytes. - * - * @returns {number} Value read. - */ - readDoubleBE() { - const b = this.read(8) - if (!Buffer.isBuffer(b)) { - return null - } - return b.readDoubleBE() - } - - /** - * Read a signed 64-bit little-endian BigInt from the stream. - * Consumes 8 bytes. - * - * @returns {bigint} Value read. - */ - readBigInt64LE() { - const b = this.read(8) - if (!Buffer.isBuffer(b)) { - return null - } - return b.readBigInt64LE() - } - - /** - * Read a signed 64-bit big-endian BigInt from the stream. - * Consumes 8 bytes. - * - * @returns {bigint} Value read. - */ - readBigInt64BE() { - const b = this.read(8) - if (!Buffer.isBuffer(b)) { - return null - } - return b.readBigInt64BE() - } - - /** - * Read an unsigned 64-bit little-endian BigInt from the stream. - * Consumes 8 bytes. - * - * @returns {bigint} Value read. - */ - readBigUInt64LE() { - const b = this.read(8) - if (!Buffer.isBuffer(b)) { - return null - } - return b.readBigUInt64LE() - } - - /** - * Read an unsigned 64-bit big-endian BigInt from the stream. - * Consumes 8 bytes. - * - * @returns {bigint} Value read. - */ - readBigUInt64BE() { - const b = this.read(8) - if (!Buffer.isBuffer(b)) { - return null - } - return b.readBigUInt64BE() - } -} - -module.exports = NoFilter - -},{"buffer":63,"stream":232}],182:[function(require,module,exports){ -'use strict'; - -var keysShim; -if (!Object.keys) { - // modified from https://github.com/es-shims/es5-shim - var has = Object.prototype.hasOwnProperty; - var toStr = Object.prototype.toString; - var isArgs = require('./isArguments'); // eslint-disable-line global-require - var isEnumerable = Object.prototype.propertyIsEnumerable; - var hasDontEnumBug = !isEnumerable.call({ toString: null }, 'toString'); - var hasProtoEnumBug = isEnumerable.call(function () {}, 'prototype'); - var dontEnums = [ - 'toString', - 'toLocaleString', - 'valueOf', - 'hasOwnProperty', - 'isPrototypeOf', - 'propertyIsEnumerable', - 'constructor' - ]; - var equalsConstructorPrototype = function (o) { - var ctor = o.constructor; - return ctor && ctor.prototype === o; - }; - var excludedKeys = { - $applicationCache: true, - $console: true, - $external: true, - $frame: true, - $frameElement: true, - $frames: true, - $innerHeight: true, - $innerWidth: true, - $onmozfullscreenchange: true, - $onmozfullscreenerror: true, - $outerHeight: true, - $outerWidth: true, - $pageXOffset: true, - $pageYOffset: true, - $parent: true, - $scrollLeft: true, - $scrollTop: true, - $scrollX: true, - $scrollY: true, - $self: true, - $webkitIndexedDB: true, - $webkitStorageInfo: true, - $window: true - }; - var hasAutomationEqualityBug = (function () { - /* global window */ - if (typeof window === 'undefined') { return false; } - for (var k in window) { - try { - if (!excludedKeys['$' + k] && has.call(window, k) && window[k] !== null && typeof window[k] === 'object') { - try { - equalsConstructorPrototype(window[k]); - } catch (e) { - return true; - } - } - } catch (e) { - return true; - } - } - return false; - }()); - var equalsConstructorPrototypeIfNotBuggy = function (o) { - /* global window */ - if (typeof window === 'undefined' || !hasAutomationEqualityBug) { - return equalsConstructorPrototype(o); - } - try { - return equalsConstructorPrototype(o); - } catch (e) { - return false; - } - }; - - keysShim = function keys(object) { - var isObject = object !== null && typeof object === 'object'; - var isFunction = toStr.call(object) === '[object Function]'; - var isArguments = isArgs(object); - var isString = isObject && toStr.call(object) === '[object String]'; - var theKeys = []; - - if (!isObject && !isFunction && !isArguments) { - throw new TypeError('Object.keys called on a non-object'); - } - - var skipProto = hasProtoEnumBug && isFunction; - if (isString && object.length > 0 && !has.call(object, 0)) { - for (var i = 0; i < object.length; ++i) { - theKeys.push(String(i)); - } - } - - if (isArguments && object.length > 0) { - for (var j = 0; j < object.length; ++j) { - theKeys.push(String(j)); - } - } else { - for (var name in object) { - if (!(skipProto && name === 'prototype') && has.call(object, name)) { - theKeys.push(String(name)); - } - } - } - - if (hasDontEnumBug) { - var skipConstructor = equalsConstructorPrototypeIfNotBuggy(object); - - for (var k = 0; k < dontEnums.length; ++k) { - if (!(skipConstructor && dontEnums[k] === 'constructor') && has.call(object, dontEnums[k])) { - theKeys.push(dontEnums[k]); - } - } - } - return theKeys; - }; -} -module.exports = keysShim; - -},{"./isArguments":184}],183:[function(require,module,exports){ -'use strict'; - -var slice = Array.prototype.slice; -var isArgs = require('./isArguments'); - -var origKeys = Object.keys; -var keysShim = origKeys ? function keys(o) { return origKeys(o); } : require('./implementation'); - -var originalKeys = Object.keys; - -keysShim.shim = function shimObjectKeys() { - if (Object.keys) { - var keysWorksWithArguments = (function () { - // Safari 5.0 bug - var args = Object.keys(arguments); - return args && args.length === arguments.length; - }(1, 2)); - if (!keysWorksWithArguments) { - Object.keys = function keys(object) { // eslint-disable-line func-name-matching - if (isArgs(object)) { - return originalKeys(slice.call(object)); - } - return originalKeys(object); - }; - } - } else { - Object.keys = keysShim; - } - return Object.keys || keysShim; -}; - -module.exports = keysShim; - -},{"./implementation":182,"./isArguments":184}],184:[function(require,module,exports){ -'use strict'; - -var toStr = Object.prototype.toString; - -module.exports = function isArguments(value) { - var str = toStr.call(value); - var isArgs = str === '[object Arguments]'; - if (!isArgs) { - isArgs = str !== '[object Array]' && - value !== null && - typeof value === 'object' && - typeof value.length === 'number' && - value.length >= 0 && - toStr.call(value.callee) === '[object Function]'; - } - return isArgs; -}; - -},{}],185:[function(require,module,exports){ -'use strict'; - -// modified from https://github.com/es-shims/es6-shim -var objectKeys = require('object-keys'); -var hasSymbols = require('has-symbols/shams')(); -var callBound = require('call-bind/callBound'); -var toObject = Object; -var $push = callBound('Array.prototype.push'); -var $propIsEnumerable = callBound('Object.prototype.propertyIsEnumerable'); -var originalGetSymbols = hasSymbols ? Object.getOwnPropertySymbols : null; - -// eslint-disable-next-line no-unused-vars -module.exports = function assign(target, source1) { - if (target == null) { throw new TypeError('target must be an object'); } - var to = toObject(target); // step 1 - if (arguments.length === 1) { - return to; // step 2 - } - for (var s = 1; s < arguments.length; ++s) { - var from = toObject(arguments[s]); // step 3.a.i - - // step 3.a.ii: - var keys = objectKeys(from); - var getSymbols = hasSymbols && (Object.getOwnPropertySymbols || originalGetSymbols); - if (getSymbols) { - var syms = getSymbols(from); - for (var j = 0; j < syms.length; ++j) { - var key = syms[j]; - if ($propIsEnumerable(from, key)) { - $push(keys, key); - } - } - } - - // step 3.a.iii: - for (var i = 0; i < keys.length; ++i) { - var nextKey = keys[i]; - if ($propIsEnumerable(from, nextKey)) { // step 3.a.iii.2 - var propValue = from[nextKey]; // step 3.a.iii.2.a - to[nextKey] = propValue; // step 3.a.iii.2.b - } - } - } - - return to; // step 4 -}; - -},{"call-bind/callBound":64,"has-symbols/shams":138,"object-keys":183}],186:[function(require,module,exports){ -'use strict'; - -var implementation = require('./implementation'); - -var lacksProperEnumerationOrder = function () { - if (!Object.assign) { - return false; - } - /* - * v8, specifically in node 4.x, has a bug with incorrect property enumeration order - * note: this does not detect the bug unless there's 20 characters - */ - var str = 'abcdefghijklmnopqrst'; - var letters = str.split(''); - var map = {}; - for (var i = 0; i < letters.length; ++i) { - map[letters[i]] = letters[i]; - } - var obj = Object.assign({}, map); - var actual = ''; - for (var k in obj) { - actual += k; - } - return str !== actual; -}; - -var assignHasPendingExceptions = function () { - if (!Object.assign || !Object.preventExtensions) { - return false; - } - /* - * Firefox 37 still has "pending exception" logic in its Object.assign implementation, - * which is 72% slower than our shim, and Firefox 40's native implementation. - */ - var thrower = Object.preventExtensions({ 1: 2 }); - try { - Object.assign(thrower, 'xy'); - } catch (e) { - return thrower[1] === 'y'; - } - return false; -}; - -module.exports = function getPolyfill() { - if (!Object.assign) { - return implementation; - } - if (lacksProperEnumerationOrder()) { - return implementation; - } - if (assignHasPendingExceptions()) { - return implementation; - } - return Object.assign; -}; - -},{"./implementation":185}],187:[function(require,module,exports){ -module.exports={"2.16.840.1.101.3.4.1.1": "aes-128-ecb", -"2.16.840.1.101.3.4.1.2": "aes-128-cbc", -"2.16.840.1.101.3.4.1.3": "aes-128-ofb", -"2.16.840.1.101.3.4.1.4": "aes-128-cfb", -"2.16.840.1.101.3.4.1.21": "aes-192-ecb", -"2.16.840.1.101.3.4.1.22": "aes-192-cbc", -"2.16.840.1.101.3.4.1.23": "aes-192-ofb", -"2.16.840.1.101.3.4.1.24": "aes-192-cfb", -"2.16.840.1.101.3.4.1.41": "aes-256-ecb", -"2.16.840.1.101.3.4.1.42": "aes-256-cbc", -"2.16.840.1.101.3.4.1.43": "aes-256-ofb", -"2.16.840.1.101.3.4.1.44": "aes-256-cfb" -} -},{}],188:[function(require,module,exports){ -// from https://github.com/indutny/self-signed/blob/gh-pages/lib/asn1.js -// Fedor, you are amazing. - -'use strict'; - -var asn1 = require('asn1.js'); - -exports.certificate = require('./certificate'); - -var RSAPrivateKey = asn1.define('RSAPrivateKey', function () { - this.seq().obj( - this.key('version')['int'](), - this.key('modulus')['int'](), - this.key('publicExponent')['int'](), - this.key('privateExponent')['int'](), - this.key('prime1')['int'](), - this.key('prime2')['int'](), - this.key('exponent1')['int'](), - this.key('exponent2')['int'](), - this.key('coefficient')['int']() - ); -}); -exports.RSAPrivateKey = RSAPrivateKey; - -var RSAPublicKey = asn1.define('RSAPublicKey', function () { - this.seq().obj( - this.key('modulus')['int'](), - this.key('publicExponent')['int']() - ); -}); -exports.RSAPublicKey = RSAPublicKey; - -var AlgorithmIdentifier = asn1.define('AlgorithmIdentifier', function () { - this.seq().obj( - this.key('algorithm').objid(), - this.key('none').null_().optional(), - this.key('curve').objid().optional(), - this.key('params').seq().obj( - this.key('p')['int'](), - this.key('q')['int'](), - this.key('g')['int']() - ).optional() - ); -}); - -var PublicKey = asn1.define('SubjectPublicKeyInfo', function () { - this.seq().obj( - this.key('algorithm').use(AlgorithmIdentifier), - this.key('subjectPublicKey').bitstr() - ); -}); -exports.PublicKey = PublicKey; - -var PrivateKeyInfo = asn1.define('PrivateKeyInfo', function () { - this.seq().obj( - this.key('version')['int'](), - this.key('algorithm').use(AlgorithmIdentifier), - this.key('subjectPrivateKey').octstr() - ); -}); -exports.PrivateKey = PrivateKeyInfo; -var EncryptedPrivateKeyInfo = asn1.define('EncryptedPrivateKeyInfo', function () { - this.seq().obj( - this.key('algorithm').seq().obj( - this.key('id').objid(), - this.key('decrypt').seq().obj( - this.key('kde').seq().obj( - this.key('id').objid(), - this.key('kdeparams').seq().obj( - this.key('salt').octstr(), - this.key('iters')['int']() - ) - ), - this.key('cipher').seq().obj( - this.key('algo').objid(), - this.key('iv').octstr() - ) - ) - ), - this.key('subjectPrivateKey').octstr() - ); -}); - -exports.EncryptedPrivateKey = EncryptedPrivateKeyInfo; - -var DSAPrivateKey = asn1.define('DSAPrivateKey', function () { - this.seq().obj( - this.key('version')['int'](), - this.key('p')['int'](), - this.key('q')['int'](), - this.key('g')['int'](), - this.key('pub_key')['int'](), - this.key('priv_key')['int']() - ); -}); -exports.DSAPrivateKey = DSAPrivateKey; - -exports.DSAparam = asn1.define('DSAparam', function () { - this['int'](); -}); - -var ECParameters = asn1.define('ECParameters', function () { - this.choice({ - namedCurve: this.objid() - }); -}); - -var ECPrivateKey = asn1.define('ECPrivateKey', function () { - this.seq().obj( - this.key('version')['int'](), - this.key('privateKey').octstr(), - this.key('parameters').optional().explicit(0).use(ECParameters), - this.key('publicKey').optional().explicit(1).bitstr() - ); -}); -exports.ECPrivateKey = ECPrivateKey; - -exports.signature = asn1.define('signature', function () { - this.seq().obj( - this.key('r')['int'](), - this.key('s')['int']() - ); -}); - -},{"./certificate":189,"asn1.js":6}],189:[function(require,module,exports){ -// from https://github.com/Rantanen/node-dtls/blob/25a7dc861bda38cfeac93a723500eea4f0ac2e86/Certificate.js -// thanks to @Rantanen - -'use strict'; - -var asn = require('asn1.js'); - -var Time = asn.define('Time', function () { - this.choice({ - utcTime: this.utctime(), - generalTime: this.gentime() - }); -}); - -var AttributeTypeValue = asn.define('AttributeTypeValue', function () { - this.seq().obj( - this.key('type').objid(), - this.key('value').any() - ); -}); - -var AlgorithmIdentifier = asn.define('AlgorithmIdentifier', function () { - this.seq().obj( - this.key('algorithm').objid(), - this.key('parameters').optional(), - this.key('curve').objid().optional() - ); -}); - -var SubjectPublicKeyInfo = asn.define('SubjectPublicKeyInfo', function () { - this.seq().obj( - this.key('algorithm').use(AlgorithmIdentifier), - this.key('subjectPublicKey').bitstr() - ); -}); - -var RelativeDistinguishedName = asn.define('RelativeDistinguishedName', function () { - this.setof(AttributeTypeValue); -}); - -var RDNSequence = asn.define('RDNSequence', function () { - this.seqof(RelativeDistinguishedName); -}); - -var Name = asn.define('Name', function () { - this.choice({ - rdnSequence: this.use(RDNSequence) - }); -}); - -var Validity = asn.define('Validity', function () { - this.seq().obj( - this.key('notBefore').use(Time), - this.key('notAfter').use(Time) - ); -}); - -var Extension = asn.define('Extension', function () { - this.seq().obj( - this.key('extnID').objid(), - this.key('critical').bool().def(false), - this.key('extnValue').octstr() - ); -}); - -var TBSCertificate = asn.define('TBSCertificate', function () { - this.seq().obj( - this.key('version').explicit(0)['int']().optional(), - this.key('serialNumber')['int'](), - this.key('signature').use(AlgorithmIdentifier), - this.key('issuer').use(Name), - this.key('validity').use(Validity), - this.key('subject').use(Name), - this.key('subjectPublicKeyInfo').use(SubjectPublicKeyInfo), - this.key('issuerUniqueID').implicit(1).bitstr().optional(), - this.key('subjectUniqueID').implicit(2).bitstr().optional(), - this.key('extensions').explicit(3).seqof(Extension).optional() - ); -}); - -var X509Certificate = asn.define('X509Certificate', function () { - this.seq().obj( - this.key('tbsCertificate').use(TBSCertificate), - this.key('signatureAlgorithm').use(AlgorithmIdentifier), - this.key('signatureValue').bitstr() - ); -}); - -module.exports = X509Certificate; - -},{"asn1.js":6}],190:[function(require,module,exports){ -'use strict'; - -// adapted from https://github.com/apatil/pemstrip -var findProc = /Proc-Type: 4,ENCRYPTED[\n\r]+DEK-Info: AES-((?:128)|(?:192)|(?:256))-CBC,([0-9A-H]+)[\n\r]+([0-9A-z\n\r+/=]+)[\n\r]+/m; -var startRegex = /^-----BEGIN ((?:.*? KEY)|CERTIFICATE)-----/m; -var fullRegex = /^-----BEGIN ((?:.*? KEY)|CERTIFICATE)-----([0-9A-z\n\r+/=]+)-----END \1-----$/m; -var evp = require('evp_bytestokey'); -var ciphers = require('browserify-aes'); -var Buffer = require('safe-buffer').Buffer; -module.exports = function (okey, password) { - var key = okey.toString(); - var match = key.match(findProc); - var decrypted; - if (!match) { - var match2 = key.match(fullRegex); - decrypted = Buffer.from(match2[2].replace(/[\r\n]/g, ''), 'base64'); - } else { - var suite = 'aes' + match[1]; - var iv = Buffer.from(match[2], 'hex'); - var cipherText = Buffer.from(match[3].replace(/[\r\n]/g, ''), 'base64'); - var cipherKey = evp(password, iv.slice(0, 8), parseInt(match[1], 10)).key; - var out = []; - var cipher = ciphers.createDecipheriv(suite, cipherKey, iv); - out.push(cipher.update(cipherText)); - out.push(cipher['final']()); - decrypted = Buffer.concat(out); - } - var tag = key.match(startRegex)[1]; - return { - tag: tag, - data: decrypted - }; -}; - -},{"browserify-aes":37,"evp_bytestokey":130,"safe-buffer":221}],191:[function(require,module,exports){ -'use strict'; - -var asn1 = require('./asn1'); -var aesid = require('./aesid.json'); -var fixProc = require('./fixProc'); -var ciphers = require('browserify-aes'); -var compat = require('pbkdf2'); -var Buffer = require('safe-buffer').Buffer; - -function decrypt(data, password) { - var salt = data.algorithm.decrypt.kde.kdeparams.salt; - var iters = parseInt(data.algorithm.decrypt.kde.kdeparams.iters.toString(), 10); - var algo = aesid[data.algorithm.decrypt.cipher.algo.join('.')]; - var iv = data.algorithm.decrypt.cipher.iv; - var cipherText = data.subjectPrivateKey; - var keylen = parseInt(algo.split('-')[1], 10) / 8; - var key = compat.pbkdf2Sync(password, salt, iters, keylen, 'sha1'); - var cipher = ciphers.createDecipheriv(algo, key, iv); - var out = []; - out.push(cipher.update(cipherText)); - out.push(cipher['final']()); - return Buffer.concat(out); -} - -function parseKeys(buffer) { - var password; - if (typeof buffer === 'object' && !Buffer.isBuffer(buffer)) { - password = buffer.passphrase; - buffer = buffer.key; - } - if (typeof buffer === 'string') { - buffer = Buffer.from(buffer); - } - - var stripped = fixProc(buffer, password); - - var type = stripped.tag; - var data = stripped.data; - var subtype, ndata; - switch (type) { - case 'CERTIFICATE': - ndata = asn1.certificate.decode(data, 'der').tbsCertificate.subjectPublicKeyInfo; - // falls through - case 'PUBLIC KEY': - if (!ndata) { - ndata = asn1.PublicKey.decode(data, 'der'); - } - subtype = ndata.algorithm.algorithm.join('.'); - switch (subtype) { - case '1.2.840.113549.1.1.1': - return asn1.RSAPublicKey.decode(ndata.subjectPublicKey.data, 'der'); - case '1.2.840.10045.2.1': - ndata.subjectPrivateKey = ndata.subjectPublicKey; - return { - type: 'ec', - data: ndata - }; - case '1.2.840.10040.4.1': - ndata.algorithm.params.pub_key = asn1.DSAparam.decode(ndata.subjectPublicKey.data, 'der'); - return { - type: 'dsa', - data: ndata.algorithm.params - }; - default: throw new Error('unknown key id ' + subtype); - } - // throw new Error('unknown key type ' + type) - case 'ENCRYPTED PRIVATE KEY': - data = asn1.EncryptedPrivateKey.decode(data, 'der'); - data = decrypt(data, password); - // falls through - case 'PRIVATE KEY': - ndata = asn1.PrivateKey.decode(data, 'der'); - subtype = ndata.algorithm.algorithm.join('.'); - switch (subtype) { - case '1.2.840.113549.1.1.1': - return asn1.RSAPrivateKey.decode(ndata.subjectPrivateKey, 'der'); - case '1.2.840.10045.2.1': - return { - curve: ndata.algorithm.curve, - privateKey: asn1.ECPrivateKey.decode(ndata.subjectPrivateKey, 'der').privateKey - }; - case '1.2.840.10040.4.1': - ndata.algorithm.params.priv_key = asn1.DSAparam.decode(ndata.subjectPrivateKey, 'der'); - return { - type: 'dsa', - params: ndata.algorithm.params - }; - default: throw new Error('unknown key id ' + subtype); - } - // throw new Error('unknown key type ' + type) - case 'RSA PUBLIC KEY': - return asn1.RSAPublicKey.decode(data, 'der'); - case 'RSA PRIVATE KEY': - return asn1.RSAPrivateKey.decode(data, 'der'); - case 'DSA PRIVATE KEY': - return { - type: 'dsa', - params: asn1.DSAPrivateKey.decode(data, 'der') - }; - case 'EC PRIVATE KEY': - data = asn1.ECPrivateKey.decode(data, 'der'); - return { - curve: data.parameters.value, - privateKey: data.privateKey - }; - default: throw new Error('unknown key type ' + type); - } -} -parseKeys.signature = asn1.signature; - -module.exports = parseKeys; - -},{"./aesid.json":187,"./asn1":188,"./fixProc":190,"browserify-aes":37,"pbkdf2":192,"safe-buffer":221}],192:[function(require,module,exports){ -exports.pbkdf2 = require('./lib/async') -exports.pbkdf2Sync = require('./lib/sync') - -},{"./lib/async":193,"./lib/sync":196}],193:[function(require,module,exports){ -(function (global){(function (){ -var Buffer = require('safe-buffer').Buffer - -var checkParameters = require('./precondition') -var defaultEncoding = require('./default-encoding') -var sync = require('./sync') -var toBuffer = require('./to-buffer') - -var ZERO_BUF -var subtle = global.crypto && global.crypto.subtle -var toBrowser = { - sha: 'SHA-1', - 'sha-1': 'SHA-1', - sha1: 'SHA-1', - sha256: 'SHA-256', - 'sha-256': 'SHA-256', - sha384: 'SHA-384', - 'sha-384': 'SHA-384', - 'sha-512': 'SHA-512', - sha512: 'SHA-512' -} -var checks = [] -function checkNative (algo) { - if (global.process && !global.process.browser) { - return Promise.resolve(false) - } - if (!subtle || !subtle.importKey || !subtle.deriveBits) { - return Promise.resolve(false) - } - if (checks[algo] !== undefined) { - return checks[algo] - } - ZERO_BUF = ZERO_BUF || Buffer.alloc(8) - var prom = browserPbkdf2(ZERO_BUF, ZERO_BUF, 10, 128, algo) - .then(function () { - return true - }).catch(function () { - return false - }) - checks[algo] = prom - return prom -} -var nextTick -function getNextTick () { - if (nextTick) { - return nextTick - } - if (global.process && global.process.nextTick) { - nextTick = global.process.nextTick - } else if (global.queueMicrotask) { - nextTick = global.queueMicrotask - } else if (global.setImmediate) { - nextTick = global.setImmediate - } else { - nextTick = global.setTimeout - } - return nextTick -} -function browserPbkdf2 (password, salt, iterations, length, algo) { - return subtle.importKey( - 'raw', password, { name: 'PBKDF2' }, false, ['deriveBits'] - ).then(function (key) { - return subtle.deriveBits({ - name: 'PBKDF2', - salt: salt, - iterations: iterations, - hash: { - name: algo - } - }, key, length << 3) - }).then(function (res) { - return Buffer.from(res) - }) -} - -function resolvePromise (promise, callback) { - promise.then(function (out) { - getNextTick()(function () { - callback(null, out) - }) - }, function (e) { - getNextTick()(function () { - callback(e) - }) - }) -} -module.exports = function (password, salt, iterations, keylen, digest, callback) { - if (typeof digest === 'function') { - callback = digest - digest = undefined - } - - digest = digest || 'sha1' - var algo = toBrowser[digest.toLowerCase()] - - if (!algo || typeof global.Promise !== 'function') { - getNextTick()(function () { - var out - try { - out = sync(password, salt, iterations, keylen, digest) - } catch (e) { - return callback(e) - } - callback(null, out) - }) - return - } - - checkParameters(iterations, keylen) - password = toBuffer(password, defaultEncoding, 'Password') - salt = toBuffer(salt, defaultEncoding, 'Salt') - if (typeof callback !== 'function') throw new Error('No callback provided to pbkdf2') - - resolvePromise(checkNative(algo).then(function (resp) { - if (resp) return browserPbkdf2(password, salt, iterations, keylen, algo) - - return sync(password, salt, iterations, keylen, digest) - }), callback) -} - -}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./default-encoding":194,"./precondition":195,"./sync":196,"./to-buffer":197,"safe-buffer":221}],194:[function(require,module,exports){ -(function (process,global){(function (){ -var defaultEncoding -/* istanbul ignore next */ -if (global.process && global.process.browser) { - defaultEncoding = 'utf-8' -} else if (global.process && global.process.version) { - var pVersionMajor = parseInt(process.version.split('.')[0].slice(1), 10) - - defaultEncoding = pVersionMajor >= 6 ? 'utf-8' : 'binary' -} else { - defaultEncoding = 'utf-8' -} -module.exports = defaultEncoding - -}).call(this)}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"_process":199}],195:[function(require,module,exports){ -var MAX_ALLOC = Math.pow(2, 30) - 1 // default in iojs - -module.exports = function (iterations, keylen) { - if (typeof iterations !== 'number') { - throw new TypeError('Iterations not a number') - } - - if (iterations < 0) { - throw new TypeError('Bad iterations') - } - - if (typeof keylen !== 'number') { - throw new TypeError('Key length not a number') - } - - if (keylen < 0 || keylen > MAX_ALLOC || keylen !== keylen) { /* eslint no-self-compare: 0 */ - throw new TypeError('Bad key length') - } -} - -},{}],196:[function(require,module,exports){ -var md5 = require('create-hash/md5') -var RIPEMD160 = require('ripemd160') -var sha = require('sha.js') -var Buffer = require('safe-buffer').Buffer - -var checkParameters = require('./precondition') -var defaultEncoding = require('./default-encoding') -var toBuffer = require('./to-buffer') - -var ZEROS = Buffer.alloc(128) -var sizes = { - md5: 16, - sha1: 20, - sha224: 28, - sha256: 32, - sha384: 48, - sha512: 64, - rmd160: 20, - ripemd160: 20 -} - -function Hmac (alg, key, saltLen) { - var hash = getDigest(alg) - var blocksize = (alg === 'sha512' || alg === 'sha384') ? 128 : 64 - - if (key.length > blocksize) { - key = hash(key) - } else if (key.length < blocksize) { - key = Buffer.concat([key, ZEROS], blocksize) - } - - var ipad = Buffer.allocUnsafe(blocksize + sizes[alg]) - var opad = Buffer.allocUnsafe(blocksize + sizes[alg]) - for (var i = 0; i < blocksize; i++) { - ipad[i] = key[i] ^ 0x36 - opad[i] = key[i] ^ 0x5C - } - - var ipad1 = Buffer.allocUnsafe(blocksize + saltLen + 4) - ipad.copy(ipad1, 0, 0, blocksize) - this.ipad1 = ipad1 - this.ipad2 = ipad - this.opad = opad - this.alg = alg - this.blocksize = blocksize - this.hash = hash - this.size = sizes[alg] -} - -Hmac.prototype.run = function (data, ipad) { - data.copy(ipad, this.blocksize) - var h = this.hash(ipad) - h.copy(this.opad, this.blocksize) - return this.hash(this.opad) -} - -function getDigest (alg) { - function shaFunc (data) { - return sha(alg).update(data).digest() - } - function rmd160Func (data) { - return new RIPEMD160().update(data).digest() - } - - if (alg === 'rmd160' || alg === 'ripemd160') return rmd160Func - if (alg === 'md5') return md5 - return shaFunc -} - -function pbkdf2 (password, salt, iterations, keylen, digest) { - checkParameters(iterations, keylen) - password = toBuffer(password, defaultEncoding, 'Password') - salt = toBuffer(salt, defaultEncoding, 'Salt') - - digest = digest || 'sha1' - - var hmac = new Hmac(digest, password, salt.length) - - var DK = Buffer.allocUnsafe(keylen) - var block1 = Buffer.allocUnsafe(salt.length + 4) - salt.copy(block1, 0, 0, salt.length) - - var destPos = 0 - var hLen = sizes[digest] - var l = Math.ceil(keylen / hLen) - - for (var i = 1; i <= l; i++) { - block1.writeUInt32BE(i, salt.length) - - var T = hmac.run(block1, hmac.ipad1) - var U = T - - for (var j = 1; j < iterations; j++) { - U = hmac.run(U, hmac.ipad2) - for (var k = 0; k < hLen; k++) T[k] ^= U[k] - } - - T.copy(DK, destPos) - destPos += hLen - } - - return DK -} - -module.exports = pbkdf2 - -},{"./default-encoding":194,"./precondition":195,"./to-buffer":197,"create-hash/md5":88,"ripemd160":220,"safe-buffer":221,"sha.js":225}],197:[function(require,module,exports){ -var Buffer = require('safe-buffer').Buffer - -module.exports = function (thing, encoding, name) { - if (Buffer.isBuffer(thing)) { - return thing - } else if (typeof thing === 'string') { - return Buffer.from(thing, encoding) - } else if (ArrayBuffer.isView(thing)) { - return Buffer.from(thing.buffer) - } else { - throw new TypeError(name + ' must be a string, a Buffer, a typed array or a DataView') - } -} - -},{"safe-buffer":221}],198:[function(require,module,exports){ -(function (process){(function (){ -'use strict'; - -if (typeof process === 'undefined' || - !process.version || - process.version.indexOf('v0.') === 0 || - process.version.indexOf('v1.') === 0 && process.version.indexOf('v1.8.') !== 0) { - module.exports = { nextTick: nextTick }; -} else { - module.exports = process -} - -function nextTick(fn, arg1, arg2, arg3) { - if (typeof fn !== 'function') { - throw new TypeError('"callback" argument must be a function'); - } - var len = arguments.length; - var args, i; - switch (len) { - case 0: - case 1: - return process.nextTick(fn); - case 2: - return process.nextTick(function afterTickOne() { - fn.call(null, arg1); - }); - case 3: - return process.nextTick(function afterTickTwo() { - fn.call(null, arg1, arg2); - }); - case 4: - return process.nextTick(function afterTickThree() { - fn.call(null, arg1, arg2, arg3); - }); - default: - args = new Array(len - 1); - i = 0; - while (i < args.length) { - args[i++] = arguments[i]; - } - return process.nextTick(function afterTick() { - fn.apply(null, args); - }); - } -} - - -}).call(this)}).call(this,require('_process')) -},{"_process":199}],199:[function(require,module,exports){ -// shim for using process in browser -var process = module.exports = {}; - -// cached from whatever global is present so that test runners that stub it -// don't break things. But we need to wrap it in a try catch in case it is -// wrapped in strict mode code which doesn't define any globals. It's inside a -// function because try/catches deoptimize in certain engines. - -var cachedSetTimeout; -var cachedClearTimeout; - -function defaultSetTimout() { - throw new Error('setTimeout has not been defined'); -} -function defaultClearTimeout () { - throw new Error('clearTimeout has not been defined'); -} -(function () { - try { - if (typeof setTimeout === 'function') { - cachedSetTimeout = setTimeout; - } else { - cachedSetTimeout = defaultSetTimout; - } - } catch (e) { - cachedSetTimeout = defaultSetTimout; - } - try { - if (typeof clearTimeout === 'function') { - cachedClearTimeout = clearTimeout; - } else { - cachedClearTimeout = defaultClearTimeout; - } - } catch (e) { - cachedClearTimeout = defaultClearTimeout; - } -} ()) -function runTimeout(fun) { - if (cachedSetTimeout === setTimeout) { - //normal enviroments in sane situations - return setTimeout(fun, 0); - } - // if setTimeout wasn't available but was latter defined - if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { - cachedSetTimeout = setTimeout; - return setTimeout(fun, 0); - } - try { - // when when somebody has screwed with setTimeout but no I.E. maddness - return cachedSetTimeout(fun, 0); - } catch(e){ - try { - // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally - return cachedSetTimeout.call(null, fun, 0); - } catch(e){ - // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error - return cachedSetTimeout.call(this, fun, 0); - } - } - - -} -function runClearTimeout(marker) { - if (cachedClearTimeout === clearTimeout) { - //normal enviroments in sane situations - return clearTimeout(marker); - } - // if clearTimeout wasn't available but was latter defined - if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { - cachedClearTimeout = clearTimeout; - return clearTimeout(marker); - } - try { - // when when somebody has screwed with setTimeout but no I.E. maddness - return cachedClearTimeout(marker); - } catch (e){ - try { - // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally - return cachedClearTimeout.call(null, marker); - } catch (e){ - // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. - // Some versions of I.E. have different rules for clearTimeout vs setTimeout - return cachedClearTimeout.call(this, marker); - } - } - - - -} -var queue = []; -var draining = false; -var currentQueue; -var queueIndex = -1; - -function cleanUpNextTick() { - if (!draining || !currentQueue) { - return; - } - draining = false; - if (currentQueue.length) { - queue = currentQueue.concat(queue); - } else { - queueIndex = -1; - } - if (queue.length) { - drainQueue(); - } -} - -function drainQueue() { - if (draining) { - return; - } - var timeout = runTimeout(cleanUpNextTick); - draining = true; - - var len = queue.length; - while(len) { - currentQueue = queue; - queue = []; - while (++queueIndex < len) { - if (currentQueue) { - currentQueue[queueIndex].run(); - } - } - queueIndex = -1; - len = queue.length; - } - currentQueue = null; - draining = false; - runClearTimeout(timeout); -} - -process.nextTick = function (fun) { - var args = new Array(arguments.length - 1); - if (arguments.length > 1) { - for (var i = 1; i < arguments.length; i++) { - args[i - 1] = arguments[i]; - } - } - queue.push(new Item(fun, args)); - if (queue.length === 1 && !draining) { - runTimeout(drainQueue); - } -}; - -// v8 likes predictible objects -function Item(fun, array) { - this.fun = fun; - this.array = array; -} -Item.prototype.run = function () { - this.fun.apply(null, this.array); -}; -process.title = 'browser'; -process.browser = true; -process.env = {}; -process.argv = []; -process.version = ''; // empty string to avoid regexp issues -process.versions = {}; - -function noop() {} - -process.on = noop; -process.addListener = noop; -process.once = noop; -process.off = noop; -process.removeListener = noop; -process.removeAllListeners = noop; -process.emit = noop; -process.prependListener = noop; -process.prependOnceListener = noop; - -process.listeners = function (name) { return [] } - -process.binding = function (name) { - throw new Error('process.binding is not supported'); -}; - -process.cwd = function () { return '/' }; -process.chdir = function (dir) { - throw new Error('process.chdir is not supported'); -}; -process.umask = function() { return 0; }; - -},{}],200:[function(require,module,exports){ -exports.publicEncrypt = require('./publicEncrypt') -exports.privateDecrypt = require('./privateDecrypt') - -exports.privateEncrypt = function privateEncrypt (key, buf) { - return exports.publicEncrypt(key, buf, true) -} - -exports.publicDecrypt = function publicDecrypt (key, buf) { - return exports.privateDecrypt(key, buf, true) -} - -},{"./privateDecrypt":203,"./publicEncrypt":204}],201:[function(require,module,exports){ -var createHash = require('create-hash') -var Buffer = require('safe-buffer').Buffer - -module.exports = function (seed, len) { - var t = Buffer.alloc(0) - var i = 0 - var c - while (t.length < len) { - c = i2ops(i++) - t = Buffer.concat([t, createHash('sha1').update(seed).update(c).digest()]) - } - return t.slice(0, len) -} - -function i2ops (c) { - var out = Buffer.allocUnsafe(4) - out.writeUInt32BE(c, 0) - return out -} - -},{"create-hash":87,"safe-buffer":221}],202:[function(require,module,exports){ -arguments[4][20][0].apply(exports,arguments) -},{"buffer":34,"dup":20}],203:[function(require,module,exports){ -var parseKeys = require('parse-asn1') -var mgf = require('./mgf') -var xor = require('./xor') -var BN = require('bn.js') -var crt = require('browserify-rsa') -var createHash = require('create-hash') -var withPublic = require('./withPublic') -var Buffer = require('safe-buffer').Buffer - -module.exports = function privateDecrypt (privateKey, enc, reverse) { - var padding - if (privateKey.padding) { - padding = privateKey.padding - } else if (reverse) { - padding = 1 - } else { - padding = 4 - } - - var key = parseKeys(privateKey) - var k = key.modulus.byteLength() - if (enc.length > k || new BN(enc).cmp(key.modulus) >= 0) { - throw new Error('decryption error') - } - var msg - if (reverse) { - msg = withPublic(new BN(enc), key) - } else { - msg = crt(enc, key) - } - var zBuffer = Buffer.alloc(k - msg.length) - msg = Buffer.concat([zBuffer, msg], k) - if (padding === 4) { - return oaep(key, msg) - } else if (padding === 1) { - return pkcs1(key, msg, reverse) - } else if (padding === 3) { - return msg - } else { - throw new Error('unknown padding') - } -} - -function oaep (key, msg) { - var k = key.modulus.byteLength() - var iHash = createHash('sha1').update(Buffer.alloc(0)).digest() - var hLen = iHash.length - if (msg[0] !== 0) { - throw new Error('decryption error') - } - var maskedSeed = msg.slice(1, hLen + 1) - var maskedDb = msg.slice(hLen + 1) - var seed = xor(maskedSeed, mgf(maskedDb, hLen)) - var db = xor(maskedDb, mgf(seed, k - hLen - 1)) - if (compare(iHash, db.slice(0, hLen))) { - throw new Error('decryption error') - } - var i = hLen - while (db[i] === 0) { - i++ - } - if (db[i++] !== 1) { - throw new Error('decryption error') - } - return db.slice(i) -} - -function pkcs1 (key, msg, reverse) { - var p1 = msg.slice(0, 2) - var i = 2 - var status = 0 - while (msg[i++] !== 0) { - if (i >= msg.length) { - status++ - break - } - } - var ps = msg.slice(2, i - 1) - - if ((p1.toString('hex') !== '0002' && !reverse) || (p1.toString('hex') !== '0001' && reverse)) { - status++ - } - if (ps.length < 8) { - status++ - } - if (status) { - throw new Error('decryption error') - } - return msg.slice(i) -} -function compare (a, b) { - a = Buffer.from(a) - b = Buffer.from(b) - var dif = 0 - var len = a.length - if (a.length !== b.length) { - dif++ - len = Math.min(a.length, b.length) - } - var i = -1 - while (++i < len) { - dif += (a[i] ^ b[i]) - } - return dif -} - -},{"./mgf":201,"./withPublic":205,"./xor":206,"bn.js":202,"browserify-rsa":55,"create-hash":87,"parse-asn1":191,"safe-buffer":221}],204:[function(require,module,exports){ -var parseKeys = require('parse-asn1') -var randomBytes = require('randombytes') -var createHash = require('create-hash') -var mgf = require('./mgf') -var xor = require('./xor') -var BN = require('bn.js') -var withPublic = require('./withPublic') -var crt = require('browserify-rsa') -var Buffer = require('safe-buffer').Buffer - -module.exports = function publicEncrypt (publicKey, msg, reverse) { - var padding - if (publicKey.padding) { - padding = publicKey.padding - } else if (reverse) { - padding = 1 - } else { - padding = 4 - } - var key = parseKeys(publicKey) - var paddedMsg - if (padding === 4) { - paddedMsg = oaep(key, msg) - } else if (padding === 1) { - paddedMsg = pkcs1(key, msg, reverse) - } else if (padding === 3) { - paddedMsg = new BN(msg) - if (paddedMsg.cmp(key.modulus) >= 0) { - throw new Error('data too long for modulus') - } - } else { - throw new Error('unknown padding') - } - if (reverse) { - return crt(paddedMsg, key) - } else { - return withPublic(paddedMsg, key) - } -} - -function oaep (key, msg) { - var k = key.modulus.byteLength() - var mLen = msg.length - var iHash = createHash('sha1').update(Buffer.alloc(0)).digest() - var hLen = iHash.length - var hLen2 = 2 * hLen - if (mLen > k - hLen2 - 2) { - throw new Error('message too long') - } - var ps = Buffer.alloc(k - mLen - hLen2 - 2) - var dblen = k - hLen - 1 - var seed = randomBytes(hLen) - var maskedDb = xor(Buffer.concat([iHash, ps, Buffer.alloc(1, 1), msg], dblen), mgf(seed, dblen)) - var maskedSeed = xor(seed, mgf(maskedDb, hLen)) - return new BN(Buffer.concat([Buffer.alloc(1), maskedSeed, maskedDb], k)) -} -function pkcs1 (key, msg, reverse) { - var mLen = msg.length - var k = key.modulus.byteLength() - if (mLen > k - 11) { - throw new Error('message too long') - } - var ps - if (reverse) { - ps = Buffer.alloc(k - mLen - 3, 0xff) - } else { - ps = nonZero(k - mLen - 3) - } - return new BN(Buffer.concat([Buffer.from([0, reverse ? 1 : 2]), ps, Buffer.alloc(1), msg], k)) -} -function nonZero (len) { - var out = Buffer.allocUnsafe(len) - var i = 0 - var cache = randomBytes(len * 2) - var cur = 0 - var num - while (i < len) { - if (cur === cache.length) { - cache = randomBytes(len * 2) - cur = 0 - } - num = cache[cur++] - if (num) { - out[i++] = num - } - } - return out -} - -},{"./mgf":201,"./withPublic":205,"./xor":206,"bn.js":202,"browserify-rsa":55,"create-hash":87,"parse-asn1":191,"randombytes":207,"safe-buffer":221}],205:[function(require,module,exports){ -var BN = require('bn.js') -var Buffer = require('safe-buffer').Buffer - -function withPublic (paddedMsg, key) { - return Buffer.from(paddedMsg - .toRed(BN.mont(key.modulus)) - .redPow(new BN(key.publicExponent)) - .fromRed() - .toArray()) -} - -module.exports = withPublic - -},{"bn.js":202,"safe-buffer":221}],206:[function(require,module,exports){ -module.exports = function xor (a, b) { - var len = a.length - var i = -1 - while (++i < len) { - a[i] ^= b[i] - } - return a -} - -},{}],207:[function(require,module,exports){ -(function (process,global){(function (){ -'use strict' - -// limit of Crypto.getRandomValues() -// https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues -var MAX_BYTES = 65536 - -// Node supports requesting up to this number of bytes -// https://github.com/nodejs/node/blob/master/lib/internal/crypto/random.js#L48 -var MAX_UINT32 = 4294967295 - -function oldBrowser () { - throw new Error('Secure random number generation is not supported by this browser.\nUse Chrome, Firefox or Internet Explorer 11') -} - -var Buffer = require('safe-buffer').Buffer -var crypto = global.crypto || global.msCrypto - -if (crypto && crypto.getRandomValues) { - module.exports = randomBytes -} else { - module.exports = oldBrowser -} - -function randomBytes (size, cb) { - // phantomjs needs to throw - if (size > MAX_UINT32) throw new RangeError('requested too many random bytes') - - var bytes = Buffer.allocUnsafe(size) - - if (size > 0) { // getRandomValues fails on IE if size == 0 - if (size > MAX_BYTES) { // this is the max bytes crypto.getRandomValues - // can do at once see https://developer.mozilla.org/en-US/docs/Web/API/window.crypto.getRandomValues - for (var generated = 0; generated < size; generated += MAX_BYTES) { - // buffer.slice automatically checks if the end is past the end of - // the buffer so we don't have to here - crypto.getRandomValues(bytes.slice(generated, generated + MAX_BYTES)) - } - } else { - crypto.getRandomValues(bytes) - } - } - - if (typeof cb === 'function') { - return process.nextTick(function () { - cb(null, bytes) - }) - } - - return bytes -} - -}).call(this)}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"_process":199,"safe-buffer":221}],208:[function(require,module,exports){ -(function (process,global){(function (){ -'use strict' - -function oldBrowser () { - throw new Error('secure random number generation not supported by this browser\nuse chrome, FireFox or Internet Explorer 11') -} -var safeBuffer = require('safe-buffer') -var randombytes = require('randombytes') -var Buffer = safeBuffer.Buffer -var kBufferMaxLength = safeBuffer.kMaxLength -var crypto = global.crypto || global.msCrypto -var kMaxUint32 = Math.pow(2, 32) - 1 -function assertOffset (offset, length) { - if (typeof offset !== 'number' || offset !== offset) { // eslint-disable-line no-self-compare - throw new TypeError('offset must be a number') - } - - if (offset > kMaxUint32 || offset < 0) { - throw new TypeError('offset must be a uint32') - } - - if (offset > kBufferMaxLength || offset > length) { - throw new RangeError('offset out of range') - } -} - -function assertSize (size, offset, length) { - if (typeof size !== 'number' || size !== size) { // eslint-disable-line no-self-compare - throw new TypeError('size must be a number') - } - - if (size > kMaxUint32 || size < 0) { - throw new TypeError('size must be a uint32') - } - - if (size + offset > length || size > kBufferMaxLength) { - throw new RangeError('buffer too small') - } -} -if ((crypto && crypto.getRandomValues) || !process.browser) { - exports.randomFill = randomFill - exports.randomFillSync = randomFillSync -} else { - exports.randomFill = oldBrowser - exports.randomFillSync = oldBrowser -} -function randomFill (buf, offset, size, cb) { - if (!Buffer.isBuffer(buf) && !(buf instanceof global.Uint8Array)) { - throw new TypeError('"buf" argument must be a Buffer or Uint8Array') - } - - if (typeof offset === 'function') { - cb = offset - offset = 0 - size = buf.length - } else if (typeof size === 'function') { - cb = size - size = buf.length - offset - } else if (typeof cb !== 'function') { - throw new TypeError('"cb" argument must be a function') - } - assertOffset(offset, buf.length) - assertSize(size, offset, buf.length) - return actualFill(buf, offset, size, cb) -} - -function actualFill (buf, offset, size, cb) { - if (process.browser) { - var ourBuf = buf.buffer - var uint = new Uint8Array(ourBuf, offset, size) - crypto.getRandomValues(uint) - if (cb) { - process.nextTick(function () { - cb(null, buf) - }) - return - } - return buf - } - if (cb) { - randombytes(size, function (err, bytes) { - if (err) { - return cb(err) - } - bytes.copy(buf, offset) - cb(null, buf) - }) - return - } - var bytes = randombytes(size) - bytes.copy(buf, offset) - return buf -} -function randomFillSync (buf, offset, size) { - if (typeof offset === 'undefined') { - offset = 0 - } - if (!Buffer.isBuffer(buf) && !(buf instanceof global.Uint8Array)) { - throw new TypeError('"buf" argument must be a Buffer or Uint8Array') - } - - assertOffset(offset, buf.length) - - if (size === undefined) size = buf.length - offset - - assertSize(size, offset, buf.length) - - return actualFill(buf, offset, size) -} - -}).call(this)}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"_process":199,"randombytes":207,"safe-buffer":221}],209:[function(require,module,exports){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// a duplex stream is just a stream that is both readable and writable. -// Since JS doesn't have multiple prototypal inheritance, this class -// prototypally inherits from Readable, and then parasitically from -// Writable. - -'use strict'; - -/**/ - -var pna = require('process-nextick-args'); -/**/ - -/**/ -var objectKeys = Object.keys || function (obj) { - var keys = []; - for (var key in obj) { - keys.push(key); - }return keys; -}; -/**/ - -module.exports = Duplex; - -/**/ -var util = Object.create(require('core-util-is')); -util.inherits = require('inherits'); -/**/ - -var Readable = require('./_stream_readable'); -var Writable = require('./_stream_writable'); - -util.inherits(Duplex, Readable); - -{ - // avoid scope creep, the keys array can then be collected - var keys = objectKeys(Writable.prototype); - for (var v = 0; v < keys.length; v++) { - var method = keys[v]; - if (!Duplex.prototype[method]) Duplex.prototype[method] = Writable.prototype[method]; - } -} - -function Duplex(options) { - if (!(this instanceof Duplex)) return new Duplex(options); - - Readable.call(this, options); - Writable.call(this, options); - - if (options && options.readable === false) this.readable = false; - - if (options && options.writable === false) this.writable = false; - - this.allowHalfOpen = true; - if (options && options.allowHalfOpen === false) this.allowHalfOpen = false; - - this.once('end', onend); -} - -Object.defineProperty(Duplex.prototype, 'writableHighWaterMark', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function () { - return this._writableState.highWaterMark; - } -}); - -// the no-half-open enforcer -function onend() { - // if we allow half-open state, or if the writable side ended, - // then we're ok. - if (this.allowHalfOpen || this._writableState.ended) return; - - // no more data can be written. - // But allow more writes to happen in this tick. - pna.nextTick(onEndNT, this); -} - -function onEndNT(self) { - self.end(); -} - -Object.defineProperty(Duplex.prototype, 'destroyed', { - get: function () { - if (this._readableState === undefined || this._writableState === undefined) { - return false; - } - return this._readableState.destroyed && this._writableState.destroyed; - }, - set: function (value) { - // we ignore the value if the stream - // has not been initialized yet - if (this._readableState === undefined || this._writableState === undefined) { - return; - } - - // backward compatibility, the user is explicitly - // managing destroyed - this._readableState.destroyed = value; - this._writableState.destroyed = value; - } -}); - -Duplex.prototype._destroy = function (err, cb) { - this.push(null); - this.end(); - - pna.nextTick(cb, err); -}; -},{"./_stream_readable":211,"./_stream_writable":213,"core-util-is":79,"inherits":155,"process-nextick-args":198}],210:[function(require,module,exports){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// a passthrough stream. -// basically just the most minimal sort of Transform stream. -// Every written chunk gets output as-is. - -'use strict'; - -module.exports = PassThrough; - -var Transform = require('./_stream_transform'); - -/**/ -var util = Object.create(require('core-util-is')); -util.inherits = require('inherits'); -/**/ - -util.inherits(PassThrough, Transform); - -function PassThrough(options) { - if (!(this instanceof PassThrough)) return new PassThrough(options); - - Transform.call(this, options); -} - -PassThrough.prototype._transform = function (chunk, encoding, cb) { - cb(null, chunk); -}; -},{"./_stream_transform":212,"core-util-is":79,"inherits":155}],211:[function(require,module,exports){ -(function (process,global){(function (){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -'use strict'; - -/**/ - -var pna = require('process-nextick-args'); -/**/ - -module.exports = Readable; - -/**/ -var isArray = require('isarray'); -/**/ - -/**/ -var Duplex; -/**/ - -Readable.ReadableState = ReadableState; - -/**/ -var EE = require('events').EventEmitter; - -var EElistenerCount = function (emitter, type) { - return emitter.listeners(type).length; -}; -/**/ - -/**/ -var Stream = require('./internal/streams/stream'); -/**/ - -/**/ - -var Buffer = require('safe-buffer').Buffer; -var OurUint8Array = (typeof global !== 'undefined' ? global : typeof window !== 'undefined' ? window : typeof self !== 'undefined' ? self : {}).Uint8Array || function () {}; -function _uint8ArrayToBuffer(chunk) { - return Buffer.from(chunk); -} -function _isUint8Array(obj) { - return Buffer.isBuffer(obj) || obj instanceof OurUint8Array; -} - -/**/ - -/**/ -var util = Object.create(require('core-util-is')); -util.inherits = require('inherits'); -/**/ - -/**/ -var debugUtil = require('util'); -var debug = void 0; -if (debugUtil && debugUtil.debuglog) { - debug = debugUtil.debuglog('stream'); -} else { - debug = function () {}; -} -/**/ - -var BufferList = require('./internal/streams/BufferList'); -var destroyImpl = require('./internal/streams/destroy'); -var StringDecoder; - -util.inherits(Readable, Stream); - -var kProxyEvents = ['error', 'close', 'destroy', 'pause', 'resume']; - -function prependListener(emitter, event, fn) { - // Sadly this is not cacheable as some libraries bundle their own - // event emitter implementation with them. - if (typeof emitter.prependListener === 'function') return emitter.prependListener(event, fn); - - // This is a hack to make sure that our error handler is attached before any - // userland ones. NEVER DO THIS. This is here only because this code needs - // to continue to work with older versions of Node.js that do not include - // the prependListener() method. The goal is to eventually remove this hack. - if (!emitter._events || !emitter._events[event]) emitter.on(event, fn);else if (isArray(emitter._events[event])) emitter._events[event].unshift(fn);else emitter._events[event] = [fn, emitter._events[event]]; -} - -function ReadableState(options, stream) { - Duplex = Duplex || require('./_stream_duplex'); - - options = options || {}; - - // Duplex streams are both readable and writable, but share - // the same options object. - // However, some cases require setting options to different - // values for the readable and the writable sides of the duplex stream. - // These options can be provided separately as readableXXX and writableXXX. - var isDuplex = stream instanceof Duplex; - - // object stream flag. Used to make read(n) ignore n and to - // make all the buffer merging and length checks go away - this.objectMode = !!options.objectMode; - - if (isDuplex) this.objectMode = this.objectMode || !!options.readableObjectMode; - - // the point at which it stops calling _read() to fill the buffer - // Note: 0 is a valid value, means "don't call _read preemptively ever" - var hwm = options.highWaterMark; - var readableHwm = options.readableHighWaterMark; - var defaultHwm = this.objectMode ? 16 : 16 * 1024; - - if (hwm || hwm === 0) this.highWaterMark = hwm;else if (isDuplex && (readableHwm || readableHwm === 0)) this.highWaterMark = readableHwm;else this.highWaterMark = defaultHwm; - - // cast to ints. - this.highWaterMark = Math.floor(this.highWaterMark); - - // A linked list is used to store data chunks instead of an array because the - // linked list can remove elements from the beginning faster than - // array.shift() - this.buffer = new BufferList(); - this.length = 0; - this.pipes = null; - this.pipesCount = 0; - this.flowing = null; - this.ended = false; - this.endEmitted = false; - this.reading = false; - - // a flag to be able to tell if the event 'readable'/'data' is emitted - // immediately, or on a later tick. We set this to true at first, because - // any actions that shouldn't happen until "later" should generally also - // not happen before the first read call. - this.sync = true; - - // whenever we return null, then we set a flag to say - // that we're awaiting a 'readable' event emission. - this.needReadable = false; - this.emittedReadable = false; - this.readableListening = false; - this.resumeScheduled = false; - - // has it been destroyed - this.destroyed = false; - - // Crypto is kind of old and crusty. Historically, its default string - // encoding is 'binary' so we have to make this configurable. - // Everything else in the universe uses 'utf8', though. - this.defaultEncoding = options.defaultEncoding || 'utf8'; - - // the number of writers that are awaiting a drain event in .pipe()s - this.awaitDrain = 0; - - // if true, a maybeReadMore has been scheduled - this.readingMore = false; - - this.decoder = null; - this.encoding = null; - if (options.encoding) { - if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder; - this.decoder = new StringDecoder(options.encoding); - this.encoding = options.encoding; - } -} - -function Readable(options) { - Duplex = Duplex || require('./_stream_duplex'); - - if (!(this instanceof Readable)) return new Readable(options); - - this._readableState = new ReadableState(options, this); - - // legacy - this.readable = true; - - if (options) { - if (typeof options.read === 'function') this._read = options.read; - - if (typeof options.destroy === 'function') this._destroy = options.destroy; - } - - Stream.call(this); -} - -Object.defineProperty(Readable.prototype, 'destroyed', { - get: function () { - if (this._readableState === undefined) { - return false; - } - return this._readableState.destroyed; - }, - set: function (value) { - // we ignore the value if the stream - // has not been initialized yet - if (!this._readableState) { - return; - } - - // backward compatibility, the user is explicitly - // managing destroyed - this._readableState.destroyed = value; - } -}); - -Readable.prototype.destroy = destroyImpl.destroy; -Readable.prototype._undestroy = destroyImpl.undestroy; -Readable.prototype._destroy = function (err, cb) { - this.push(null); - cb(err); -}; - -// Manually shove something into the read() buffer. -// This returns true if the highWaterMark has not been hit yet, -// similar to how Writable.write() returns true if you should -// write() some more. -Readable.prototype.push = function (chunk, encoding) { - var state = this._readableState; - var skipChunkCheck; - - if (!state.objectMode) { - if (typeof chunk === 'string') { - encoding = encoding || state.defaultEncoding; - if (encoding !== state.encoding) { - chunk = Buffer.from(chunk, encoding); - encoding = ''; - } - skipChunkCheck = true; - } - } else { - skipChunkCheck = true; - } - - return readableAddChunk(this, chunk, encoding, false, skipChunkCheck); -}; - -// Unshift should *always* be something directly out of read() -Readable.prototype.unshift = function (chunk) { - return readableAddChunk(this, chunk, null, true, false); -}; - -function readableAddChunk(stream, chunk, encoding, addToFront, skipChunkCheck) { - var state = stream._readableState; - if (chunk === null) { - state.reading = false; - onEofChunk(stream, state); - } else { - var er; - if (!skipChunkCheck) er = chunkInvalid(state, chunk); - if (er) { - stream.emit('error', er); - } else if (state.objectMode || chunk && chunk.length > 0) { - if (typeof chunk !== 'string' && !state.objectMode && Object.getPrototypeOf(chunk) !== Buffer.prototype) { - chunk = _uint8ArrayToBuffer(chunk); - } - - if (addToFront) { - if (state.endEmitted) stream.emit('error', new Error('stream.unshift() after end event'));else addChunk(stream, state, chunk, true); - } else if (state.ended) { - stream.emit('error', new Error('stream.push() after EOF')); - } else { - state.reading = false; - if (state.decoder && !encoding) { - chunk = state.decoder.write(chunk); - if (state.objectMode || chunk.length !== 0) addChunk(stream, state, chunk, false);else maybeReadMore(stream, state); - } else { - addChunk(stream, state, chunk, false); - } - } - } else if (!addToFront) { - state.reading = false; - } - } - - return needMoreData(state); -} - -function addChunk(stream, state, chunk, addToFront) { - if (state.flowing && state.length === 0 && !state.sync) { - stream.emit('data', chunk); - stream.read(0); - } else { - // update the buffer info. - state.length += state.objectMode ? 1 : chunk.length; - if (addToFront) state.buffer.unshift(chunk);else state.buffer.push(chunk); - - if (state.needReadable) emitReadable(stream); - } - maybeReadMore(stream, state); -} - -function chunkInvalid(state, chunk) { - var er; - if (!_isUint8Array(chunk) && typeof chunk !== 'string' && chunk !== undefined && !state.objectMode) { - er = new TypeError('Invalid non-string/buffer chunk'); - } - return er; -} - -// if it's past the high water mark, we can push in some more. -// Also, if we have no data yet, we can stand some -// more bytes. This is to work around cases where hwm=0, -// such as the repl. Also, if the push() triggered a -// readable event, and the user called read(largeNumber) such that -// needReadable was set, then we ought to push more, so that another -// 'readable' event will be triggered. -function needMoreData(state) { - return !state.ended && (state.needReadable || state.length < state.highWaterMark || state.length === 0); -} - -Readable.prototype.isPaused = function () { - return this._readableState.flowing === false; -}; - -// backwards compatibility. -Readable.prototype.setEncoding = function (enc) { - if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder; - this._readableState.decoder = new StringDecoder(enc); - this._readableState.encoding = enc; - return this; -}; - -// Don't raise the hwm > 8MB -var MAX_HWM = 0x800000; -function computeNewHighWaterMark(n) { - if (n >= MAX_HWM) { - n = MAX_HWM; - } else { - // Get the next highest power of 2 to prevent increasing hwm excessively in - // tiny amounts - n--; - n |= n >>> 1; - n |= n >>> 2; - n |= n >>> 4; - n |= n >>> 8; - n |= n >>> 16; - n++; - } - return n; -} - -// This function is designed to be inlinable, so please take care when making -// changes to the function body. -function howMuchToRead(n, state) { - if (n <= 0 || state.length === 0 && state.ended) return 0; - if (state.objectMode) return 1; - if (n !== n) { - // Only flow one buffer at a time - if (state.flowing && state.length) return state.buffer.head.data.length;else return state.length; - } - // If we're asking for more than the current hwm, then raise the hwm. - if (n > state.highWaterMark) state.highWaterMark = computeNewHighWaterMark(n); - if (n <= state.length) return n; - // Don't have enough - if (!state.ended) { - state.needReadable = true; - return 0; - } - return state.length; -} - -// you can override either this method, or the async _read(n) below. -Readable.prototype.read = function (n) { - debug('read', n); - n = parseInt(n, 10); - var state = this._readableState; - var nOrig = n; - - if (n !== 0) state.emittedReadable = false; - - // if we're doing read(0) to trigger a readable event, but we - // already have a bunch of data in the buffer, then just trigger - // the 'readable' event and move on. - if (n === 0 && state.needReadable && (state.length >= state.highWaterMark || state.ended)) { - debug('read: emitReadable', state.length, state.ended); - if (state.length === 0 && state.ended) endReadable(this);else emitReadable(this); - return null; - } - - n = howMuchToRead(n, state); - - // if we've ended, and we're now clear, then finish it up. - if (n === 0 && state.ended) { - if (state.length === 0) endReadable(this); - return null; - } - - // All the actual chunk generation logic needs to be - // *below* the call to _read. The reason is that in certain - // synthetic stream cases, such as passthrough streams, _read - // may be a completely synchronous operation which may change - // the state of the read buffer, providing enough data when - // before there was *not* enough. - // - // So, the steps are: - // 1. Figure out what the state of things will be after we do - // a read from the buffer. - // - // 2. If that resulting state will trigger a _read, then call _read. - // Note that this may be asynchronous, or synchronous. Yes, it is - // deeply ugly to write APIs this way, but that still doesn't mean - // that the Readable class should behave improperly, as streams are - // designed to be sync/async agnostic. - // Take note if the _read call is sync or async (ie, if the read call - // has returned yet), so that we know whether or not it's safe to emit - // 'readable' etc. - // - // 3. Actually pull the requested chunks out of the buffer and return. - - // if we need a readable event, then we need to do some reading. - var doRead = state.needReadable; - debug('need readable', doRead); - - // if we currently have less than the highWaterMark, then also read some - if (state.length === 0 || state.length - n < state.highWaterMark) { - doRead = true; - debug('length less than watermark', doRead); - } - - // however, if we've ended, then there's no point, and if we're already - // reading, then it's unnecessary. - if (state.ended || state.reading) { - doRead = false; - debug('reading or ended', doRead); - } else if (doRead) { - debug('do read'); - state.reading = true; - state.sync = true; - // if the length is currently zero, then we *need* a readable event. - if (state.length === 0) state.needReadable = true; - // call internal read method - this._read(state.highWaterMark); - state.sync = false; - // If _read pushed data synchronously, then `reading` will be false, - // and we need to re-evaluate how much data we can return to the user. - if (!state.reading) n = howMuchToRead(nOrig, state); - } - - var ret; - if (n > 0) ret = fromList(n, state);else ret = null; - - if (ret === null) { - state.needReadable = true; - n = 0; - } else { - state.length -= n; - } - - if (state.length === 0) { - // If we have nothing in the buffer, then we want to know - // as soon as we *do* get something into the buffer. - if (!state.ended) state.needReadable = true; - - // If we tried to read() past the EOF, then emit end on the next tick. - if (nOrig !== n && state.ended) endReadable(this); - } - - if (ret !== null) this.emit('data', ret); - - return ret; -}; - -function onEofChunk(stream, state) { - if (state.ended) return; - if (state.decoder) { - var chunk = state.decoder.end(); - if (chunk && chunk.length) { - state.buffer.push(chunk); - state.length += state.objectMode ? 1 : chunk.length; - } - } - state.ended = true; - - // emit 'readable' now to make sure it gets picked up. - emitReadable(stream); -} - -// Don't emit readable right away in sync mode, because this can trigger -// another read() call => stack overflow. This way, it might trigger -// a nextTick recursion warning, but that's not so bad. -function emitReadable(stream) { - var state = stream._readableState; - state.needReadable = false; - if (!state.emittedReadable) { - debug('emitReadable', state.flowing); - state.emittedReadable = true; - if (state.sync) pna.nextTick(emitReadable_, stream);else emitReadable_(stream); - } -} - -function emitReadable_(stream) { - debug('emit readable'); - stream.emit('readable'); - flow(stream); -} - -// at this point, the user has presumably seen the 'readable' event, -// and called read() to consume some data. that may have triggered -// in turn another _read(n) call, in which case reading = true if -// it's in progress. -// However, if we're not ended, or reading, and the length < hwm, -// then go ahead and try to read some more preemptively. -function maybeReadMore(stream, state) { - if (!state.readingMore) { - state.readingMore = true; - pna.nextTick(maybeReadMore_, stream, state); - } -} - -function maybeReadMore_(stream, state) { - var len = state.length; - while (!state.reading && !state.flowing && !state.ended && state.length < state.highWaterMark) { - debug('maybeReadMore read 0'); - stream.read(0); - if (len === state.length) - // didn't get any data, stop spinning. - break;else len = state.length; - } - state.readingMore = false; -} - -// abstract method. to be overridden in specific implementation classes. -// call cb(er, data) where data is <= n in length. -// for virtual (non-string, non-buffer) streams, "length" is somewhat -// arbitrary, and perhaps not very meaningful. -Readable.prototype._read = function (n) { - this.emit('error', new Error('_read() is not implemented')); -}; - -Readable.prototype.pipe = function (dest, pipeOpts) { - var src = this; - var state = this._readableState; - - switch (state.pipesCount) { - case 0: - state.pipes = dest; - break; - case 1: - state.pipes = [state.pipes, dest]; - break; - default: - state.pipes.push(dest); - break; - } - state.pipesCount += 1; - debug('pipe count=%d opts=%j', state.pipesCount, pipeOpts); - - var doEnd = (!pipeOpts || pipeOpts.end !== false) && dest !== process.stdout && dest !== process.stderr; - - var endFn = doEnd ? onend : unpipe; - if (state.endEmitted) pna.nextTick(endFn);else src.once('end', endFn); - - dest.on('unpipe', onunpipe); - function onunpipe(readable, unpipeInfo) { - debug('onunpipe'); - if (readable === src) { - if (unpipeInfo && unpipeInfo.hasUnpiped === false) { - unpipeInfo.hasUnpiped = true; - cleanup(); - } - } - } - - function onend() { - debug('onend'); - dest.end(); - } - - // when the dest drains, it reduces the awaitDrain counter - // on the source. This would be more elegant with a .once() - // handler in flow(), but adding and removing repeatedly is - // too slow. - var ondrain = pipeOnDrain(src); - dest.on('drain', ondrain); - - var cleanedUp = false; - function cleanup() { - debug('cleanup'); - // cleanup event handlers once the pipe is broken - dest.removeListener('close', onclose); - dest.removeListener('finish', onfinish); - dest.removeListener('drain', ondrain); - dest.removeListener('error', onerror); - dest.removeListener('unpipe', onunpipe); - src.removeListener('end', onend); - src.removeListener('end', unpipe); - src.removeListener('data', ondata); - - cleanedUp = true; - - // if the reader is waiting for a drain event from this - // specific writer, then it would cause it to never start - // flowing again. - // So, if this is awaiting a drain, then we just call it now. - // If we don't know, then assume that we are waiting for one. - if (state.awaitDrain && (!dest._writableState || dest._writableState.needDrain)) ondrain(); - } - - // If the user pushes more data while we're writing to dest then we'll end up - // in ondata again. However, we only want to increase awaitDrain once because - // dest will only emit one 'drain' event for the multiple writes. - // => Introduce a guard on increasing awaitDrain. - var increasedAwaitDrain = false; - src.on('data', ondata); - function ondata(chunk) { - debug('ondata'); - increasedAwaitDrain = false; - var ret = dest.write(chunk); - if (false === ret && !increasedAwaitDrain) { - // If the user unpiped during `dest.write()`, it is possible - // to get stuck in a permanently paused state if that write - // also returned false. - // => Check whether `dest` is still a piping destination. - if ((state.pipesCount === 1 && state.pipes === dest || state.pipesCount > 1 && indexOf(state.pipes, dest) !== -1) && !cleanedUp) { - debug('false write response, pause', state.awaitDrain); - state.awaitDrain++; - increasedAwaitDrain = true; - } - src.pause(); - } - } - - // if the dest has an error, then stop piping into it. - // however, don't suppress the throwing behavior for this. - function onerror(er) { - debug('onerror', er); - unpipe(); - dest.removeListener('error', onerror); - if (EElistenerCount(dest, 'error') === 0) dest.emit('error', er); - } - - // Make sure our error handler is attached before userland ones. - prependListener(dest, 'error', onerror); - - // Both close and finish should trigger unpipe, but only once. - function onclose() { - dest.removeListener('finish', onfinish); - unpipe(); - } - dest.once('close', onclose); - function onfinish() { - debug('onfinish'); - dest.removeListener('close', onclose); - unpipe(); - } - dest.once('finish', onfinish); - - function unpipe() { - debug('unpipe'); - src.unpipe(dest); - } - - // tell the dest that it's being piped to - dest.emit('pipe', src); - - // start the flow if it hasn't been started already. - if (!state.flowing) { - debug('pipe resume'); - src.resume(); - } - - return dest; -}; - -function pipeOnDrain(src) { - return function () { - var state = src._readableState; - debug('pipeOnDrain', state.awaitDrain); - if (state.awaitDrain) state.awaitDrain--; - if (state.awaitDrain === 0 && EElistenerCount(src, 'data')) { - state.flowing = true; - flow(src); - } - }; -} - -Readable.prototype.unpipe = function (dest) { - var state = this._readableState; - var unpipeInfo = { hasUnpiped: false }; - - // if we're not piping anywhere, then do nothing. - if (state.pipesCount === 0) return this; - - // just one destination. most common case. - if (state.pipesCount === 1) { - // passed in one, but it's not the right one. - if (dest && dest !== state.pipes) return this; - - if (!dest) dest = state.pipes; - - // got a match. - state.pipes = null; - state.pipesCount = 0; - state.flowing = false; - if (dest) dest.emit('unpipe', this, unpipeInfo); - return this; - } - - // slow case. multiple pipe destinations. - - if (!dest) { - // remove all. - var dests = state.pipes; - var len = state.pipesCount; - state.pipes = null; - state.pipesCount = 0; - state.flowing = false; - - for (var i = 0; i < len; i++) { - dests[i].emit('unpipe', this, { hasUnpiped: false }); - }return this; - } - - // try to find the right one. - var index = indexOf(state.pipes, dest); - if (index === -1) return this; - - state.pipes.splice(index, 1); - state.pipesCount -= 1; - if (state.pipesCount === 1) state.pipes = state.pipes[0]; - - dest.emit('unpipe', this, unpipeInfo); - - return this; -}; - -// set up data events if they are asked for -// Ensure readable listeners eventually get something -Readable.prototype.on = function (ev, fn) { - var res = Stream.prototype.on.call(this, ev, fn); - - if (ev === 'data') { - // Start flowing on next tick if stream isn't explicitly paused - if (this._readableState.flowing !== false) this.resume(); - } else if (ev === 'readable') { - var state = this._readableState; - if (!state.endEmitted && !state.readableListening) { - state.readableListening = state.needReadable = true; - state.emittedReadable = false; - if (!state.reading) { - pna.nextTick(nReadingNextTick, this); - } else if (state.length) { - emitReadable(this); - } - } - } - - return res; -}; -Readable.prototype.addListener = Readable.prototype.on; - -function nReadingNextTick(self) { - debug('readable nexttick read 0'); - self.read(0); -} - -// pause() and resume() are remnants of the legacy readable stream API -// If the user uses them, then switch into old mode. -Readable.prototype.resume = function () { - var state = this._readableState; - if (!state.flowing) { - debug('resume'); - state.flowing = true; - resume(this, state); - } - return this; -}; - -function resume(stream, state) { - if (!state.resumeScheduled) { - state.resumeScheduled = true; - pna.nextTick(resume_, stream, state); - } -} - -function resume_(stream, state) { - if (!state.reading) { - debug('resume read 0'); - stream.read(0); - } - - state.resumeScheduled = false; - state.awaitDrain = 0; - stream.emit('resume'); - flow(stream); - if (state.flowing && !state.reading) stream.read(0); -} - -Readable.prototype.pause = function () { - debug('call pause flowing=%j', this._readableState.flowing); - if (false !== this._readableState.flowing) { - debug('pause'); - this._readableState.flowing = false; - this.emit('pause'); - } - return this; -}; - -function flow(stream) { - var state = stream._readableState; - debug('flow', state.flowing); - while (state.flowing && stream.read() !== null) {} -} - -// wrap an old-style stream as the async data source. -// This is *not* part of the readable stream interface. -// It is an ugly unfortunate mess of history. -Readable.prototype.wrap = function (stream) { - var _this = this; - - var state = this._readableState; - var paused = false; - - stream.on('end', function () { - debug('wrapped end'); - if (state.decoder && !state.ended) { - var chunk = state.decoder.end(); - if (chunk && chunk.length) _this.push(chunk); - } - - _this.push(null); - }); - - stream.on('data', function (chunk) { - debug('wrapped data'); - if (state.decoder) chunk = state.decoder.write(chunk); - - // don't skip over falsy values in objectMode - if (state.objectMode && (chunk === null || chunk === undefined)) return;else if (!state.objectMode && (!chunk || !chunk.length)) return; - - var ret = _this.push(chunk); - if (!ret) { - paused = true; - stream.pause(); - } - }); - - // proxy all the other methods. - // important when wrapping filters and duplexes. - for (var i in stream) { - if (this[i] === undefined && typeof stream[i] === 'function') { - this[i] = function (method) { - return function () { - return stream[method].apply(stream, arguments); - }; - }(i); - } - } - - // proxy certain important events. - for (var n = 0; n < kProxyEvents.length; n++) { - stream.on(kProxyEvents[n], this.emit.bind(this, kProxyEvents[n])); - } - - // when we try to consume some more bytes, simply unpause the - // underlying stream. - this._read = function (n) { - debug('wrapped _read', n); - if (paused) { - paused = false; - stream.resume(); - } - }; - - return this; -}; - -Object.defineProperty(Readable.prototype, 'readableHighWaterMark', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function () { - return this._readableState.highWaterMark; - } -}); - -// exposed for testing purposes only. -Readable._fromList = fromList; - -// Pluck off n bytes from an array of buffers. -// Length is the combined lengths of all the buffers in the list. -// This function is designed to be inlinable, so please take care when making -// changes to the function body. -function fromList(n, state) { - // nothing buffered - if (state.length === 0) return null; - - var ret; - if (state.objectMode) ret = state.buffer.shift();else if (!n || n >= state.length) { - // read it all, truncate the list - if (state.decoder) ret = state.buffer.join('');else if (state.buffer.length === 1) ret = state.buffer.head.data;else ret = state.buffer.concat(state.length); - state.buffer.clear(); - } else { - // read part of list - ret = fromListPartial(n, state.buffer, state.decoder); - } - - return ret; -} - -// Extracts only enough buffered data to satisfy the amount requested. -// This function is designed to be inlinable, so please take care when making -// changes to the function body. -function fromListPartial(n, list, hasStrings) { - var ret; - if (n < list.head.data.length) { - // slice is the same for buffers and strings - ret = list.head.data.slice(0, n); - list.head.data = list.head.data.slice(n); - } else if (n === list.head.data.length) { - // first chunk is a perfect match - ret = list.shift(); - } else { - // result spans more than one buffer - ret = hasStrings ? copyFromBufferString(n, list) : copyFromBuffer(n, list); - } - return ret; -} - -// Copies a specified amount of characters from the list of buffered data -// chunks. -// This function is designed to be inlinable, so please take care when making -// changes to the function body. -function copyFromBufferString(n, list) { - var p = list.head; - var c = 1; - var ret = p.data; - n -= ret.length; - while (p = p.next) { - var str = p.data; - var nb = n > str.length ? str.length : n; - if (nb === str.length) ret += str;else ret += str.slice(0, n); - n -= nb; - if (n === 0) { - if (nb === str.length) { - ++c; - if (p.next) list.head = p.next;else list.head = list.tail = null; - } else { - list.head = p; - p.data = str.slice(nb); - } - break; - } - ++c; - } - list.length -= c; - return ret; -} - -// Copies a specified amount of bytes from the list of buffered data chunks. -// This function is designed to be inlinable, so please take care when making -// changes to the function body. -function copyFromBuffer(n, list) { - var ret = Buffer.allocUnsafe(n); - var p = list.head; - var c = 1; - p.data.copy(ret); - n -= p.data.length; - while (p = p.next) { - var buf = p.data; - var nb = n > buf.length ? buf.length : n; - buf.copy(ret, ret.length - n, 0, nb); - n -= nb; - if (n === 0) { - if (nb === buf.length) { - ++c; - if (p.next) list.head = p.next;else list.head = list.tail = null; - } else { - list.head = p; - p.data = buf.slice(nb); - } - break; - } - ++c; - } - list.length -= c; - return ret; -} - -function endReadable(stream) { - var state = stream._readableState; - - // If we get here before consuming all the bytes, then that is a - // bug in node. Should never happen. - if (state.length > 0) throw new Error('"endReadable()" called on non-empty stream'); - - if (!state.endEmitted) { - state.ended = true; - pna.nextTick(endReadableNT, state, stream); - } -} - -function endReadableNT(state, stream) { - // Check that we didn't get one last unshift. - if (!state.endEmitted && state.length === 0) { - state.endEmitted = true; - stream.readable = false; - stream.emit('end'); - } -} - -function indexOf(xs, x) { - for (var i = 0, l = xs.length; i < l; i++) { - if (xs[i] === x) return i; - } - return -1; -} -}).call(this)}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./_stream_duplex":209,"./internal/streams/BufferList":214,"./internal/streams/destroy":215,"./internal/streams/stream":216,"_process":199,"core-util-is":79,"events":129,"inherits":155,"isarray":156,"process-nextick-args":198,"safe-buffer":217,"string_decoder/":218,"util":34}],212:[function(require,module,exports){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// a transform stream is a readable/writable stream where you do -// something with the data. Sometimes it's called a "filter", -// but that's not a great name for it, since that implies a thing where -// some bits pass through, and others are simply ignored. (That would -// be a valid example of a transform, of course.) -// -// While the output is causally related to the input, it's not a -// necessarily symmetric or synchronous transformation. For example, -// a zlib stream might take multiple plain-text writes(), and then -// emit a single compressed chunk some time in the future. -// -// Here's how this works: -// -// The Transform stream has all the aspects of the readable and writable -// stream classes. When you write(chunk), that calls _write(chunk,cb) -// internally, and returns false if there's a lot of pending writes -// buffered up. When you call read(), that calls _read(n) until -// there's enough pending readable data buffered up. -// -// In a transform stream, the written data is placed in a buffer. When -// _read(n) is called, it transforms the queued up data, calling the -// buffered _write cb's as it consumes chunks. If consuming a single -// written chunk would result in multiple output chunks, then the first -// outputted bit calls the readcb, and subsequent chunks just go into -// the read buffer, and will cause it to emit 'readable' if necessary. -// -// This way, back-pressure is actually determined by the reading side, -// since _read has to be called to start processing a new chunk. However, -// a pathological inflate type of transform can cause excessive buffering -// here. For example, imagine a stream where every byte of input is -// interpreted as an integer from 0-255, and then results in that many -// bytes of output. Writing the 4 bytes {ff,ff,ff,ff} would result in -// 1kb of data being output. In this case, you could write a very small -// amount of input, and end up with a very large amount of output. In -// such a pathological inflating mechanism, there'd be no way to tell -// the system to stop doing the transform. A single 4MB write could -// cause the system to run out of memory. -// -// However, even in such a pathological case, only a single written chunk -// would be consumed, and then the rest would wait (un-transformed) until -// the results of the previous transformed chunk were consumed. - -'use strict'; - -module.exports = Transform; - -var Duplex = require('./_stream_duplex'); - -/**/ -var util = Object.create(require('core-util-is')); -util.inherits = require('inherits'); -/**/ - -util.inherits(Transform, Duplex); - -function afterTransform(er, data) { - var ts = this._transformState; - ts.transforming = false; - - var cb = ts.writecb; - - if (!cb) { - return this.emit('error', new Error('write callback called multiple times')); - } - - ts.writechunk = null; - ts.writecb = null; - - if (data != null) // single equals check for both `null` and `undefined` - this.push(data); - - cb(er); - - var rs = this._readableState; - rs.reading = false; - if (rs.needReadable || rs.length < rs.highWaterMark) { - this._read(rs.highWaterMark); - } -} - -function Transform(options) { - if (!(this instanceof Transform)) return new Transform(options); - - Duplex.call(this, options); - - this._transformState = { - afterTransform: afterTransform.bind(this), - needTransform: false, - transforming: false, - writecb: null, - writechunk: null, - writeencoding: null - }; - - // start out asking for a readable event once data is transformed. - this._readableState.needReadable = true; - - // we have implemented the _read method, and done the other things - // that Readable wants before the first _read call, so unset the - // sync guard flag. - this._readableState.sync = false; - - if (options) { - if (typeof options.transform === 'function') this._transform = options.transform; - - if (typeof options.flush === 'function') this._flush = options.flush; - } - - // When the writable side finishes, then flush out anything remaining. - this.on('prefinish', prefinish); -} - -function prefinish() { - var _this = this; - - if (typeof this._flush === 'function') { - this._flush(function (er, data) { - done(_this, er, data); - }); - } else { - done(this, null, null); - } -} - -Transform.prototype.push = function (chunk, encoding) { - this._transformState.needTransform = false; - return Duplex.prototype.push.call(this, chunk, encoding); -}; - -// This is the part where you do stuff! -// override this function in implementation classes. -// 'chunk' is an input chunk. -// -// Call `push(newChunk)` to pass along transformed output -// to the readable side. You may call 'push' zero or more times. -// -// Call `cb(err)` when you are done with this chunk. If you pass -// an error, then that'll put the hurt on the whole operation. If you -// never call cb(), then you'll never get another chunk. -Transform.prototype._transform = function (chunk, encoding, cb) { - throw new Error('_transform() is not implemented'); -}; - -Transform.prototype._write = function (chunk, encoding, cb) { - var ts = this._transformState; - ts.writecb = cb; - ts.writechunk = chunk; - ts.writeencoding = encoding; - if (!ts.transforming) { - var rs = this._readableState; - if (ts.needTransform || rs.needReadable || rs.length < rs.highWaterMark) this._read(rs.highWaterMark); - } -}; - -// Doesn't matter what the args are here. -// _transform does all the work. -// That we got here means that the readable side wants more data. -Transform.prototype._read = function (n) { - var ts = this._transformState; - - if (ts.writechunk !== null && ts.writecb && !ts.transforming) { - ts.transforming = true; - this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform); - } else { - // mark that we need a transform, so that any data that comes in - // will get processed, now that we've asked for it. - ts.needTransform = true; - } -}; - -Transform.prototype._destroy = function (err, cb) { - var _this2 = this; - - Duplex.prototype._destroy.call(this, err, function (err2) { - cb(err2); - _this2.emit('close'); - }); -}; - -function done(stream, er, data) { - if (er) return stream.emit('error', er); - - if (data != null) // single equals check for both `null` and `undefined` - stream.push(data); - - // if there's nothing in the write buffer, then that means - // that nothing more will ever be provided - if (stream._writableState.length) throw new Error('Calling transform done when ws.length != 0'); - - if (stream._transformState.transforming) throw new Error('Calling transform done when still transforming'); - - return stream.push(null); -} -},{"./_stream_duplex":209,"core-util-is":79,"inherits":155}],213:[function(require,module,exports){ -(function (process,global,setImmediate){(function (){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// A bit simpler than readable streams. -// Implement an async ._write(chunk, encoding, cb), and it'll handle all -// the drain event emission and buffering. - -'use strict'; - -/**/ - -var pna = require('process-nextick-args'); -/**/ - -module.exports = Writable; - -/* */ -function WriteReq(chunk, encoding, cb) { - this.chunk = chunk; - this.encoding = encoding; - this.callback = cb; - this.next = null; -} - -// It seems a linked list but it is not -// there will be only 2 of these for each stream -function CorkedRequest(state) { - var _this = this; - - this.next = null; - this.entry = null; - this.finish = function () { - onCorkedFinish(_this, state); - }; -} -/* */ - -/**/ -var asyncWrite = !process.browser && ['v0.10', 'v0.9.'].indexOf(process.version.slice(0, 5)) > -1 ? setImmediate : pna.nextTick; -/**/ - -/**/ -var Duplex; -/**/ - -Writable.WritableState = WritableState; - -/**/ -var util = Object.create(require('core-util-is')); -util.inherits = require('inherits'); -/**/ - -/**/ -var internalUtil = { - deprecate: require('util-deprecate') -}; -/**/ - -/**/ -var Stream = require('./internal/streams/stream'); -/**/ - -/**/ - -var Buffer = require('safe-buffer').Buffer; -var OurUint8Array = (typeof global !== 'undefined' ? global : typeof window !== 'undefined' ? window : typeof self !== 'undefined' ? self : {}).Uint8Array || function () {}; -function _uint8ArrayToBuffer(chunk) { - return Buffer.from(chunk); -} -function _isUint8Array(obj) { - return Buffer.isBuffer(obj) || obj instanceof OurUint8Array; -} - -/**/ - -var destroyImpl = require('./internal/streams/destroy'); - -util.inherits(Writable, Stream); - -function nop() {} - -function WritableState(options, stream) { - Duplex = Duplex || require('./_stream_duplex'); - - options = options || {}; - - // Duplex streams are both readable and writable, but share - // the same options object. - // However, some cases require setting options to different - // values for the readable and the writable sides of the duplex stream. - // These options can be provided separately as readableXXX and writableXXX. - var isDuplex = stream instanceof Duplex; - - // object stream flag to indicate whether or not this stream - // contains buffers or objects. - this.objectMode = !!options.objectMode; - - if (isDuplex) this.objectMode = this.objectMode || !!options.writableObjectMode; - - // the point at which write() starts returning false - // Note: 0 is a valid value, means that we always return false if - // the entire buffer is not flushed immediately on write() - var hwm = options.highWaterMark; - var writableHwm = options.writableHighWaterMark; - var defaultHwm = this.objectMode ? 16 : 16 * 1024; - - if (hwm || hwm === 0) this.highWaterMark = hwm;else if (isDuplex && (writableHwm || writableHwm === 0)) this.highWaterMark = writableHwm;else this.highWaterMark = defaultHwm; - - // cast to ints. - this.highWaterMark = Math.floor(this.highWaterMark); - - // if _final has been called - this.finalCalled = false; - - // drain event flag. - this.needDrain = false; - // at the start of calling end() - this.ending = false; - // when end() has been called, and returned - this.ended = false; - // when 'finish' is emitted - this.finished = false; - - // has it been destroyed - this.destroyed = false; - - // should we decode strings into buffers before passing to _write? - // this is here so that some node-core streams can optimize string - // handling at a lower level. - var noDecode = options.decodeStrings === false; - this.decodeStrings = !noDecode; - - // Crypto is kind of old and crusty. Historically, its default string - // encoding is 'binary' so we have to make this configurable. - // Everything else in the universe uses 'utf8', though. - this.defaultEncoding = options.defaultEncoding || 'utf8'; - - // not an actual buffer we keep track of, but a measurement - // of how much we're waiting to get pushed to some underlying - // socket or file. - this.length = 0; - - // a flag to see when we're in the middle of a write. - this.writing = false; - - // when true all writes will be buffered until .uncork() call - this.corked = 0; - - // a flag to be able to tell if the onwrite cb is called immediately, - // or on a later tick. We set this to true at first, because any - // actions that shouldn't happen until "later" should generally also - // not happen before the first write call. - this.sync = true; - - // a flag to know if we're processing previously buffered items, which - // may call the _write() callback in the same tick, so that we don't - // end up in an overlapped onwrite situation. - this.bufferProcessing = false; - - // the callback that's passed to _write(chunk,cb) - this.onwrite = function (er) { - onwrite(stream, er); - }; - - // the callback that the user supplies to write(chunk,encoding,cb) - this.writecb = null; - - // the amount that is being written when _write is called. - this.writelen = 0; - - this.bufferedRequest = null; - this.lastBufferedRequest = null; - - // number of pending user-supplied write callbacks - // this must be 0 before 'finish' can be emitted - this.pendingcb = 0; - - // emit prefinish if the only thing we're waiting for is _write cbs - // This is relevant for synchronous Transform streams - this.prefinished = false; - - // True if the error was already emitted and should not be thrown again - this.errorEmitted = false; - - // count buffered requests - this.bufferedRequestCount = 0; - - // allocate the first CorkedRequest, there is always - // one allocated and free to use, and we maintain at most two - this.corkedRequestsFree = new CorkedRequest(this); -} - -WritableState.prototype.getBuffer = function getBuffer() { - var current = this.bufferedRequest; - var out = []; - while (current) { - out.push(current); - current = current.next; - } - return out; -}; - -(function () { - try { - Object.defineProperty(WritableState.prototype, 'buffer', { - get: internalUtil.deprecate(function () { - return this.getBuffer(); - }, '_writableState.buffer is deprecated. Use _writableState.getBuffer ' + 'instead.', 'DEP0003') - }); - } catch (_) {} -})(); - -// Test _writableState for inheritance to account for Duplex streams, -// whose prototype chain only points to Readable. -var realHasInstance; -if (typeof Symbol === 'function' && Symbol.hasInstance && typeof Function.prototype[Symbol.hasInstance] === 'function') { - realHasInstance = Function.prototype[Symbol.hasInstance]; - Object.defineProperty(Writable, Symbol.hasInstance, { - value: function (object) { - if (realHasInstance.call(this, object)) return true; - if (this !== Writable) return false; - - return object && object._writableState instanceof WritableState; - } - }); -} else { - realHasInstance = function (object) { - return object instanceof this; - }; -} - -function Writable(options) { - Duplex = Duplex || require('./_stream_duplex'); - - // Writable ctor is applied to Duplexes, too. - // `realHasInstance` is necessary because using plain `instanceof` - // would return false, as no `_writableState` property is attached. - - // Trying to use the custom `instanceof` for Writable here will also break the - // Node.js LazyTransform implementation, which has a non-trivial getter for - // `_writableState` that would lead to infinite recursion. - if (!realHasInstance.call(Writable, this) && !(this instanceof Duplex)) { - return new Writable(options); - } - - this._writableState = new WritableState(options, this); - - // legacy. - this.writable = true; - - if (options) { - if (typeof options.write === 'function') this._write = options.write; - - if (typeof options.writev === 'function') this._writev = options.writev; - - if (typeof options.destroy === 'function') this._destroy = options.destroy; - - if (typeof options.final === 'function') this._final = options.final; - } - - Stream.call(this); -} - -// Otherwise people can pipe Writable streams, which is just wrong. -Writable.prototype.pipe = function () { - this.emit('error', new Error('Cannot pipe, not readable')); -}; - -function writeAfterEnd(stream, cb) { - var er = new Error('write after end'); - // TODO: defer error events consistently everywhere, not just the cb - stream.emit('error', er); - pna.nextTick(cb, er); -} - -// Checks that a user-supplied chunk is valid, especially for the particular -// mode the stream is in. Currently this means that `null` is never accepted -// and undefined/non-string values are only allowed in object mode. -function validChunk(stream, state, chunk, cb) { - var valid = true; - var er = false; - - if (chunk === null) { - er = new TypeError('May not write null values to stream'); - } else if (typeof chunk !== 'string' && chunk !== undefined && !state.objectMode) { - er = new TypeError('Invalid non-string/buffer chunk'); - } - if (er) { - stream.emit('error', er); - pna.nextTick(cb, er); - valid = false; - } - return valid; -} - -Writable.prototype.write = function (chunk, encoding, cb) { - var state = this._writableState; - var ret = false; - var isBuf = !state.objectMode && _isUint8Array(chunk); - - if (isBuf && !Buffer.isBuffer(chunk)) { - chunk = _uint8ArrayToBuffer(chunk); - } - - if (typeof encoding === 'function') { - cb = encoding; - encoding = null; - } - - if (isBuf) encoding = 'buffer';else if (!encoding) encoding = state.defaultEncoding; - - if (typeof cb !== 'function') cb = nop; - - if (state.ended) writeAfterEnd(this, cb);else if (isBuf || validChunk(this, state, chunk, cb)) { - state.pendingcb++; - ret = writeOrBuffer(this, state, isBuf, chunk, encoding, cb); - } - - return ret; -}; - -Writable.prototype.cork = function () { - var state = this._writableState; - - state.corked++; -}; - -Writable.prototype.uncork = function () { - var state = this._writableState; - - if (state.corked) { - state.corked--; - - if (!state.writing && !state.corked && !state.bufferProcessing && state.bufferedRequest) clearBuffer(this, state); - } -}; - -Writable.prototype.setDefaultEncoding = function setDefaultEncoding(encoding) { - // node::ParseEncoding() requires lower case. - if (typeof encoding === 'string') encoding = encoding.toLowerCase(); - if (!(['hex', 'utf8', 'utf-8', 'ascii', 'binary', 'base64', 'ucs2', 'ucs-2', 'utf16le', 'utf-16le', 'raw'].indexOf((encoding + '').toLowerCase()) > -1)) throw new TypeError('Unknown encoding: ' + encoding); - this._writableState.defaultEncoding = encoding; - return this; -}; - -function decodeChunk(state, chunk, encoding) { - if (!state.objectMode && state.decodeStrings !== false && typeof chunk === 'string') { - chunk = Buffer.from(chunk, encoding); - } - return chunk; -} - -Object.defineProperty(Writable.prototype, 'writableHighWaterMark', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function () { - return this._writableState.highWaterMark; - } -}); - -// if we're already writing something, then just put this -// in the queue, and wait our turn. Otherwise, call _write -// If we return false, then we need a drain event, so set that flag. -function writeOrBuffer(stream, state, isBuf, chunk, encoding, cb) { - if (!isBuf) { - var newChunk = decodeChunk(state, chunk, encoding); - if (chunk !== newChunk) { - isBuf = true; - encoding = 'buffer'; - chunk = newChunk; - } - } - var len = state.objectMode ? 1 : chunk.length; - - state.length += len; - - var ret = state.length < state.highWaterMark; - // we must ensure that previous needDrain will not be reset to false. - if (!ret) state.needDrain = true; - - if (state.writing || state.corked) { - var last = state.lastBufferedRequest; - state.lastBufferedRequest = { - chunk: chunk, - encoding: encoding, - isBuf: isBuf, - callback: cb, - next: null - }; - if (last) { - last.next = state.lastBufferedRequest; - } else { - state.bufferedRequest = state.lastBufferedRequest; - } - state.bufferedRequestCount += 1; - } else { - doWrite(stream, state, false, len, chunk, encoding, cb); - } - - return ret; -} - -function doWrite(stream, state, writev, len, chunk, encoding, cb) { - state.writelen = len; - state.writecb = cb; - state.writing = true; - state.sync = true; - if (writev) stream._writev(chunk, state.onwrite);else stream._write(chunk, encoding, state.onwrite); - state.sync = false; -} - -function onwriteError(stream, state, sync, er, cb) { - --state.pendingcb; - - if (sync) { - // defer the callback if we are being called synchronously - // to avoid piling up things on the stack - pna.nextTick(cb, er); - // this can emit finish, and it will always happen - // after error - pna.nextTick(finishMaybe, stream, state); - stream._writableState.errorEmitted = true; - stream.emit('error', er); - } else { - // the caller expect this to happen before if - // it is async - cb(er); - stream._writableState.errorEmitted = true; - stream.emit('error', er); - // this can emit finish, but finish must - // always follow error - finishMaybe(stream, state); - } -} - -function onwriteStateUpdate(state) { - state.writing = false; - state.writecb = null; - state.length -= state.writelen; - state.writelen = 0; -} - -function onwrite(stream, er) { - var state = stream._writableState; - var sync = state.sync; - var cb = state.writecb; - - onwriteStateUpdate(state); - - if (er) onwriteError(stream, state, sync, er, cb);else { - // Check if we're actually ready to finish, but don't emit yet - var finished = needFinish(state); - - if (!finished && !state.corked && !state.bufferProcessing && state.bufferedRequest) { - clearBuffer(stream, state); - } - - if (sync) { - /**/ - asyncWrite(afterWrite, stream, state, finished, cb); - /**/ - } else { - afterWrite(stream, state, finished, cb); - } - } -} - -function afterWrite(stream, state, finished, cb) { - if (!finished) onwriteDrain(stream, state); - state.pendingcb--; - cb(); - finishMaybe(stream, state); -} - -// Must force callback to be called on nextTick, so that we don't -// emit 'drain' before the write() consumer gets the 'false' return -// value, and has a chance to attach a 'drain' listener. -function onwriteDrain(stream, state) { - if (state.length === 0 && state.needDrain) { - state.needDrain = false; - stream.emit('drain'); - } -} - -// if there's something in the buffer waiting, then process it -function clearBuffer(stream, state) { - state.bufferProcessing = true; - var entry = state.bufferedRequest; - - if (stream._writev && entry && entry.next) { - // Fast case, write everything using _writev() - var l = state.bufferedRequestCount; - var buffer = new Array(l); - var holder = state.corkedRequestsFree; - holder.entry = entry; - - var count = 0; - var allBuffers = true; - while (entry) { - buffer[count] = entry; - if (!entry.isBuf) allBuffers = false; - entry = entry.next; - count += 1; - } - buffer.allBuffers = allBuffers; - - doWrite(stream, state, true, state.length, buffer, '', holder.finish); - - // doWrite is almost always async, defer these to save a bit of time - // as the hot path ends with doWrite - state.pendingcb++; - state.lastBufferedRequest = null; - if (holder.next) { - state.corkedRequestsFree = holder.next; - holder.next = null; - } else { - state.corkedRequestsFree = new CorkedRequest(state); - } - state.bufferedRequestCount = 0; - } else { - // Slow case, write chunks one-by-one - while (entry) { - var chunk = entry.chunk; - var encoding = entry.encoding; - var cb = entry.callback; - var len = state.objectMode ? 1 : chunk.length; - - doWrite(stream, state, false, len, chunk, encoding, cb); - entry = entry.next; - state.bufferedRequestCount--; - // if we didn't call the onwrite immediately, then - // it means that we need to wait until it does. - // also, that means that the chunk and cb are currently - // being processed, so move the buffer counter past them. - if (state.writing) { - break; - } - } - - if (entry === null) state.lastBufferedRequest = null; - } - - state.bufferedRequest = entry; - state.bufferProcessing = false; -} - -Writable.prototype._write = function (chunk, encoding, cb) { - cb(new Error('_write() is not implemented')); -}; - -Writable.prototype._writev = null; - -Writable.prototype.end = function (chunk, encoding, cb) { - var state = this._writableState; - - if (typeof chunk === 'function') { - cb = chunk; - chunk = null; - encoding = null; - } else if (typeof encoding === 'function') { - cb = encoding; - encoding = null; - } - - if (chunk !== null && chunk !== undefined) this.write(chunk, encoding); - - // .end() fully uncorks - if (state.corked) { - state.corked = 1; - this.uncork(); - } - - // ignore unnecessary end() calls. - if (!state.ending) endWritable(this, state, cb); -}; - -function needFinish(state) { - return state.ending && state.length === 0 && state.bufferedRequest === null && !state.finished && !state.writing; -} -function callFinal(stream, state) { - stream._final(function (err) { - state.pendingcb--; - if (err) { - stream.emit('error', err); - } - state.prefinished = true; - stream.emit('prefinish'); - finishMaybe(stream, state); - }); -} -function prefinish(stream, state) { - if (!state.prefinished && !state.finalCalled) { - if (typeof stream._final === 'function') { - state.pendingcb++; - state.finalCalled = true; - pna.nextTick(callFinal, stream, state); - } else { - state.prefinished = true; - stream.emit('prefinish'); - } - } -} - -function finishMaybe(stream, state) { - var need = needFinish(state); - if (need) { - prefinish(stream, state); - if (state.pendingcb === 0) { - state.finished = true; - stream.emit('finish'); - } - } - return need; -} - -function endWritable(stream, state, cb) { - state.ending = true; - finishMaybe(stream, state); - if (cb) { - if (state.finished) pna.nextTick(cb);else stream.once('finish', cb); - } - state.ended = true; - stream.writable = false; -} - -function onCorkedFinish(corkReq, state, err) { - var entry = corkReq.entry; - corkReq.entry = null; - while (entry) { - var cb = entry.callback; - state.pendingcb--; - cb(err); - entry = entry.next; - } - - // reuse the free corkReq. - state.corkedRequestsFree.next = corkReq; -} - -Object.defineProperty(Writable.prototype, 'destroyed', { - get: function () { - if (this._writableState === undefined) { - return false; - } - return this._writableState.destroyed; - }, - set: function (value) { - // we ignore the value if the stream - // has not been initialized yet - if (!this._writableState) { - return; - } - - // backward compatibility, the user is explicitly - // managing destroyed - this._writableState.destroyed = value; - } -}); - -Writable.prototype.destroy = destroyImpl.destroy; -Writable.prototype._undestroy = destroyImpl.undestroy; -Writable.prototype._destroy = function (err, cb) { - this.end(); - cb(err); -}; -}).call(this)}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("timers").setImmediate) -},{"./_stream_duplex":209,"./internal/streams/destroy":215,"./internal/streams/stream":216,"_process":199,"core-util-is":79,"inherits":155,"process-nextick-args":198,"safe-buffer":217,"timers":248,"util-deprecate":249}],214:[function(require,module,exports){ -'use strict'; - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -var Buffer = require('safe-buffer').Buffer; -var util = require('util'); - -function copyBuffer(src, target, offset) { - src.copy(target, offset); -} - -module.exports = function () { - function BufferList() { - _classCallCheck(this, BufferList); - - this.head = null; - this.tail = null; - this.length = 0; - } - - BufferList.prototype.push = function push(v) { - var entry = { data: v, next: null }; - if (this.length > 0) this.tail.next = entry;else this.head = entry; - this.tail = entry; - ++this.length; - }; - - BufferList.prototype.unshift = function unshift(v) { - var entry = { data: v, next: this.head }; - if (this.length === 0) this.tail = entry; - this.head = entry; - ++this.length; - }; - - BufferList.prototype.shift = function shift() { - if (this.length === 0) return; - var ret = this.head.data; - if (this.length === 1) this.head = this.tail = null;else this.head = this.head.next; - --this.length; - return ret; - }; - - BufferList.prototype.clear = function clear() { - this.head = this.tail = null; - this.length = 0; - }; - - BufferList.prototype.join = function join(s) { - if (this.length === 0) return ''; - var p = this.head; - var ret = '' + p.data; - while (p = p.next) { - ret += s + p.data; - }return ret; - }; - - BufferList.prototype.concat = function concat(n) { - if (this.length === 0) return Buffer.alloc(0); - var ret = Buffer.allocUnsafe(n >>> 0); - var p = this.head; - var i = 0; - while (p) { - copyBuffer(p.data, ret, i); - i += p.data.length; - p = p.next; - } - return ret; - }; - - return BufferList; -}(); - -if (util && util.inspect && util.inspect.custom) { - module.exports.prototype[util.inspect.custom] = function () { - var obj = util.inspect({ length: this.length }); - return this.constructor.name + ' ' + obj; - }; -} -},{"safe-buffer":217,"util":34}],215:[function(require,module,exports){ -'use strict'; - -/**/ - -var pna = require('process-nextick-args'); -/**/ - -// undocumented cb() API, needed for core, not for public API -function destroy(err, cb) { - var _this = this; - - var readableDestroyed = this._readableState && this._readableState.destroyed; - var writableDestroyed = this._writableState && this._writableState.destroyed; - - if (readableDestroyed || writableDestroyed) { - if (cb) { - cb(err); - } else if (err) { - if (!this._writableState) { - pna.nextTick(emitErrorNT, this, err); - } else if (!this._writableState.errorEmitted) { - this._writableState.errorEmitted = true; - pna.nextTick(emitErrorNT, this, err); - } - } - - return this; - } - - // we set destroyed to true before firing error callbacks in order - // to make it re-entrance safe in case destroy() is called within callbacks - - if (this._readableState) { - this._readableState.destroyed = true; - } - - // if this is a duplex stream mark the writable part as destroyed as well - if (this._writableState) { - this._writableState.destroyed = true; - } - - this._destroy(err || null, function (err) { - if (!cb && err) { - if (!_this._writableState) { - pna.nextTick(emitErrorNT, _this, err); - } else if (!_this._writableState.errorEmitted) { - _this._writableState.errorEmitted = true; - pna.nextTick(emitErrorNT, _this, err); - } - } else if (cb) { - cb(err); - } - }); - - return this; -} - -function undestroy() { - if (this._readableState) { - this._readableState.destroyed = false; - this._readableState.reading = false; - this._readableState.ended = false; - this._readableState.endEmitted = false; - } - - if (this._writableState) { - this._writableState.destroyed = false; - this._writableState.ended = false; - this._writableState.ending = false; - this._writableState.finalCalled = false; - this._writableState.prefinished = false; - this._writableState.finished = false; - this._writableState.errorEmitted = false; - } -} - -function emitErrorNT(self, err) { - self.emit('error', err); -} - -module.exports = { - destroy: destroy, - undestroy: undestroy -}; -},{"process-nextick-args":198}],216:[function(require,module,exports){ -module.exports = require('events').EventEmitter; - -},{"events":129}],217:[function(require,module,exports){ -/* eslint-disable node/no-deprecated-api */ -var buffer = require('buffer') -var Buffer = buffer.Buffer - -// alternative to using Object.keys for old browsers -function copyProps (src, dst) { - for (var key in src) { - dst[key] = src[key] - } -} -if (Buffer.from && Buffer.alloc && Buffer.allocUnsafe && Buffer.allocUnsafeSlow) { - module.exports = buffer -} else { - // Copy properties from require('buffer') - copyProps(buffer, exports) - exports.Buffer = SafeBuffer -} - -function SafeBuffer (arg, encodingOrOffset, length) { - return Buffer(arg, encodingOrOffset, length) -} - -// Copy static methods from Buffer -copyProps(Buffer, SafeBuffer) - -SafeBuffer.from = function (arg, encodingOrOffset, length) { - if (typeof arg === 'number') { - throw new TypeError('Argument must not be a number') - } - return Buffer(arg, encodingOrOffset, length) -} - -SafeBuffer.alloc = function (size, fill, encoding) { - if (typeof size !== 'number') { - throw new TypeError('Argument must be a number') - } - var buf = Buffer(size) - if (fill !== undefined) { - if (typeof encoding === 'string') { - buf.fill(fill, encoding) - } else { - buf.fill(fill) - } - } else { - buf.fill(0) - } - return buf -} - -SafeBuffer.allocUnsafe = function (size) { - if (typeof size !== 'number') { - throw new TypeError('Argument must be a number') - } - return Buffer(size) -} - -SafeBuffer.allocUnsafeSlow = function (size) { - if (typeof size !== 'number') { - throw new TypeError('Argument must be a number') - } - return buffer.SlowBuffer(size) -} - -},{"buffer":63}],218:[function(require,module,exports){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -'use strict'; - -/**/ - -var Buffer = require('safe-buffer').Buffer; -/**/ - -var isEncoding = Buffer.isEncoding || function (encoding) { - encoding = '' + encoding; - switch (encoding && encoding.toLowerCase()) { - case 'hex':case 'utf8':case 'utf-8':case 'ascii':case 'binary':case 'base64':case 'ucs2':case 'ucs-2':case 'utf16le':case 'utf-16le':case 'raw': - return true; - default: - return false; - } -}; - -function _normalizeEncoding(enc) { - if (!enc) return 'utf8'; - var retried; - while (true) { - switch (enc) { - case 'utf8': - case 'utf-8': - return 'utf8'; - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return 'utf16le'; - case 'latin1': - case 'binary': - return 'latin1'; - case 'base64': - case 'ascii': - case 'hex': - return enc; - default: - if (retried) return; // undefined - enc = ('' + enc).toLowerCase(); - retried = true; - } - } -}; - -// Do not cache `Buffer.isEncoding` when checking encoding names as some -// modules monkey-patch it to support additional encodings -function normalizeEncoding(enc) { - var nenc = _normalizeEncoding(enc); - if (typeof nenc !== 'string' && (Buffer.isEncoding === isEncoding || !isEncoding(enc))) throw new Error('Unknown encoding: ' + enc); - return nenc || enc; -} - -// StringDecoder provides an interface for efficiently splitting a series of -// buffers into a series of JS strings without breaking apart multi-byte -// characters. -exports.StringDecoder = StringDecoder; -function StringDecoder(encoding) { - this.encoding = normalizeEncoding(encoding); - var nb; - switch (this.encoding) { - case 'utf16le': - this.text = utf16Text; - this.end = utf16End; - nb = 4; - break; - case 'utf8': - this.fillLast = utf8FillLast; - nb = 4; - break; - case 'base64': - this.text = base64Text; - this.end = base64End; - nb = 3; - break; - default: - this.write = simpleWrite; - this.end = simpleEnd; - return; - } - this.lastNeed = 0; - this.lastTotal = 0; - this.lastChar = Buffer.allocUnsafe(nb); -} - -StringDecoder.prototype.write = function (buf) { - if (buf.length === 0) return ''; - var r; - var i; - if (this.lastNeed) { - r = this.fillLast(buf); - if (r === undefined) return ''; - i = this.lastNeed; - this.lastNeed = 0; - } else { - i = 0; - } - if (i < buf.length) return r ? r + this.text(buf, i) : this.text(buf, i); - return r || ''; -}; - -StringDecoder.prototype.end = utf8End; - -// Returns only complete characters in a Buffer -StringDecoder.prototype.text = utf8Text; - -// Attempts to complete a partial non-UTF-8 character using bytes from a Buffer -StringDecoder.prototype.fillLast = function (buf) { - if (this.lastNeed <= buf.length) { - buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, this.lastNeed); - return this.lastChar.toString(this.encoding, 0, this.lastTotal); - } - buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, buf.length); - this.lastNeed -= buf.length; -}; - -// Checks the type of a UTF-8 byte, whether it's ASCII, a leading byte, or a -// continuation byte. If an invalid byte is detected, -2 is returned. -function utf8CheckByte(byte) { - if (byte <= 0x7F) return 0;else if (byte >> 5 === 0x06) return 2;else if (byte >> 4 === 0x0E) return 3;else if (byte >> 3 === 0x1E) return 4; - return byte >> 6 === 0x02 ? -1 : -2; -} - -// Checks at most 3 bytes at the end of a Buffer in order to detect an -// incomplete multi-byte UTF-8 character. The total number of bytes (2, 3, or 4) -// needed to complete the UTF-8 character (if applicable) are returned. -function utf8CheckIncomplete(self, buf, i) { - var j = buf.length - 1; - if (j < i) return 0; - var nb = utf8CheckByte(buf[j]); - if (nb >= 0) { - if (nb > 0) self.lastNeed = nb - 1; - return nb; - } - if (--j < i || nb === -2) return 0; - nb = utf8CheckByte(buf[j]); - if (nb >= 0) { - if (nb > 0) self.lastNeed = nb - 2; - return nb; - } - if (--j < i || nb === -2) return 0; - nb = utf8CheckByte(buf[j]); - if (nb >= 0) { - if (nb > 0) { - if (nb === 2) nb = 0;else self.lastNeed = nb - 3; - } - return nb; - } - return 0; -} - -// Validates as many continuation bytes for a multi-byte UTF-8 character as -// needed or are available. If we see a non-continuation byte where we expect -// one, we "replace" the validated continuation bytes we've seen so far with -// a single UTF-8 replacement character ('\ufffd'), to match v8's UTF-8 decoding -// behavior. The continuation byte check is included three times in the case -// where all of the continuation bytes for a character exist in the same buffer. -// It is also done this way as a slight performance increase instead of using a -// loop. -function utf8CheckExtraBytes(self, buf, p) { - if ((buf[0] & 0xC0) !== 0x80) { - self.lastNeed = 0; - return '\ufffd'; - } - if (self.lastNeed > 1 && buf.length > 1) { - if ((buf[1] & 0xC0) !== 0x80) { - self.lastNeed = 1; - return '\ufffd'; - } - if (self.lastNeed > 2 && buf.length > 2) { - if ((buf[2] & 0xC0) !== 0x80) { - self.lastNeed = 2; - return '\ufffd'; - } - } - } -} - -// Attempts to complete a multi-byte UTF-8 character using bytes from a Buffer. -function utf8FillLast(buf) { - var p = this.lastTotal - this.lastNeed; - var r = utf8CheckExtraBytes(this, buf, p); - if (r !== undefined) return r; - if (this.lastNeed <= buf.length) { - buf.copy(this.lastChar, p, 0, this.lastNeed); - return this.lastChar.toString(this.encoding, 0, this.lastTotal); - } - buf.copy(this.lastChar, p, 0, buf.length); - this.lastNeed -= buf.length; -} - -// Returns all complete UTF-8 characters in a Buffer. If the Buffer ended on a -// partial character, the character's bytes are buffered until the required -// number of bytes are available. -function utf8Text(buf, i) { - var total = utf8CheckIncomplete(this, buf, i); - if (!this.lastNeed) return buf.toString('utf8', i); - this.lastTotal = total; - var end = buf.length - (total - this.lastNeed); - buf.copy(this.lastChar, 0, end); - return buf.toString('utf8', i, end); -} - -// For UTF-8, a replacement character is added when ending on a partial -// character. -function utf8End(buf) { - var r = buf && buf.length ? this.write(buf) : ''; - if (this.lastNeed) return r + '\ufffd'; - return r; -} - -// UTF-16LE typically needs two bytes per character, but even if we have an even -// number of bytes available, we need to check if we end on a leading/high -// surrogate. In that case, we need to wait for the next two bytes in order to -// decode the last character properly. -function utf16Text(buf, i) { - if ((buf.length - i) % 2 === 0) { - var r = buf.toString('utf16le', i); - if (r) { - var c = r.charCodeAt(r.length - 1); - if (c >= 0xD800 && c <= 0xDBFF) { - this.lastNeed = 2; - this.lastTotal = 4; - this.lastChar[0] = buf[buf.length - 2]; - this.lastChar[1] = buf[buf.length - 1]; - return r.slice(0, -1); - } - } - return r; - } - this.lastNeed = 1; - this.lastTotal = 2; - this.lastChar[0] = buf[buf.length - 1]; - return buf.toString('utf16le', i, buf.length - 1); -} - -// For UTF-16LE we do not explicitly append special replacement characters if we -// end on a partial character, we simply let v8 handle that. -function utf16End(buf) { - var r = buf && buf.length ? this.write(buf) : ''; - if (this.lastNeed) { - var end = this.lastTotal - this.lastNeed; - return r + this.lastChar.toString('utf16le', 0, end); - } - return r; -} - -function base64Text(buf, i) { - var n = (buf.length - i) % 3; - if (n === 0) return buf.toString('base64', i); - this.lastNeed = 3 - n; - this.lastTotal = 3; - if (n === 1) { - this.lastChar[0] = buf[buf.length - 1]; - } else { - this.lastChar[0] = buf[buf.length - 2]; - this.lastChar[1] = buf[buf.length - 1]; - } - return buf.toString('base64', i, buf.length - n); -} - -function base64End(buf) { - var r = buf && buf.length ? this.write(buf) : ''; - if (this.lastNeed) return r + this.lastChar.toString('base64', 0, 3 - this.lastNeed); - return r; -} - -// Pass bytes on through for single-byte encodings (e.g. ascii, latin1, hex) -function simpleWrite(buf) { - return buf.toString(this.encoding); -} - -function simpleEnd(buf) { - return buf && buf.length ? this.write(buf) : ''; -} -},{"safe-buffer":217}],219:[function(require,module,exports){ -exports = module.exports = require('./lib/_stream_readable.js'); -exports.Stream = exports; -exports.Readable = exports; -exports.Writable = require('./lib/_stream_writable.js'); -exports.Duplex = require('./lib/_stream_duplex.js'); -exports.Transform = require('./lib/_stream_transform.js'); -exports.PassThrough = require('./lib/_stream_passthrough.js'); - -},{"./lib/_stream_duplex.js":209,"./lib/_stream_passthrough.js":210,"./lib/_stream_readable.js":211,"./lib/_stream_transform.js":212,"./lib/_stream_writable.js":213}],220:[function(require,module,exports){ -'use strict' -var Buffer = require('buffer').Buffer -var inherits = require('inherits') -var HashBase = require('hash-base') - -var ARRAY16 = new Array(16) - -var zl = [ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, - 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, - 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, - 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13 -] - -var zr = [ - 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, - 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, - 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, - 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, - 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11 -] - -var sl = [ - 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, - 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, - 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, - 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, - 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6 -] - -var sr = [ - 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, - 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, - 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, - 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, - 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11 -] - -var hl = [0x00000000, 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xa953fd4e] -var hr = [0x50a28be6, 0x5c4dd124, 0x6d703ef3, 0x7a6d76e9, 0x00000000] - -function RIPEMD160 () { - HashBase.call(this, 64) - - // state - this._a = 0x67452301 - this._b = 0xefcdab89 - this._c = 0x98badcfe - this._d = 0x10325476 - this._e = 0xc3d2e1f0 -} - -inherits(RIPEMD160, HashBase) - -RIPEMD160.prototype._update = function () { - var words = ARRAY16 - for (var j = 0; j < 16; ++j) words[j] = this._block.readInt32LE(j * 4) - - var al = this._a | 0 - var bl = this._b | 0 - var cl = this._c | 0 - var dl = this._d | 0 - var el = this._e | 0 - - var ar = this._a | 0 - var br = this._b | 0 - var cr = this._c | 0 - var dr = this._d | 0 - var er = this._e | 0 - - // computation - for (var i = 0; i < 80; i += 1) { - var tl - var tr - if (i < 16) { - tl = fn1(al, bl, cl, dl, el, words[zl[i]], hl[0], sl[i]) - tr = fn5(ar, br, cr, dr, er, words[zr[i]], hr[0], sr[i]) - } else if (i < 32) { - tl = fn2(al, bl, cl, dl, el, words[zl[i]], hl[1], sl[i]) - tr = fn4(ar, br, cr, dr, er, words[zr[i]], hr[1], sr[i]) - } else if (i < 48) { - tl = fn3(al, bl, cl, dl, el, words[zl[i]], hl[2], sl[i]) - tr = fn3(ar, br, cr, dr, er, words[zr[i]], hr[2], sr[i]) - } else if (i < 64) { - tl = fn4(al, bl, cl, dl, el, words[zl[i]], hl[3], sl[i]) - tr = fn2(ar, br, cr, dr, er, words[zr[i]], hr[3], sr[i]) - } else { // if (i<80) { - tl = fn5(al, bl, cl, dl, el, words[zl[i]], hl[4], sl[i]) - tr = fn1(ar, br, cr, dr, er, words[zr[i]], hr[4], sr[i]) - } - - al = el - el = dl - dl = rotl(cl, 10) - cl = bl - bl = tl - - ar = er - er = dr - dr = rotl(cr, 10) - cr = br - br = tr - } - - // update state - var t = (this._b + cl + dr) | 0 - this._b = (this._c + dl + er) | 0 - this._c = (this._d + el + ar) | 0 - this._d = (this._e + al + br) | 0 - this._e = (this._a + bl + cr) | 0 - this._a = t -} - -RIPEMD160.prototype._digest = function () { - // create padding and handle blocks - this._block[this._blockOffset++] = 0x80 - if (this._blockOffset > 56) { - this._block.fill(0, this._blockOffset, 64) - this._update() - this._blockOffset = 0 - } - - this._block.fill(0, this._blockOffset, 56) - this._block.writeUInt32LE(this._length[0], 56) - this._block.writeUInt32LE(this._length[1], 60) - this._update() - - // produce result - var buffer = Buffer.alloc ? Buffer.alloc(20) : new Buffer(20) - buffer.writeInt32LE(this._a, 0) - buffer.writeInt32LE(this._b, 4) - buffer.writeInt32LE(this._c, 8) - buffer.writeInt32LE(this._d, 12) - buffer.writeInt32LE(this._e, 16) - return buffer -} - -function rotl (x, n) { - return (x << n) | (x >>> (32 - n)) -} - -function fn1 (a, b, c, d, e, m, k, s) { - return (rotl((a + (b ^ c ^ d) + m + k) | 0, s) + e) | 0 -} - -function fn2 (a, b, c, d, e, m, k, s) { - return (rotl((a + ((b & c) | ((~b) & d)) + m + k) | 0, s) + e) | 0 -} - -function fn3 (a, b, c, d, e, m, k, s) { - return (rotl((a + ((b | (~c)) ^ d) + m + k) | 0, s) + e) | 0 -} - -function fn4 (a, b, c, d, e, m, k, s) { - return (rotl((a + ((b & d) | (c & (~d))) + m + k) | 0, s) + e) | 0 -} - -function fn5 (a, b, c, d, e, m, k, s) { - return (rotl((a + (b ^ (c | (~d))) + m + k) | 0, s) + e) | 0 -} - -module.exports = RIPEMD160 - -},{"buffer":63,"hash-base":139,"inherits":155}],221:[function(require,module,exports){ -/*! safe-buffer. MIT License. Feross Aboukhadijeh */ -/* eslint-disable node/no-deprecated-api */ -var buffer = require('buffer') -var Buffer = buffer.Buffer - -// alternative to using Object.keys for old browsers -function copyProps (src, dst) { - for (var key in src) { - dst[key] = src[key] - } -} -if (Buffer.from && Buffer.alloc && Buffer.allocUnsafe && Buffer.allocUnsafeSlow) { - module.exports = buffer -} else { - // Copy properties from require('buffer') - copyProps(buffer, exports) - exports.Buffer = SafeBuffer -} - -function SafeBuffer (arg, encodingOrOffset, length) { - return Buffer(arg, encodingOrOffset, length) -} - -SafeBuffer.prototype = Object.create(Buffer.prototype) - -// Copy static methods from Buffer -copyProps(Buffer, SafeBuffer) - -SafeBuffer.from = function (arg, encodingOrOffset, length) { - if (typeof arg === 'number') { - throw new TypeError('Argument must not be a number') - } - return Buffer(arg, encodingOrOffset, length) -} - -SafeBuffer.alloc = function (size, fill, encoding) { - if (typeof size !== 'number') { - throw new TypeError('Argument must be a number') - } - var buf = Buffer(size) - if (fill !== undefined) { - if (typeof encoding === 'string') { - buf.fill(fill, encoding) - } else { - buf.fill(fill) - } - } else { - buf.fill(0) - } - return buf -} - -SafeBuffer.allocUnsafe = function (size) { - if (typeof size !== 'number') { - throw new TypeError('Argument must be a number') - } - return Buffer(size) -} - -SafeBuffer.allocUnsafeSlow = function (size) { - if (typeof size !== 'number') { - throw new TypeError('Argument must be a number') - } - return buffer.SlowBuffer(size) -} - -},{"buffer":63}],222:[function(require,module,exports){ -(function (process){(function (){ -/* eslint-disable node/no-deprecated-api */ - -'use strict' - -var buffer = require('buffer') -var Buffer = buffer.Buffer - -var safer = {} - -var key - -for (key in buffer) { - if (!buffer.hasOwnProperty(key)) continue - if (key === 'SlowBuffer' || key === 'Buffer') continue - safer[key] = buffer[key] -} - -var Safer = safer.Buffer = {} -for (key in Buffer) { - if (!Buffer.hasOwnProperty(key)) continue - if (key === 'allocUnsafe' || key === 'allocUnsafeSlow') continue - Safer[key] = Buffer[key] -} - -safer.Buffer.prototype = Buffer.prototype - -if (!Safer.from || Safer.from === Uint8Array.from) { - Safer.from = function (value, encodingOrOffset, length) { - if (typeof value === 'number') { - throw new TypeError('The "value" argument must not be of type number. Received type ' + typeof value) - } - if (value && typeof value.length === 'undefined') { - throw new TypeError('The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type ' + typeof value) - } - return Buffer(value, encodingOrOffset, length) - } -} - -if (!Safer.alloc) { - Safer.alloc = function (size, fill, encoding) { - if (typeof size !== 'number') { - throw new TypeError('The "size" argument must be of type number. Received type ' + typeof size) - } - if (size < 0 || size >= 2 * (1 << 30)) { - throw new RangeError('The value "' + size + '" is invalid for option "size"') - } - var buf = Buffer(size) - if (!fill || fill.length === 0) { - buf.fill(0) - } else if (typeof encoding === 'string') { - buf.fill(fill, encoding) - } else { - buf.fill(fill) - } - return buf - } -} - -if (!safer.kStringMaxLength) { - try { - safer.kStringMaxLength = process.binding('buffer').kStringMaxLength - } catch (e) { - // we can't determine kStringMaxLength in environments where process.binding - // is unsupported, so let's not set it - } -} - -if (!safer.constants) { - safer.constants = { - MAX_LENGTH: safer.kMaxLength - } - if (safer.kStringMaxLength) { - safer.constants.MAX_STRING_LENGTH = safer.kStringMaxLength - } -} - -module.exports = safer - -}).call(this)}).call(this,require('_process')) -},{"_process":199,"buffer":63}],223:[function(require,module,exports){ -'use strict'; - -var GetIntrinsic = require('get-intrinsic'); -var define = require('define-data-property'); -var hasDescriptors = require('has-property-descriptors')(); -var gOPD = require('gopd'); - -var $TypeError = require('es-errors/type'); -var $floor = GetIntrinsic('%Math.floor%'); - -/** @type {import('.')} */ -module.exports = function setFunctionLength(fn, length) { - if (typeof fn !== 'function') { - throw new $TypeError('`fn` is not a function'); - } - if (typeof length !== 'number' || length < 0 || length > 0xFFFFFFFF || $floor(length) !== length) { - throw new $TypeError('`length` must be a positive 32-bit integer'); - } - - var loose = arguments.length > 2 && !!arguments[2]; - - var functionLengthIsConfigurable = true; - var functionLengthIsWritable = true; - if ('length' in fn && gOPD) { - var desc = gOPD(fn, 'length'); - if (desc && !desc.configurable) { - functionLengthIsConfigurable = false; - } - if (desc && !desc.writable) { - functionLengthIsWritable = false; - } - } - - if (functionLengthIsConfigurable || functionLengthIsWritable || !loose) { - if (hasDescriptors) { - define(/** @type {Parameters[0]} */ (fn), 'length', length, true, true); - } else { - define(/** @type {Parameters[0]} */ (fn), 'length', length); - } - } - return fn; -}; - -},{"define-data-property":92,"es-errors/type":127,"get-intrinsic":133,"gopd":134,"has-property-descriptors":135}],224:[function(require,module,exports){ -var Buffer = require('safe-buffer').Buffer - -// prototype class for hash functions -function Hash (blockSize, finalSize) { - this._block = Buffer.alloc(blockSize) - this._finalSize = finalSize - this._blockSize = blockSize - this._len = 0 -} - -Hash.prototype.update = function (data, enc) { - if (typeof data === 'string') { - enc = enc || 'utf8' - data = Buffer.from(data, enc) - } - - var block = this._block - var blockSize = this._blockSize - var length = data.length - var accum = this._len - - for (var offset = 0; offset < length;) { - var assigned = accum % blockSize - var remainder = Math.min(length - offset, blockSize - assigned) - - for (var i = 0; i < remainder; i++) { - block[assigned + i] = data[offset + i] - } - - accum += remainder - offset += remainder - - if ((accum % blockSize) === 0) { - this._update(block) - } - } - - this._len += length - return this -} - -Hash.prototype.digest = function (enc) { - var rem = this._len % this._blockSize - - this._block[rem] = 0x80 - - // zero (rem + 1) trailing bits, where (rem + 1) is the smallest - // non-negative solution to the equation (length + 1 + (rem + 1)) === finalSize mod blockSize - this._block.fill(0, rem + 1) - - if (rem >= this._finalSize) { - this._update(this._block) - this._block.fill(0) - } - - var bits = this._len * 8 - - // uint32 - if (bits <= 0xffffffff) { - this._block.writeUInt32BE(bits, this._blockSize - 4) - - // uint64 - } else { - var lowBits = (bits & 0xffffffff) >>> 0 - var highBits = (bits - lowBits) / 0x100000000 - - this._block.writeUInt32BE(highBits, this._blockSize - 8) - this._block.writeUInt32BE(lowBits, this._blockSize - 4) - } - - this._update(this._block) - var hash = this._hash() - - return enc ? hash.toString(enc) : hash -} - -Hash.prototype._update = function () { - throw new Error('_update must be implemented by subclass') -} - -module.exports = Hash - -},{"safe-buffer":221}],225:[function(require,module,exports){ -var exports = module.exports = function SHA (algorithm) { - algorithm = algorithm.toLowerCase() - - var Algorithm = exports[algorithm] - if (!Algorithm) throw new Error(algorithm + ' is not supported (we accept pull requests)') - - return new Algorithm() -} - -exports.sha = require('./sha') -exports.sha1 = require('./sha1') -exports.sha224 = require('./sha224') -exports.sha256 = require('./sha256') -exports.sha384 = require('./sha384') -exports.sha512 = require('./sha512') - -},{"./sha":226,"./sha1":227,"./sha224":228,"./sha256":229,"./sha384":230,"./sha512":231}],226:[function(require,module,exports){ -/* - * A JavaScript implementation of the Secure Hash Algorithm, SHA-0, as defined - * in FIPS PUB 180-1 - * This source code is derived from sha1.js of the same repository. - * The difference between SHA-0 and SHA-1 is just a bitwise rotate left - * operation was added. - */ - -var inherits = require('inherits') -var Hash = require('./hash') -var Buffer = require('safe-buffer').Buffer - -var K = [ - 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc | 0, 0xca62c1d6 | 0 -] - -var W = new Array(80) - -function Sha () { - this.init() - this._w = W - - Hash.call(this, 64, 56) -} - -inherits(Sha, Hash) - -Sha.prototype.init = function () { - this._a = 0x67452301 - this._b = 0xefcdab89 - this._c = 0x98badcfe - this._d = 0x10325476 - this._e = 0xc3d2e1f0 - - return this -} - -function rotl5 (num) { - return (num << 5) | (num >>> 27) -} - -function rotl30 (num) { - return (num << 30) | (num >>> 2) -} - -function ft (s, b, c, d) { - if (s === 0) return (b & c) | ((~b) & d) - if (s === 2) return (b & c) | (b & d) | (c & d) - return b ^ c ^ d -} - -Sha.prototype._update = function (M) { - var W = this._w - - var a = this._a | 0 - var b = this._b | 0 - var c = this._c | 0 - var d = this._d | 0 - var e = this._e | 0 - - for (var i = 0; i < 16; ++i) W[i] = M.readInt32BE(i * 4) - for (; i < 80; ++i) W[i] = W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16] - - for (var j = 0; j < 80; ++j) { - var s = ~~(j / 20) - var t = (rotl5(a) + ft(s, b, c, d) + e + W[j] + K[s]) | 0 - - e = d - d = c - c = rotl30(b) - b = a - a = t - } - - this._a = (a + this._a) | 0 - this._b = (b + this._b) | 0 - this._c = (c + this._c) | 0 - this._d = (d + this._d) | 0 - this._e = (e + this._e) | 0 -} - -Sha.prototype._hash = function () { - var H = Buffer.allocUnsafe(20) - - H.writeInt32BE(this._a | 0, 0) - H.writeInt32BE(this._b | 0, 4) - H.writeInt32BE(this._c | 0, 8) - H.writeInt32BE(this._d | 0, 12) - H.writeInt32BE(this._e | 0, 16) - - return H -} - -module.exports = Sha - -},{"./hash":224,"inherits":155,"safe-buffer":221}],227:[function(require,module,exports){ -/* - * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined - * in FIPS PUB 180-1 - * Version 2.1a Copyright Paul Johnston 2000 - 2002. - * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet - * Distributed under the BSD License - * See http://pajhome.org.uk/crypt/md5 for details. - */ - -var inherits = require('inherits') -var Hash = require('./hash') -var Buffer = require('safe-buffer').Buffer - -var K = [ - 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc | 0, 0xca62c1d6 | 0 -] - -var W = new Array(80) - -function Sha1 () { - this.init() - this._w = W - - Hash.call(this, 64, 56) -} - -inherits(Sha1, Hash) - -Sha1.prototype.init = function () { - this._a = 0x67452301 - this._b = 0xefcdab89 - this._c = 0x98badcfe - this._d = 0x10325476 - this._e = 0xc3d2e1f0 - - return this -} - -function rotl1 (num) { - return (num << 1) | (num >>> 31) -} - -function rotl5 (num) { - return (num << 5) | (num >>> 27) -} - -function rotl30 (num) { - return (num << 30) | (num >>> 2) -} - -function ft (s, b, c, d) { - if (s === 0) return (b & c) | ((~b) & d) - if (s === 2) return (b & c) | (b & d) | (c & d) - return b ^ c ^ d -} - -Sha1.prototype._update = function (M) { - var W = this._w - - var a = this._a | 0 - var b = this._b | 0 - var c = this._c | 0 - var d = this._d | 0 - var e = this._e | 0 - - for (var i = 0; i < 16; ++i) W[i] = M.readInt32BE(i * 4) - for (; i < 80; ++i) W[i] = rotl1(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16]) - - for (var j = 0; j < 80; ++j) { - var s = ~~(j / 20) - var t = (rotl5(a) + ft(s, b, c, d) + e + W[j] + K[s]) | 0 - - e = d - d = c - c = rotl30(b) - b = a - a = t - } - - this._a = (a + this._a) | 0 - this._b = (b + this._b) | 0 - this._c = (c + this._c) | 0 - this._d = (d + this._d) | 0 - this._e = (e + this._e) | 0 -} - -Sha1.prototype._hash = function () { - var H = Buffer.allocUnsafe(20) - - H.writeInt32BE(this._a | 0, 0) - H.writeInt32BE(this._b | 0, 4) - H.writeInt32BE(this._c | 0, 8) - H.writeInt32BE(this._d | 0, 12) - H.writeInt32BE(this._e | 0, 16) - - return H -} - -module.exports = Sha1 - -},{"./hash":224,"inherits":155,"safe-buffer":221}],228:[function(require,module,exports){ -/** - * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined - * in FIPS 180-2 - * Version 2.2-beta Copyright Angel Marin, Paul Johnston 2000 - 2009. - * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet - * - */ - -var inherits = require('inherits') -var Sha256 = require('./sha256') -var Hash = require('./hash') -var Buffer = require('safe-buffer').Buffer - -var W = new Array(64) - -function Sha224 () { - this.init() - - this._w = W // new Array(64) - - Hash.call(this, 64, 56) -} - -inherits(Sha224, Sha256) - -Sha224.prototype.init = function () { - this._a = 0xc1059ed8 - this._b = 0x367cd507 - this._c = 0x3070dd17 - this._d = 0xf70e5939 - this._e = 0xffc00b31 - this._f = 0x68581511 - this._g = 0x64f98fa7 - this._h = 0xbefa4fa4 - - return this -} - -Sha224.prototype._hash = function () { - var H = Buffer.allocUnsafe(28) - - H.writeInt32BE(this._a, 0) - H.writeInt32BE(this._b, 4) - H.writeInt32BE(this._c, 8) - H.writeInt32BE(this._d, 12) - H.writeInt32BE(this._e, 16) - H.writeInt32BE(this._f, 20) - H.writeInt32BE(this._g, 24) - - return H -} - -module.exports = Sha224 - -},{"./hash":224,"./sha256":229,"inherits":155,"safe-buffer":221}],229:[function(require,module,exports){ -/** - * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined - * in FIPS 180-2 - * Version 2.2-beta Copyright Angel Marin, Paul Johnston 2000 - 2009. - * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet - * - */ - -var inherits = require('inherits') -var Hash = require('./hash') -var Buffer = require('safe-buffer').Buffer - -var K = [ - 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, - 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, - 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, - 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, - 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, - 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, - 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, - 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967, - 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, - 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, - 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, - 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, - 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, - 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, - 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, - 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2 -] - -var W = new Array(64) - -function Sha256 () { - this.init() - - this._w = W // new Array(64) - - Hash.call(this, 64, 56) -} - -inherits(Sha256, Hash) - -Sha256.prototype.init = function () { - this._a = 0x6a09e667 - this._b = 0xbb67ae85 - this._c = 0x3c6ef372 - this._d = 0xa54ff53a - this._e = 0x510e527f - this._f = 0x9b05688c - this._g = 0x1f83d9ab - this._h = 0x5be0cd19 - - return this -} - -function ch (x, y, z) { - return z ^ (x & (y ^ z)) -} - -function maj (x, y, z) { - return (x & y) | (z & (x | y)) -} - -function sigma0 (x) { - return (x >>> 2 | x << 30) ^ (x >>> 13 | x << 19) ^ (x >>> 22 | x << 10) -} - -function sigma1 (x) { - return (x >>> 6 | x << 26) ^ (x >>> 11 | x << 21) ^ (x >>> 25 | x << 7) -} - -function gamma0 (x) { - return (x >>> 7 | x << 25) ^ (x >>> 18 | x << 14) ^ (x >>> 3) -} - -function gamma1 (x) { - return (x >>> 17 | x << 15) ^ (x >>> 19 | x << 13) ^ (x >>> 10) -} - -Sha256.prototype._update = function (M) { - var W = this._w - - var a = this._a | 0 - var b = this._b | 0 - var c = this._c | 0 - var d = this._d | 0 - var e = this._e | 0 - var f = this._f | 0 - var g = this._g | 0 - var h = this._h | 0 - - for (var i = 0; i < 16; ++i) W[i] = M.readInt32BE(i * 4) - for (; i < 64; ++i) W[i] = (gamma1(W[i - 2]) + W[i - 7] + gamma0(W[i - 15]) + W[i - 16]) | 0 - - for (var j = 0; j < 64; ++j) { - var T1 = (h + sigma1(e) + ch(e, f, g) + K[j] + W[j]) | 0 - var T2 = (sigma0(a) + maj(a, b, c)) | 0 - - h = g - g = f - f = e - e = (d + T1) | 0 - d = c - c = b - b = a - a = (T1 + T2) | 0 - } - - this._a = (a + this._a) | 0 - this._b = (b + this._b) | 0 - this._c = (c + this._c) | 0 - this._d = (d + this._d) | 0 - this._e = (e + this._e) | 0 - this._f = (f + this._f) | 0 - this._g = (g + this._g) | 0 - this._h = (h + this._h) | 0 -} - -Sha256.prototype._hash = function () { - var H = Buffer.allocUnsafe(32) - - H.writeInt32BE(this._a, 0) - H.writeInt32BE(this._b, 4) - H.writeInt32BE(this._c, 8) - H.writeInt32BE(this._d, 12) - H.writeInt32BE(this._e, 16) - H.writeInt32BE(this._f, 20) - H.writeInt32BE(this._g, 24) - H.writeInt32BE(this._h, 28) - - return H -} - -module.exports = Sha256 - -},{"./hash":224,"inherits":155,"safe-buffer":221}],230:[function(require,module,exports){ -var inherits = require('inherits') -var SHA512 = require('./sha512') -var Hash = require('./hash') -var Buffer = require('safe-buffer').Buffer - -var W = new Array(160) - -function Sha384 () { - this.init() - this._w = W - - Hash.call(this, 128, 112) -} - -inherits(Sha384, SHA512) - -Sha384.prototype.init = function () { - this._ah = 0xcbbb9d5d - this._bh = 0x629a292a - this._ch = 0x9159015a - this._dh = 0x152fecd8 - this._eh = 0x67332667 - this._fh = 0x8eb44a87 - this._gh = 0xdb0c2e0d - this._hh = 0x47b5481d - - this._al = 0xc1059ed8 - this._bl = 0x367cd507 - this._cl = 0x3070dd17 - this._dl = 0xf70e5939 - this._el = 0xffc00b31 - this._fl = 0x68581511 - this._gl = 0x64f98fa7 - this._hl = 0xbefa4fa4 - - return this -} - -Sha384.prototype._hash = function () { - var H = Buffer.allocUnsafe(48) - - function writeInt64BE (h, l, offset) { - H.writeInt32BE(h, offset) - H.writeInt32BE(l, offset + 4) - } - - writeInt64BE(this._ah, this._al, 0) - writeInt64BE(this._bh, this._bl, 8) - writeInt64BE(this._ch, this._cl, 16) - writeInt64BE(this._dh, this._dl, 24) - writeInt64BE(this._eh, this._el, 32) - writeInt64BE(this._fh, this._fl, 40) - - return H -} - -module.exports = Sha384 - -},{"./hash":224,"./sha512":231,"inherits":155,"safe-buffer":221}],231:[function(require,module,exports){ -var inherits = require('inherits') -var Hash = require('./hash') -var Buffer = require('safe-buffer').Buffer - -var K = [ - 0x428a2f98, 0xd728ae22, 0x71374491, 0x23ef65cd, - 0xb5c0fbcf, 0xec4d3b2f, 0xe9b5dba5, 0x8189dbbc, - 0x3956c25b, 0xf348b538, 0x59f111f1, 0xb605d019, - 0x923f82a4, 0xaf194f9b, 0xab1c5ed5, 0xda6d8118, - 0xd807aa98, 0xa3030242, 0x12835b01, 0x45706fbe, - 0x243185be, 0x4ee4b28c, 0x550c7dc3, 0xd5ffb4e2, - 0x72be5d74, 0xf27b896f, 0x80deb1fe, 0x3b1696b1, - 0x9bdc06a7, 0x25c71235, 0xc19bf174, 0xcf692694, - 0xe49b69c1, 0x9ef14ad2, 0xefbe4786, 0x384f25e3, - 0x0fc19dc6, 0x8b8cd5b5, 0x240ca1cc, 0x77ac9c65, - 0x2de92c6f, 0x592b0275, 0x4a7484aa, 0x6ea6e483, - 0x5cb0a9dc, 0xbd41fbd4, 0x76f988da, 0x831153b5, - 0x983e5152, 0xee66dfab, 0xa831c66d, 0x2db43210, - 0xb00327c8, 0x98fb213f, 0xbf597fc7, 0xbeef0ee4, - 0xc6e00bf3, 0x3da88fc2, 0xd5a79147, 0x930aa725, - 0x06ca6351, 0xe003826f, 0x14292967, 0x0a0e6e70, - 0x27b70a85, 0x46d22ffc, 0x2e1b2138, 0x5c26c926, - 0x4d2c6dfc, 0x5ac42aed, 0x53380d13, 0x9d95b3df, - 0x650a7354, 0x8baf63de, 0x766a0abb, 0x3c77b2a8, - 0x81c2c92e, 0x47edaee6, 0x92722c85, 0x1482353b, - 0xa2bfe8a1, 0x4cf10364, 0xa81a664b, 0xbc423001, - 0xc24b8b70, 0xd0f89791, 0xc76c51a3, 0x0654be30, - 0xd192e819, 0xd6ef5218, 0xd6990624, 0x5565a910, - 0xf40e3585, 0x5771202a, 0x106aa070, 0x32bbd1b8, - 0x19a4c116, 0xb8d2d0c8, 0x1e376c08, 0x5141ab53, - 0x2748774c, 0xdf8eeb99, 0x34b0bcb5, 0xe19b48a8, - 0x391c0cb3, 0xc5c95a63, 0x4ed8aa4a, 0xe3418acb, - 0x5b9cca4f, 0x7763e373, 0x682e6ff3, 0xd6b2b8a3, - 0x748f82ee, 0x5defb2fc, 0x78a5636f, 0x43172f60, - 0x84c87814, 0xa1f0ab72, 0x8cc70208, 0x1a6439ec, - 0x90befffa, 0x23631e28, 0xa4506ceb, 0xde82bde9, - 0xbef9a3f7, 0xb2c67915, 0xc67178f2, 0xe372532b, - 0xca273ece, 0xea26619c, 0xd186b8c7, 0x21c0c207, - 0xeada7dd6, 0xcde0eb1e, 0xf57d4f7f, 0xee6ed178, - 0x06f067aa, 0x72176fba, 0x0a637dc5, 0xa2c898a6, - 0x113f9804, 0xbef90dae, 0x1b710b35, 0x131c471b, - 0x28db77f5, 0x23047d84, 0x32caab7b, 0x40c72493, - 0x3c9ebe0a, 0x15c9bebc, 0x431d67c4, 0x9c100d4c, - 0x4cc5d4be, 0xcb3e42b6, 0x597f299c, 0xfc657e2a, - 0x5fcb6fab, 0x3ad6faec, 0x6c44198c, 0x4a475817 -] - -var W = new Array(160) - -function Sha512 () { - this.init() - this._w = W - - Hash.call(this, 128, 112) -} - -inherits(Sha512, Hash) - -Sha512.prototype.init = function () { - this._ah = 0x6a09e667 - this._bh = 0xbb67ae85 - this._ch = 0x3c6ef372 - this._dh = 0xa54ff53a - this._eh = 0x510e527f - this._fh = 0x9b05688c - this._gh = 0x1f83d9ab - this._hh = 0x5be0cd19 - - this._al = 0xf3bcc908 - this._bl = 0x84caa73b - this._cl = 0xfe94f82b - this._dl = 0x5f1d36f1 - this._el = 0xade682d1 - this._fl = 0x2b3e6c1f - this._gl = 0xfb41bd6b - this._hl = 0x137e2179 - - return this -} - -function Ch (x, y, z) { - return z ^ (x & (y ^ z)) -} - -function maj (x, y, z) { - return (x & y) | (z & (x | y)) -} - -function sigma0 (x, xl) { - return (x >>> 28 | xl << 4) ^ (xl >>> 2 | x << 30) ^ (xl >>> 7 | x << 25) -} - -function sigma1 (x, xl) { - return (x >>> 14 | xl << 18) ^ (x >>> 18 | xl << 14) ^ (xl >>> 9 | x << 23) -} - -function Gamma0 (x, xl) { - return (x >>> 1 | xl << 31) ^ (x >>> 8 | xl << 24) ^ (x >>> 7) -} - -function Gamma0l (x, xl) { - return (x >>> 1 | xl << 31) ^ (x >>> 8 | xl << 24) ^ (x >>> 7 | xl << 25) -} - -function Gamma1 (x, xl) { - return (x >>> 19 | xl << 13) ^ (xl >>> 29 | x << 3) ^ (x >>> 6) -} - -function Gamma1l (x, xl) { - return (x >>> 19 | xl << 13) ^ (xl >>> 29 | x << 3) ^ (x >>> 6 | xl << 26) -} - -function getCarry (a, b) { - return (a >>> 0) < (b >>> 0) ? 1 : 0 -} - -Sha512.prototype._update = function (M) { - var W = this._w - - var ah = this._ah | 0 - var bh = this._bh | 0 - var ch = this._ch | 0 - var dh = this._dh | 0 - var eh = this._eh | 0 - var fh = this._fh | 0 - var gh = this._gh | 0 - var hh = this._hh | 0 - - var al = this._al | 0 - var bl = this._bl | 0 - var cl = this._cl | 0 - var dl = this._dl | 0 - var el = this._el | 0 - var fl = this._fl | 0 - var gl = this._gl | 0 - var hl = this._hl | 0 - - for (var i = 0; i < 32; i += 2) { - W[i] = M.readInt32BE(i * 4) - W[i + 1] = M.readInt32BE(i * 4 + 4) - } - for (; i < 160; i += 2) { - var xh = W[i - 15 * 2] - var xl = W[i - 15 * 2 + 1] - var gamma0 = Gamma0(xh, xl) - var gamma0l = Gamma0l(xl, xh) - - xh = W[i - 2 * 2] - xl = W[i - 2 * 2 + 1] - var gamma1 = Gamma1(xh, xl) - var gamma1l = Gamma1l(xl, xh) - - // W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16] - var Wi7h = W[i - 7 * 2] - var Wi7l = W[i - 7 * 2 + 1] - - var Wi16h = W[i - 16 * 2] - var Wi16l = W[i - 16 * 2 + 1] - - var Wil = (gamma0l + Wi7l) | 0 - var Wih = (gamma0 + Wi7h + getCarry(Wil, gamma0l)) | 0 - Wil = (Wil + gamma1l) | 0 - Wih = (Wih + gamma1 + getCarry(Wil, gamma1l)) | 0 - Wil = (Wil + Wi16l) | 0 - Wih = (Wih + Wi16h + getCarry(Wil, Wi16l)) | 0 - - W[i] = Wih - W[i + 1] = Wil - } - - for (var j = 0; j < 160; j += 2) { - Wih = W[j] - Wil = W[j + 1] - - var majh = maj(ah, bh, ch) - var majl = maj(al, bl, cl) - - var sigma0h = sigma0(ah, al) - var sigma0l = sigma0(al, ah) - var sigma1h = sigma1(eh, el) - var sigma1l = sigma1(el, eh) - - // t1 = h + sigma1 + ch + K[j] + W[j] - var Kih = K[j] - var Kil = K[j + 1] - - var chh = Ch(eh, fh, gh) - var chl = Ch(el, fl, gl) - - var t1l = (hl + sigma1l) | 0 - var t1h = (hh + sigma1h + getCarry(t1l, hl)) | 0 - t1l = (t1l + chl) | 0 - t1h = (t1h + chh + getCarry(t1l, chl)) | 0 - t1l = (t1l + Kil) | 0 - t1h = (t1h + Kih + getCarry(t1l, Kil)) | 0 - t1l = (t1l + Wil) | 0 - t1h = (t1h + Wih + getCarry(t1l, Wil)) | 0 - - // t2 = sigma0 + maj - var t2l = (sigma0l + majl) | 0 - var t2h = (sigma0h + majh + getCarry(t2l, sigma0l)) | 0 - - hh = gh - hl = gl - gh = fh - gl = fl - fh = eh - fl = el - el = (dl + t1l) | 0 - eh = (dh + t1h + getCarry(el, dl)) | 0 - dh = ch - dl = cl - ch = bh - cl = bl - bh = ah - bl = al - al = (t1l + t2l) | 0 - ah = (t1h + t2h + getCarry(al, t1l)) | 0 - } - - this._al = (this._al + al) | 0 - this._bl = (this._bl + bl) | 0 - this._cl = (this._cl + cl) | 0 - this._dl = (this._dl + dl) | 0 - this._el = (this._el + el) | 0 - this._fl = (this._fl + fl) | 0 - this._gl = (this._gl + gl) | 0 - this._hl = (this._hl + hl) | 0 - - this._ah = (this._ah + ah + getCarry(this._al, al)) | 0 - this._bh = (this._bh + bh + getCarry(this._bl, bl)) | 0 - this._ch = (this._ch + ch + getCarry(this._cl, cl)) | 0 - this._dh = (this._dh + dh + getCarry(this._dl, dl)) | 0 - this._eh = (this._eh + eh + getCarry(this._el, el)) | 0 - this._fh = (this._fh + fh + getCarry(this._fl, fl)) | 0 - this._gh = (this._gh + gh + getCarry(this._gl, gl)) | 0 - this._hh = (this._hh + hh + getCarry(this._hl, hl)) | 0 -} - -Sha512.prototype._hash = function () { - var H = Buffer.allocUnsafe(64) - - function writeInt64BE (h, l, offset) { - H.writeInt32BE(h, offset) - H.writeInt32BE(l, offset + 4) - } - - writeInt64BE(this._ah, this._al, 0) - writeInt64BE(this._bh, this._bl, 8) - writeInt64BE(this._ch, this._cl, 16) - writeInt64BE(this._dh, this._dl, 24) - writeInt64BE(this._eh, this._el, 32) - writeInt64BE(this._fh, this._fl, 40) - writeInt64BE(this._gh, this._gl, 48) - writeInt64BE(this._hh, this._hl, 56) - - return H -} - -module.exports = Sha512 - -},{"./hash":224,"inherits":155,"safe-buffer":221}],232:[function(require,module,exports){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -module.exports = Stream; - -var EE = require('events').EventEmitter; -var inherits = require('inherits'); - -inherits(Stream, EE); -Stream.Readable = require('readable-stream/lib/_stream_readable.js'); -Stream.Writable = require('readable-stream/lib/_stream_writable.js'); -Stream.Duplex = require('readable-stream/lib/_stream_duplex.js'); -Stream.Transform = require('readable-stream/lib/_stream_transform.js'); -Stream.PassThrough = require('readable-stream/lib/_stream_passthrough.js'); -Stream.finished = require('readable-stream/lib/internal/streams/end-of-stream.js') -Stream.pipeline = require('readable-stream/lib/internal/streams/pipeline.js') - -// Backwards-compat with node 0.4.x -Stream.Stream = Stream; - - - -// old-style streams. Note that the pipe method (the only relevant -// part of this class) is overridden in the Readable class. - -function Stream() { - EE.call(this); -} - -Stream.prototype.pipe = function(dest, options) { - var source = this; - - function ondata(chunk) { - if (dest.writable) { - if (false === dest.write(chunk) && source.pause) { - source.pause(); - } - } - } - - source.on('data', ondata); - - function ondrain() { - if (source.readable && source.resume) { - source.resume(); - } - } - - dest.on('drain', ondrain); - - // If the 'end' option is not supplied, dest.end() will be called when - // source gets the 'end' or 'close' events. Only dest.end() once. - if (!dest._isStdio && (!options || options.end !== false)) { - source.on('end', onend); - source.on('close', onclose); - } - - var didOnEnd = false; - function onend() { - if (didOnEnd) return; - didOnEnd = true; - - dest.end(); - } - - - function onclose() { - if (didOnEnd) return; - didOnEnd = true; - - if (typeof dest.destroy === 'function') dest.destroy(); - } - - // don't leave dangling pipes when there are errors. - function onerror(er) { - cleanup(); - if (EE.listenerCount(this, 'error') === 0) { - throw er; // Unhandled stream error in pipe. - } - } - - source.on('error', onerror); - dest.on('error', onerror); - - // remove all the event listeners that were added. - function cleanup() { - source.removeListener('data', ondata); - dest.removeListener('drain', ondrain); - - source.removeListener('end', onend); - source.removeListener('close', onclose); - - source.removeListener('error', onerror); - dest.removeListener('error', onerror); - - source.removeListener('end', cleanup); - source.removeListener('close', cleanup); - - dest.removeListener('close', cleanup); - } - - source.on('end', cleanup); - source.on('close', cleanup); - - dest.on('close', cleanup); - - dest.emit('pipe', source); - - // Allow for unix-like usage: A.pipe(B).pipe(C) - return dest; -}; - -},{"events":129,"inherits":155,"readable-stream/lib/_stream_duplex.js":234,"readable-stream/lib/_stream_passthrough.js":235,"readable-stream/lib/_stream_readable.js":236,"readable-stream/lib/_stream_transform.js":237,"readable-stream/lib/_stream_writable.js":238,"readable-stream/lib/internal/streams/end-of-stream.js":242,"readable-stream/lib/internal/streams/pipeline.js":244}],233:[function(require,module,exports){ -'use strict'; - -function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } - -var codes = {}; - -function createErrorType(code, message, Base) { - if (!Base) { - Base = Error; - } - - function getMessage(arg1, arg2, arg3) { - if (typeof message === 'string') { - return message; - } else { - return message(arg1, arg2, arg3); - } - } - - var NodeError = - /*#__PURE__*/ - function (_Base) { - _inheritsLoose(NodeError, _Base); - - function NodeError(arg1, arg2, arg3) { - return _Base.call(this, getMessage(arg1, arg2, arg3)) || this; - } - - return NodeError; - }(Base); - - NodeError.prototype.name = Base.name; - NodeError.prototype.code = code; - codes[code] = NodeError; -} // https://github.com/nodejs/node/blob/v10.8.0/lib/internal/errors.js - - -function oneOf(expected, thing) { - if (Array.isArray(expected)) { - var len = expected.length; - expected = expected.map(function (i) { - return String(i); - }); - - if (len > 2) { - return "one of ".concat(thing, " ").concat(expected.slice(0, len - 1).join(', '), ", or ") + expected[len - 1]; - } else if (len === 2) { - return "one of ".concat(thing, " ").concat(expected[0], " or ").concat(expected[1]); - } else { - return "of ".concat(thing, " ").concat(expected[0]); - } - } else { - return "of ".concat(thing, " ").concat(String(expected)); - } -} // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith - - -function startsWith(str, search, pos) { - return str.substr(!pos || pos < 0 ? 0 : +pos, search.length) === search; -} // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith - - -function endsWith(str, search, this_len) { - if (this_len === undefined || this_len > str.length) { - this_len = str.length; - } - - return str.substring(this_len - search.length, this_len) === search; -} // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes - - -function includes(str, search, start) { - if (typeof start !== 'number') { - start = 0; - } - - if (start + search.length > str.length) { - return false; - } else { - return str.indexOf(search, start) !== -1; - } -} - -createErrorType('ERR_INVALID_OPT_VALUE', function (name, value) { - return 'The value "' + value + '" is invalid for option "' + name + '"'; -}, TypeError); -createErrorType('ERR_INVALID_ARG_TYPE', function (name, expected, actual) { - // determiner: 'must be' or 'must not be' - var determiner; - - if (typeof expected === 'string' && startsWith(expected, 'not ')) { - determiner = 'must not be'; - expected = expected.replace(/^not /, ''); - } else { - determiner = 'must be'; - } - - var msg; - - if (endsWith(name, ' argument')) { - // For cases like 'first argument' - msg = "The ".concat(name, " ").concat(determiner, " ").concat(oneOf(expected, 'type')); - } else { - var type = includes(name, '.') ? 'property' : 'argument'; - msg = "The \"".concat(name, "\" ").concat(type, " ").concat(determiner, " ").concat(oneOf(expected, 'type')); - } - - msg += ". Received type ".concat(typeof actual); - return msg; -}, TypeError); -createErrorType('ERR_STREAM_PUSH_AFTER_EOF', 'stream.push() after EOF'); -createErrorType('ERR_METHOD_NOT_IMPLEMENTED', function (name) { - return 'The ' + name + ' method is not implemented'; -}); -createErrorType('ERR_STREAM_PREMATURE_CLOSE', 'Premature close'); -createErrorType('ERR_STREAM_DESTROYED', function (name) { - return 'Cannot call ' + name + ' after a stream was destroyed'; -}); -createErrorType('ERR_MULTIPLE_CALLBACK', 'Callback called multiple times'); -createErrorType('ERR_STREAM_CANNOT_PIPE', 'Cannot pipe, not readable'); -createErrorType('ERR_STREAM_WRITE_AFTER_END', 'write after end'); -createErrorType('ERR_STREAM_NULL_VALUES', 'May not write null values to stream', TypeError); -createErrorType('ERR_UNKNOWN_ENCODING', function (arg) { - return 'Unknown encoding: ' + arg; -}, TypeError); -createErrorType('ERR_STREAM_UNSHIFT_AFTER_END_EVENT', 'stream.unshift() after end event'); -module.exports.codes = codes; - -},{}],234:[function(require,module,exports){ -(function (process){(function (){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// a duplex stream is just a stream that is both readable and writable. -// Since JS doesn't have multiple prototypal inheritance, this class -// prototypally inherits from Readable, and then parasitically from -// Writable. - -'use strict'; - -/**/ -var objectKeys = Object.keys || function (obj) { - var keys = []; - for (var key in obj) keys.push(key); - return keys; -}; -/**/ - -module.exports = Duplex; -var Readable = require('./_stream_readable'); -var Writable = require('./_stream_writable'); -require('inherits')(Duplex, Readable); -{ - // Allow the keys array to be GC'ed. - var keys = objectKeys(Writable.prototype); - for (var v = 0; v < keys.length; v++) { - var method = keys[v]; - if (!Duplex.prototype[method]) Duplex.prototype[method] = Writable.prototype[method]; - } -} -function Duplex(options) { - if (!(this instanceof Duplex)) return new Duplex(options); - Readable.call(this, options); - Writable.call(this, options); - this.allowHalfOpen = true; - if (options) { - if (options.readable === false) this.readable = false; - if (options.writable === false) this.writable = false; - if (options.allowHalfOpen === false) { - this.allowHalfOpen = false; - this.once('end', onend); - } - } -} -Object.defineProperty(Duplex.prototype, 'writableHighWaterMark', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._writableState.highWaterMark; - } -}); -Object.defineProperty(Duplex.prototype, 'writableBuffer', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._writableState && this._writableState.getBuffer(); - } -}); -Object.defineProperty(Duplex.prototype, 'writableLength', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._writableState.length; - } -}); - -// the no-half-open enforcer -function onend() { - // If the writable side ended, then we're ok. - if (this._writableState.ended) return; - - // no more data can be written. - // But allow more writes to happen in this tick. - process.nextTick(onEndNT, this); -} -function onEndNT(self) { - self.end(); -} -Object.defineProperty(Duplex.prototype, 'destroyed', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - if (this._readableState === undefined || this._writableState === undefined) { - return false; - } - return this._readableState.destroyed && this._writableState.destroyed; - }, - set: function set(value) { - // we ignore the value if the stream - // has not been initialized yet - if (this._readableState === undefined || this._writableState === undefined) { - return; - } - - // backward compatibility, the user is explicitly - // managing destroyed - this._readableState.destroyed = value; - this._writableState.destroyed = value; - } -}); -}).call(this)}).call(this,require('_process')) -},{"./_stream_readable":236,"./_stream_writable":238,"_process":199,"inherits":155}],235:[function(require,module,exports){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// a passthrough stream. -// basically just the most minimal sort of Transform stream. -// Every written chunk gets output as-is. - -'use strict'; - -module.exports = PassThrough; -var Transform = require('./_stream_transform'); -require('inherits')(PassThrough, Transform); -function PassThrough(options) { - if (!(this instanceof PassThrough)) return new PassThrough(options); - Transform.call(this, options); -} -PassThrough.prototype._transform = function (chunk, encoding, cb) { - cb(null, chunk); -}; -},{"./_stream_transform":237,"inherits":155}],236:[function(require,module,exports){ -(function (process,global){(function (){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -'use strict'; - -module.exports = Readable; - -/**/ -var Duplex; -/**/ - -Readable.ReadableState = ReadableState; - -/**/ -var EE = require('events').EventEmitter; -var EElistenerCount = function EElistenerCount(emitter, type) { - return emitter.listeners(type).length; -}; -/**/ - -/**/ -var Stream = require('./internal/streams/stream'); -/**/ - -var Buffer = require('buffer').Buffer; -var OurUint8Array = (typeof global !== 'undefined' ? global : typeof window !== 'undefined' ? window : typeof self !== 'undefined' ? self : {}).Uint8Array || function () {}; -function _uint8ArrayToBuffer(chunk) { - return Buffer.from(chunk); -} -function _isUint8Array(obj) { - return Buffer.isBuffer(obj) || obj instanceof OurUint8Array; -} - -/**/ -var debugUtil = require('util'); -var debug; -if (debugUtil && debugUtil.debuglog) { - debug = debugUtil.debuglog('stream'); -} else { - debug = function debug() {}; -} -/**/ - -var BufferList = require('./internal/streams/buffer_list'); -var destroyImpl = require('./internal/streams/destroy'); -var _require = require('./internal/streams/state'), - getHighWaterMark = _require.getHighWaterMark; -var _require$codes = require('../errors').codes, - ERR_INVALID_ARG_TYPE = _require$codes.ERR_INVALID_ARG_TYPE, - ERR_STREAM_PUSH_AFTER_EOF = _require$codes.ERR_STREAM_PUSH_AFTER_EOF, - ERR_METHOD_NOT_IMPLEMENTED = _require$codes.ERR_METHOD_NOT_IMPLEMENTED, - ERR_STREAM_UNSHIFT_AFTER_END_EVENT = _require$codes.ERR_STREAM_UNSHIFT_AFTER_END_EVENT; - -// Lazy loaded to improve the startup performance. -var StringDecoder; -var createReadableStreamAsyncIterator; -var from; -require('inherits')(Readable, Stream); -var errorOrDestroy = destroyImpl.errorOrDestroy; -var kProxyEvents = ['error', 'close', 'destroy', 'pause', 'resume']; -function prependListener(emitter, event, fn) { - // Sadly this is not cacheable as some libraries bundle their own - // event emitter implementation with them. - if (typeof emitter.prependListener === 'function') return emitter.prependListener(event, fn); - - // This is a hack to make sure that our error handler is attached before any - // userland ones. NEVER DO THIS. This is here only because this code needs - // to continue to work with older versions of Node.js that do not include - // the prependListener() method. The goal is to eventually remove this hack. - if (!emitter._events || !emitter._events[event]) emitter.on(event, fn);else if (Array.isArray(emitter._events[event])) emitter._events[event].unshift(fn);else emitter._events[event] = [fn, emitter._events[event]]; -} -function ReadableState(options, stream, isDuplex) { - Duplex = Duplex || require('./_stream_duplex'); - options = options || {}; - - // Duplex streams are both readable and writable, but share - // the same options object. - // However, some cases require setting options to different - // values for the readable and the writable sides of the duplex stream. - // These options can be provided separately as readableXXX and writableXXX. - if (typeof isDuplex !== 'boolean') isDuplex = stream instanceof Duplex; - - // object stream flag. Used to make read(n) ignore n and to - // make all the buffer merging and length checks go away - this.objectMode = !!options.objectMode; - if (isDuplex) this.objectMode = this.objectMode || !!options.readableObjectMode; - - // the point at which it stops calling _read() to fill the buffer - // Note: 0 is a valid value, means "don't call _read preemptively ever" - this.highWaterMark = getHighWaterMark(this, options, 'readableHighWaterMark', isDuplex); - - // A linked list is used to store data chunks instead of an array because the - // linked list can remove elements from the beginning faster than - // array.shift() - this.buffer = new BufferList(); - this.length = 0; - this.pipes = null; - this.pipesCount = 0; - this.flowing = null; - this.ended = false; - this.endEmitted = false; - this.reading = false; - - // a flag to be able to tell if the event 'readable'/'data' is emitted - // immediately, or on a later tick. We set this to true at first, because - // any actions that shouldn't happen until "later" should generally also - // not happen before the first read call. - this.sync = true; - - // whenever we return null, then we set a flag to say - // that we're awaiting a 'readable' event emission. - this.needReadable = false; - this.emittedReadable = false; - this.readableListening = false; - this.resumeScheduled = false; - this.paused = true; - - // Should close be emitted on destroy. Defaults to true. - this.emitClose = options.emitClose !== false; - - // Should .destroy() be called after 'end' (and potentially 'finish') - this.autoDestroy = !!options.autoDestroy; - - // has it been destroyed - this.destroyed = false; - - // Crypto is kind of old and crusty. Historically, its default string - // encoding is 'binary' so we have to make this configurable. - // Everything else in the universe uses 'utf8', though. - this.defaultEncoding = options.defaultEncoding || 'utf8'; - - // the number of writers that are awaiting a drain event in .pipe()s - this.awaitDrain = 0; - - // if true, a maybeReadMore has been scheduled - this.readingMore = false; - this.decoder = null; - this.encoding = null; - if (options.encoding) { - if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder; - this.decoder = new StringDecoder(options.encoding); - this.encoding = options.encoding; - } -} -function Readable(options) { - Duplex = Duplex || require('./_stream_duplex'); - if (!(this instanceof Readable)) return new Readable(options); - - // Checking for a Stream.Duplex instance is faster here instead of inside - // the ReadableState constructor, at least with V8 6.5 - var isDuplex = this instanceof Duplex; - this._readableState = new ReadableState(options, this, isDuplex); - - // legacy - this.readable = true; - if (options) { - if (typeof options.read === 'function') this._read = options.read; - if (typeof options.destroy === 'function') this._destroy = options.destroy; - } - Stream.call(this); -} -Object.defineProperty(Readable.prototype, 'destroyed', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - if (this._readableState === undefined) { - return false; - } - return this._readableState.destroyed; - }, - set: function set(value) { - // we ignore the value if the stream - // has not been initialized yet - if (!this._readableState) { - return; - } - - // backward compatibility, the user is explicitly - // managing destroyed - this._readableState.destroyed = value; - } -}); -Readable.prototype.destroy = destroyImpl.destroy; -Readable.prototype._undestroy = destroyImpl.undestroy; -Readable.prototype._destroy = function (err, cb) { - cb(err); -}; - -// Manually shove something into the read() buffer. -// This returns true if the highWaterMark has not been hit yet, -// similar to how Writable.write() returns true if you should -// write() some more. -Readable.prototype.push = function (chunk, encoding) { - var state = this._readableState; - var skipChunkCheck; - if (!state.objectMode) { - if (typeof chunk === 'string') { - encoding = encoding || state.defaultEncoding; - if (encoding !== state.encoding) { - chunk = Buffer.from(chunk, encoding); - encoding = ''; - } - skipChunkCheck = true; - } - } else { - skipChunkCheck = true; - } - return readableAddChunk(this, chunk, encoding, false, skipChunkCheck); -}; - -// Unshift should *always* be something directly out of read() -Readable.prototype.unshift = function (chunk) { - return readableAddChunk(this, chunk, null, true, false); -}; -function readableAddChunk(stream, chunk, encoding, addToFront, skipChunkCheck) { - debug('readableAddChunk', chunk); - var state = stream._readableState; - if (chunk === null) { - state.reading = false; - onEofChunk(stream, state); - } else { - var er; - if (!skipChunkCheck) er = chunkInvalid(state, chunk); - if (er) { - errorOrDestroy(stream, er); - } else if (state.objectMode || chunk && chunk.length > 0) { - if (typeof chunk !== 'string' && !state.objectMode && Object.getPrototypeOf(chunk) !== Buffer.prototype) { - chunk = _uint8ArrayToBuffer(chunk); - } - if (addToFront) { - if (state.endEmitted) errorOrDestroy(stream, new ERR_STREAM_UNSHIFT_AFTER_END_EVENT());else addChunk(stream, state, chunk, true); - } else if (state.ended) { - errorOrDestroy(stream, new ERR_STREAM_PUSH_AFTER_EOF()); - } else if (state.destroyed) { - return false; - } else { - state.reading = false; - if (state.decoder && !encoding) { - chunk = state.decoder.write(chunk); - if (state.objectMode || chunk.length !== 0) addChunk(stream, state, chunk, false);else maybeReadMore(stream, state); - } else { - addChunk(stream, state, chunk, false); - } - } - } else if (!addToFront) { - state.reading = false; - maybeReadMore(stream, state); - } - } - - // We can push more data if we are below the highWaterMark. - // Also, if we have no data yet, we can stand some more bytes. - // This is to work around cases where hwm=0, such as the repl. - return !state.ended && (state.length < state.highWaterMark || state.length === 0); -} -function addChunk(stream, state, chunk, addToFront) { - if (state.flowing && state.length === 0 && !state.sync) { - state.awaitDrain = 0; - stream.emit('data', chunk); - } else { - // update the buffer info. - state.length += state.objectMode ? 1 : chunk.length; - if (addToFront) state.buffer.unshift(chunk);else state.buffer.push(chunk); - if (state.needReadable) emitReadable(stream); - } - maybeReadMore(stream, state); -} -function chunkInvalid(state, chunk) { - var er; - if (!_isUint8Array(chunk) && typeof chunk !== 'string' && chunk !== undefined && !state.objectMode) { - er = new ERR_INVALID_ARG_TYPE('chunk', ['string', 'Buffer', 'Uint8Array'], chunk); - } - return er; -} -Readable.prototype.isPaused = function () { - return this._readableState.flowing === false; -}; - -// backwards compatibility. -Readable.prototype.setEncoding = function (enc) { - if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder; - var decoder = new StringDecoder(enc); - this._readableState.decoder = decoder; - // If setEncoding(null), decoder.encoding equals utf8 - this._readableState.encoding = this._readableState.decoder.encoding; - - // Iterate over current buffer to convert already stored Buffers: - var p = this._readableState.buffer.head; - var content = ''; - while (p !== null) { - content += decoder.write(p.data); - p = p.next; - } - this._readableState.buffer.clear(); - if (content !== '') this._readableState.buffer.push(content); - this._readableState.length = content.length; - return this; -}; - -// Don't raise the hwm > 1GB -var MAX_HWM = 0x40000000; -function computeNewHighWaterMark(n) { - if (n >= MAX_HWM) { - // TODO(ronag): Throw ERR_VALUE_OUT_OF_RANGE. - n = MAX_HWM; - } else { - // Get the next highest power of 2 to prevent increasing hwm excessively in - // tiny amounts - n--; - n |= n >>> 1; - n |= n >>> 2; - n |= n >>> 4; - n |= n >>> 8; - n |= n >>> 16; - n++; - } - return n; -} - -// This function is designed to be inlinable, so please take care when making -// changes to the function body. -function howMuchToRead(n, state) { - if (n <= 0 || state.length === 0 && state.ended) return 0; - if (state.objectMode) return 1; - if (n !== n) { - // Only flow one buffer at a time - if (state.flowing && state.length) return state.buffer.head.data.length;else return state.length; - } - // If we're asking for more than the current hwm, then raise the hwm. - if (n > state.highWaterMark) state.highWaterMark = computeNewHighWaterMark(n); - if (n <= state.length) return n; - // Don't have enough - if (!state.ended) { - state.needReadable = true; - return 0; - } - return state.length; -} - -// you can override either this method, or the async _read(n) below. -Readable.prototype.read = function (n) { - debug('read', n); - n = parseInt(n, 10); - var state = this._readableState; - var nOrig = n; - if (n !== 0) state.emittedReadable = false; - - // if we're doing read(0) to trigger a readable event, but we - // already have a bunch of data in the buffer, then just trigger - // the 'readable' event and move on. - if (n === 0 && state.needReadable && ((state.highWaterMark !== 0 ? state.length >= state.highWaterMark : state.length > 0) || state.ended)) { - debug('read: emitReadable', state.length, state.ended); - if (state.length === 0 && state.ended) endReadable(this);else emitReadable(this); - return null; - } - n = howMuchToRead(n, state); - - // if we've ended, and we're now clear, then finish it up. - if (n === 0 && state.ended) { - if (state.length === 0) endReadable(this); - return null; - } - - // All the actual chunk generation logic needs to be - // *below* the call to _read. The reason is that in certain - // synthetic stream cases, such as passthrough streams, _read - // may be a completely synchronous operation which may change - // the state of the read buffer, providing enough data when - // before there was *not* enough. - // - // So, the steps are: - // 1. Figure out what the state of things will be after we do - // a read from the buffer. - // - // 2. If that resulting state will trigger a _read, then call _read. - // Note that this may be asynchronous, or synchronous. Yes, it is - // deeply ugly to write APIs this way, but that still doesn't mean - // that the Readable class should behave improperly, as streams are - // designed to be sync/async agnostic. - // Take note if the _read call is sync or async (ie, if the read call - // has returned yet), so that we know whether or not it's safe to emit - // 'readable' etc. - // - // 3. Actually pull the requested chunks out of the buffer and return. - - // if we need a readable event, then we need to do some reading. - var doRead = state.needReadable; - debug('need readable', doRead); - - // if we currently have less than the highWaterMark, then also read some - if (state.length === 0 || state.length - n < state.highWaterMark) { - doRead = true; - debug('length less than watermark', doRead); - } - - // however, if we've ended, then there's no point, and if we're already - // reading, then it's unnecessary. - if (state.ended || state.reading) { - doRead = false; - debug('reading or ended', doRead); - } else if (doRead) { - debug('do read'); - state.reading = true; - state.sync = true; - // if the length is currently zero, then we *need* a readable event. - if (state.length === 0) state.needReadable = true; - // call internal read method - this._read(state.highWaterMark); - state.sync = false; - // If _read pushed data synchronously, then `reading` will be false, - // and we need to re-evaluate how much data we can return to the user. - if (!state.reading) n = howMuchToRead(nOrig, state); - } - var ret; - if (n > 0) ret = fromList(n, state);else ret = null; - if (ret === null) { - state.needReadable = state.length <= state.highWaterMark; - n = 0; - } else { - state.length -= n; - state.awaitDrain = 0; - } - if (state.length === 0) { - // If we have nothing in the buffer, then we want to know - // as soon as we *do* get something into the buffer. - if (!state.ended) state.needReadable = true; - - // If we tried to read() past the EOF, then emit end on the next tick. - if (nOrig !== n && state.ended) endReadable(this); - } - if (ret !== null) this.emit('data', ret); - return ret; -}; -function onEofChunk(stream, state) { - debug('onEofChunk'); - if (state.ended) return; - if (state.decoder) { - var chunk = state.decoder.end(); - if (chunk && chunk.length) { - state.buffer.push(chunk); - state.length += state.objectMode ? 1 : chunk.length; - } - } - state.ended = true; - if (state.sync) { - // if we are sync, wait until next tick to emit the data. - // Otherwise we risk emitting data in the flow() - // the readable code triggers during a read() call - emitReadable(stream); - } else { - // emit 'readable' now to make sure it gets picked up. - state.needReadable = false; - if (!state.emittedReadable) { - state.emittedReadable = true; - emitReadable_(stream); - } - } -} - -// Don't emit readable right away in sync mode, because this can trigger -// another read() call => stack overflow. This way, it might trigger -// a nextTick recursion warning, but that's not so bad. -function emitReadable(stream) { - var state = stream._readableState; - debug('emitReadable', state.needReadable, state.emittedReadable); - state.needReadable = false; - if (!state.emittedReadable) { - debug('emitReadable', state.flowing); - state.emittedReadable = true; - process.nextTick(emitReadable_, stream); - } -} -function emitReadable_(stream) { - var state = stream._readableState; - debug('emitReadable_', state.destroyed, state.length, state.ended); - if (!state.destroyed && (state.length || state.ended)) { - stream.emit('readable'); - state.emittedReadable = false; - } - - // The stream needs another readable event if - // 1. It is not flowing, as the flow mechanism will take - // care of it. - // 2. It is not ended. - // 3. It is below the highWaterMark, so we can schedule - // another readable later. - state.needReadable = !state.flowing && !state.ended && state.length <= state.highWaterMark; - flow(stream); -} - -// at this point, the user has presumably seen the 'readable' event, -// and called read() to consume some data. that may have triggered -// in turn another _read(n) call, in which case reading = true if -// it's in progress. -// However, if we're not ended, or reading, and the length < hwm, -// then go ahead and try to read some more preemptively. -function maybeReadMore(stream, state) { - if (!state.readingMore) { - state.readingMore = true; - process.nextTick(maybeReadMore_, stream, state); - } -} -function maybeReadMore_(stream, state) { - // Attempt to read more data if we should. - // - // The conditions for reading more data are (one of): - // - Not enough data buffered (state.length < state.highWaterMark). The loop - // is responsible for filling the buffer with enough data if such data - // is available. If highWaterMark is 0 and we are not in the flowing mode - // we should _not_ attempt to buffer any extra data. We'll get more data - // when the stream consumer calls read() instead. - // - No data in the buffer, and the stream is in flowing mode. In this mode - // the loop below is responsible for ensuring read() is called. Failing to - // call read here would abort the flow and there's no other mechanism for - // continuing the flow if the stream consumer has just subscribed to the - // 'data' event. - // - // In addition to the above conditions to keep reading data, the following - // conditions prevent the data from being read: - // - The stream has ended (state.ended). - // - There is already a pending 'read' operation (state.reading). This is a - // case where the the stream has called the implementation defined _read() - // method, but they are processing the call asynchronously and have _not_ - // called push() with new data. In this case we skip performing more - // read()s. The execution ends in this method again after the _read() ends - // up calling push() with more data. - while (!state.reading && !state.ended && (state.length < state.highWaterMark || state.flowing && state.length === 0)) { - var len = state.length; - debug('maybeReadMore read 0'); - stream.read(0); - if (len === state.length) - // didn't get any data, stop spinning. - break; - } - state.readingMore = false; -} - -// abstract method. to be overridden in specific implementation classes. -// call cb(er, data) where data is <= n in length. -// for virtual (non-string, non-buffer) streams, "length" is somewhat -// arbitrary, and perhaps not very meaningful. -Readable.prototype._read = function (n) { - errorOrDestroy(this, new ERR_METHOD_NOT_IMPLEMENTED('_read()')); -}; -Readable.prototype.pipe = function (dest, pipeOpts) { - var src = this; - var state = this._readableState; - switch (state.pipesCount) { - case 0: - state.pipes = dest; - break; - case 1: - state.pipes = [state.pipes, dest]; - break; - default: - state.pipes.push(dest); - break; - } - state.pipesCount += 1; - debug('pipe count=%d opts=%j', state.pipesCount, pipeOpts); - var doEnd = (!pipeOpts || pipeOpts.end !== false) && dest !== process.stdout && dest !== process.stderr; - var endFn = doEnd ? onend : unpipe; - if (state.endEmitted) process.nextTick(endFn);else src.once('end', endFn); - dest.on('unpipe', onunpipe); - function onunpipe(readable, unpipeInfo) { - debug('onunpipe'); - if (readable === src) { - if (unpipeInfo && unpipeInfo.hasUnpiped === false) { - unpipeInfo.hasUnpiped = true; - cleanup(); - } - } - } - function onend() { - debug('onend'); - dest.end(); - } - - // when the dest drains, it reduces the awaitDrain counter - // on the source. This would be more elegant with a .once() - // handler in flow(), but adding and removing repeatedly is - // too slow. - var ondrain = pipeOnDrain(src); - dest.on('drain', ondrain); - var cleanedUp = false; - function cleanup() { - debug('cleanup'); - // cleanup event handlers once the pipe is broken - dest.removeListener('close', onclose); - dest.removeListener('finish', onfinish); - dest.removeListener('drain', ondrain); - dest.removeListener('error', onerror); - dest.removeListener('unpipe', onunpipe); - src.removeListener('end', onend); - src.removeListener('end', unpipe); - src.removeListener('data', ondata); - cleanedUp = true; - - // if the reader is waiting for a drain event from this - // specific writer, then it would cause it to never start - // flowing again. - // So, if this is awaiting a drain, then we just call it now. - // If we don't know, then assume that we are waiting for one. - if (state.awaitDrain && (!dest._writableState || dest._writableState.needDrain)) ondrain(); - } - src.on('data', ondata); - function ondata(chunk) { - debug('ondata'); - var ret = dest.write(chunk); - debug('dest.write', ret); - if (ret === false) { - // If the user unpiped during `dest.write()`, it is possible - // to get stuck in a permanently paused state if that write - // also returned false. - // => Check whether `dest` is still a piping destination. - if ((state.pipesCount === 1 && state.pipes === dest || state.pipesCount > 1 && indexOf(state.pipes, dest) !== -1) && !cleanedUp) { - debug('false write response, pause', state.awaitDrain); - state.awaitDrain++; - } - src.pause(); - } - } - - // if the dest has an error, then stop piping into it. - // however, don't suppress the throwing behavior for this. - function onerror(er) { - debug('onerror', er); - unpipe(); - dest.removeListener('error', onerror); - if (EElistenerCount(dest, 'error') === 0) errorOrDestroy(dest, er); - } - - // Make sure our error handler is attached before userland ones. - prependListener(dest, 'error', onerror); - - // Both close and finish should trigger unpipe, but only once. - function onclose() { - dest.removeListener('finish', onfinish); - unpipe(); - } - dest.once('close', onclose); - function onfinish() { - debug('onfinish'); - dest.removeListener('close', onclose); - unpipe(); - } - dest.once('finish', onfinish); - function unpipe() { - debug('unpipe'); - src.unpipe(dest); - } - - // tell the dest that it's being piped to - dest.emit('pipe', src); - - // start the flow if it hasn't been started already. - if (!state.flowing) { - debug('pipe resume'); - src.resume(); - } - return dest; -}; -function pipeOnDrain(src) { - return function pipeOnDrainFunctionResult() { - var state = src._readableState; - debug('pipeOnDrain', state.awaitDrain); - if (state.awaitDrain) state.awaitDrain--; - if (state.awaitDrain === 0 && EElistenerCount(src, 'data')) { - state.flowing = true; - flow(src); - } - }; -} -Readable.prototype.unpipe = function (dest) { - var state = this._readableState; - var unpipeInfo = { - hasUnpiped: false - }; - - // if we're not piping anywhere, then do nothing. - if (state.pipesCount === 0) return this; - - // just one destination. most common case. - if (state.pipesCount === 1) { - // passed in one, but it's not the right one. - if (dest && dest !== state.pipes) return this; - if (!dest) dest = state.pipes; - - // got a match. - state.pipes = null; - state.pipesCount = 0; - state.flowing = false; - if (dest) dest.emit('unpipe', this, unpipeInfo); - return this; - } - - // slow case. multiple pipe destinations. - - if (!dest) { - // remove all. - var dests = state.pipes; - var len = state.pipesCount; - state.pipes = null; - state.pipesCount = 0; - state.flowing = false; - for (var i = 0; i < len; i++) dests[i].emit('unpipe', this, { - hasUnpiped: false - }); - return this; - } - - // try to find the right one. - var index = indexOf(state.pipes, dest); - if (index === -1) return this; - state.pipes.splice(index, 1); - state.pipesCount -= 1; - if (state.pipesCount === 1) state.pipes = state.pipes[0]; - dest.emit('unpipe', this, unpipeInfo); - return this; -}; - -// set up data events if they are asked for -// Ensure readable listeners eventually get something -Readable.prototype.on = function (ev, fn) { - var res = Stream.prototype.on.call(this, ev, fn); - var state = this._readableState; - if (ev === 'data') { - // update readableListening so that resume() may be a no-op - // a few lines down. This is needed to support once('readable'). - state.readableListening = this.listenerCount('readable') > 0; - - // Try start flowing on next tick if stream isn't explicitly paused - if (state.flowing !== false) this.resume(); - } else if (ev === 'readable') { - if (!state.endEmitted && !state.readableListening) { - state.readableListening = state.needReadable = true; - state.flowing = false; - state.emittedReadable = false; - debug('on readable', state.length, state.reading); - if (state.length) { - emitReadable(this); - } else if (!state.reading) { - process.nextTick(nReadingNextTick, this); - } - } - } - return res; -}; -Readable.prototype.addListener = Readable.prototype.on; -Readable.prototype.removeListener = function (ev, fn) { - var res = Stream.prototype.removeListener.call(this, ev, fn); - if (ev === 'readable') { - // We need to check if there is someone still listening to - // readable and reset the state. However this needs to happen - // after readable has been emitted but before I/O (nextTick) to - // support once('readable', fn) cycles. This means that calling - // resume within the same tick will have no - // effect. - process.nextTick(updateReadableListening, this); - } - return res; -}; -Readable.prototype.removeAllListeners = function (ev) { - var res = Stream.prototype.removeAllListeners.apply(this, arguments); - if (ev === 'readable' || ev === undefined) { - // We need to check if there is someone still listening to - // readable and reset the state. However this needs to happen - // after readable has been emitted but before I/O (nextTick) to - // support once('readable', fn) cycles. This means that calling - // resume within the same tick will have no - // effect. - process.nextTick(updateReadableListening, this); - } - return res; -}; -function updateReadableListening(self) { - var state = self._readableState; - state.readableListening = self.listenerCount('readable') > 0; - if (state.resumeScheduled && !state.paused) { - // flowing needs to be set to true now, otherwise - // the upcoming resume will not flow. - state.flowing = true; - - // crude way to check if we should resume - } else if (self.listenerCount('data') > 0) { - self.resume(); - } -} -function nReadingNextTick(self) { - debug('readable nexttick read 0'); - self.read(0); -} - -// pause() and resume() are remnants of the legacy readable stream API -// If the user uses them, then switch into old mode. -Readable.prototype.resume = function () { - var state = this._readableState; - if (!state.flowing) { - debug('resume'); - // we flow only if there is no one listening - // for readable, but we still have to call - // resume() - state.flowing = !state.readableListening; - resume(this, state); - } - state.paused = false; - return this; -}; -function resume(stream, state) { - if (!state.resumeScheduled) { - state.resumeScheduled = true; - process.nextTick(resume_, stream, state); - } -} -function resume_(stream, state) { - debug('resume', state.reading); - if (!state.reading) { - stream.read(0); - } - state.resumeScheduled = false; - stream.emit('resume'); - flow(stream); - if (state.flowing && !state.reading) stream.read(0); -} -Readable.prototype.pause = function () { - debug('call pause flowing=%j', this._readableState.flowing); - if (this._readableState.flowing !== false) { - debug('pause'); - this._readableState.flowing = false; - this.emit('pause'); - } - this._readableState.paused = true; - return this; -}; -function flow(stream) { - var state = stream._readableState; - debug('flow', state.flowing); - while (state.flowing && stream.read() !== null); -} - -// wrap an old-style stream as the async data source. -// This is *not* part of the readable stream interface. -// It is an ugly unfortunate mess of history. -Readable.prototype.wrap = function (stream) { - var _this = this; - var state = this._readableState; - var paused = false; - stream.on('end', function () { - debug('wrapped end'); - if (state.decoder && !state.ended) { - var chunk = state.decoder.end(); - if (chunk && chunk.length) _this.push(chunk); - } - _this.push(null); - }); - stream.on('data', function (chunk) { - debug('wrapped data'); - if (state.decoder) chunk = state.decoder.write(chunk); - - // don't skip over falsy values in objectMode - if (state.objectMode && (chunk === null || chunk === undefined)) return;else if (!state.objectMode && (!chunk || !chunk.length)) return; - var ret = _this.push(chunk); - if (!ret) { - paused = true; - stream.pause(); - } - }); - - // proxy all the other methods. - // important when wrapping filters and duplexes. - for (var i in stream) { - if (this[i] === undefined && typeof stream[i] === 'function') { - this[i] = function methodWrap(method) { - return function methodWrapReturnFunction() { - return stream[method].apply(stream, arguments); - }; - }(i); - } - } - - // proxy certain important events. - for (var n = 0; n < kProxyEvents.length; n++) { - stream.on(kProxyEvents[n], this.emit.bind(this, kProxyEvents[n])); - } - - // when we try to consume some more bytes, simply unpause the - // underlying stream. - this._read = function (n) { - debug('wrapped _read', n); - if (paused) { - paused = false; - stream.resume(); - } - }; - return this; -}; -if (typeof Symbol === 'function') { - Readable.prototype[Symbol.asyncIterator] = function () { - if (createReadableStreamAsyncIterator === undefined) { - createReadableStreamAsyncIterator = require('./internal/streams/async_iterator'); - } - return createReadableStreamAsyncIterator(this); - }; -} -Object.defineProperty(Readable.prototype, 'readableHighWaterMark', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._readableState.highWaterMark; - } -}); -Object.defineProperty(Readable.prototype, 'readableBuffer', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._readableState && this._readableState.buffer; - } -}); -Object.defineProperty(Readable.prototype, 'readableFlowing', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._readableState.flowing; - }, - set: function set(state) { - if (this._readableState) { - this._readableState.flowing = state; - } - } -}); - -// exposed for testing purposes only. -Readable._fromList = fromList; -Object.defineProperty(Readable.prototype, 'readableLength', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._readableState.length; - } -}); - -// Pluck off n bytes from an array of buffers. -// Length is the combined lengths of all the buffers in the list. -// This function is designed to be inlinable, so please take care when making -// changes to the function body. -function fromList(n, state) { - // nothing buffered - if (state.length === 0) return null; - var ret; - if (state.objectMode) ret = state.buffer.shift();else if (!n || n >= state.length) { - // read it all, truncate the list - if (state.decoder) ret = state.buffer.join('');else if (state.buffer.length === 1) ret = state.buffer.first();else ret = state.buffer.concat(state.length); - state.buffer.clear(); - } else { - // read part of list - ret = state.buffer.consume(n, state.decoder); - } - return ret; -} -function endReadable(stream) { - var state = stream._readableState; - debug('endReadable', state.endEmitted); - if (!state.endEmitted) { - state.ended = true; - process.nextTick(endReadableNT, state, stream); - } -} -function endReadableNT(state, stream) { - debug('endReadableNT', state.endEmitted, state.length); - - // Check that we didn't get one last unshift. - if (!state.endEmitted && state.length === 0) { - state.endEmitted = true; - stream.readable = false; - stream.emit('end'); - if (state.autoDestroy) { - // In case of duplex streams we need a way to detect - // if the writable side is ready for autoDestroy as well - var wState = stream._writableState; - if (!wState || wState.autoDestroy && wState.finished) { - stream.destroy(); - } - } - } -} -if (typeof Symbol === 'function') { - Readable.from = function (iterable, opts) { - if (from === undefined) { - from = require('./internal/streams/from'); - } - return from(Readable, iterable, opts); - }; -} -function indexOf(xs, x) { - for (var i = 0, l = xs.length; i < l; i++) { - if (xs[i] === x) return i; - } - return -1; -} -}).call(this)}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"../errors":233,"./_stream_duplex":234,"./internal/streams/async_iterator":239,"./internal/streams/buffer_list":240,"./internal/streams/destroy":241,"./internal/streams/from":243,"./internal/streams/state":245,"./internal/streams/stream":246,"_process":199,"buffer":63,"events":129,"inherits":155,"string_decoder/":247,"util":34}],237:[function(require,module,exports){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// a transform stream is a readable/writable stream where you do -// something with the data. Sometimes it's called a "filter", -// but that's not a great name for it, since that implies a thing where -// some bits pass through, and others are simply ignored. (That would -// be a valid example of a transform, of course.) -// -// While the output is causally related to the input, it's not a -// necessarily symmetric or synchronous transformation. For example, -// a zlib stream might take multiple plain-text writes(), and then -// emit a single compressed chunk some time in the future. -// -// Here's how this works: -// -// The Transform stream has all the aspects of the readable and writable -// stream classes. When you write(chunk), that calls _write(chunk,cb) -// internally, and returns false if there's a lot of pending writes -// buffered up. When you call read(), that calls _read(n) until -// there's enough pending readable data buffered up. -// -// In a transform stream, the written data is placed in a buffer. When -// _read(n) is called, it transforms the queued up data, calling the -// buffered _write cb's as it consumes chunks. If consuming a single -// written chunk would result in multiple output chunks, then the first -// outputted bit calls the readcb, and subsequent chunks just go into -// the read buffer, and will cause it to emit 'readable' if necessary. -// -// This way, back-pressure is actually determined by the reading side, -// since _read has to be called to start processing a new chunk. However, -// a pathological inflate type of transform can cause excessive buffering -// here. For example, imagine a stream where every byte of input is -// interpreted as an integer from 0-255, and then results in that many -// bytes of output. Writing the 4 bytes {ff,ff,ff,ff} would result in -// 1kb of data being output. In this case, you could write a very small -// amount of input, and end up with a very large amount of output. In -// such a pathological inflating mechanism, there'd be no way to tell -// the system to stop doing the transform. A single 4MB write could -// cause the system to run out of memory. -// -// However, even in such a pathological case, only a single written chunk -// would be consumed, and then the rest would wait (un-transformed) until -// the results of the previous transformed chunk were consumed. - -'use strict'; - -module.exports = Transform; -var _require$codes = require('../errors').codes, - ERR_METHOD_NOT_IMPLEMENTED = _require$codes.ERR_METHOD_NOT_IMPLEMENTED, - ERR_MULTIPLE_CALLBACK = _require$codes.ERR_MULTIPLE_CALLBACK, - ERR_TRANSFORM_ALREADY_TRANSFORMING = _require$codes.ERR_TRANSFORM_ALREADY_TRANSFORMING, - ERR_TRANSFORM_WITH_LENGTH_0 = _require$codes.ERR_TRANSFORM_WITH_LENGTH_0; -var Duplex = require('./_stream_duplex'); -require('inherits')(Transform, Duplex); -function afterTransform(er, data) { - var ts = this._transformState; - ts.transforming = false; - var cb = ts.writecb; - if (cb === null) { - return this.emit('error', new ERR_MULTIPLE_CALLBACK()); - } - ts.writechunk = null; - ts.writecb = null; - if (data != null) - // single equals check for both `null` and `undefined` - this.push(data); - cb(er); - var rs = this._readableState; - rs.reading = false; - if (rs.needReadable || rs.length < rs.highWaterMark) { - this._read(rs.highWaterMark); - } -} -function Transform(options) { - if (!(this instanceof Transform)) return new Transform(options); - Duplex.call(this, options); - this._transformState = { - afterTransform: afterTransform.bind(this), - needTransform: false, - transforming: false, - writecb: null, - writechunk: null, - writeencoding: null - }; - - // start out asking for a readable event once data is transformed. - this._readableState.needReadable = true; - - // we have implemented the _read method, and done the other things - // that Readable wants before the first _read call, so unset the - // sync guard flag. - this._readableState.sync = false; - if (options) { - if (typeof options.transform === 'function') this._transform = options.transform; - if (typeof options.flush === 'function') this._flush = options.flush; - } - - // When the writable side finishes, then flush out anything remaining. - this.on('prefinish', prefinish); -} -function prefinish() { - var _this = this; - if (typeof this._flush === 'function' && !this._readableState.destroyed) { - this._flush(function (er, data) { - done(_this, er, data); - }); - } else { - done(this, null, null); - } -} -Transform.prototype.push = function (chunk, encoding) { - this._transformState.needTransform = false; - return Duplex.prototype.push.call(this, chunk, encoding); -}; - -// This is the part where you do stuff! -// override this function in implementation classes. -// 'chunk' is an input chunk. -// -// Call `push(newChunk)` to pass along transformed output -// to the readable side. You may call 'push' zero or more times. -// -// Call `cb(err)` when you are done with this chunk. If you pass -// an error, then that'll put the hurt on the whole operation. If you -// never call cb(), then you'll never get another chunk. -Transform.prototype._transform = function (chunk, encoding, cb) { - cb(new ERR_METHOD_NOT_IMPLEMENTED('_transform()')); -}; -Transform.prototype._write = function (chunk, encoding, cb) { - var ts = this._transformState; - ts.writecb = cb; - ts.writechunk = chunk; - ts.writeencoding = encoding; - if (!ts.transforming) { - var rs = this._readableState; - if (ts.needTransform || rs.needReadable || rs.length < rs.highWaterMark) this._read(rs.highWaterMark); - } -}; - -// Doesn't matter what the args are here. -// _transform does all the work. -// That we got here means that the readable side wants more data. -Transform.prototype._read = function (n) { - var ts = this._transformState; - if (ts.writechunk !== null && !ts.transforming) { - ts.transforming = true; - this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform); - } else { - // mark that we need a transform, so that any data that comes in - // will get processed, now that we've asked for it. - ts.needTransform = true; - } -}; -Transform.prototype._destroy = function (err, cb) { - Duplex.prototype._destroy.call(this, err, function (err2) { - cb(err2); - }); -}; -function done(stream, er, data) { - if (er) return stream.emit('error', er); - if (data != null) - // single equals check for both `null` and `undefined` - stream.push(data); - - // TODO(BridgeAR): Write a test for these two error cases - // if there's nothing in the write buffer, then that means - // that nothing more will ever be provided - if (stream._writableState.length) throw new ERR_TRANSFORM_WITH_LENGTH_0(); - if (stream._transformState.transforming) throw new ERR_TRANSFORM_ALREADY_TRANSFORMING(); - return stream.push(null); -} -},{"../errors":233,"./_stream_duplex":234,"inherits":155}],238:[function(require,module,exports){ -(function (process,global){(function (){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// A bit simpler than readable streams. -// Implement an async ._write(chunk, encoding, cb), and it'll handle all -// the drain event emission and buffering. - -'use strict'; - -module.exports = Writable; - -/* */ -function WriteReq(chunk, encoding, cb) { - this.chunk = chunk; - this.encoding = encoding; - this.callback = cb; - this.next = null; -} - -// It seems a linked list but it is not -// there will be only 2 of these for each stream -function CorkedRequest(state) { - var _this = this; - this.next = null; - this.entry = null; - this.finish = function () { - onCorkedFinish(_this, state); - }; -} -/* */ - -/**/ -var Duplex; -/**/ - -Writable.WritableState = WritableState; - -/**/ -var internalUtil = { - deprecate: require('util-deprecate') -}; -/**/ - -/**/ -var Stream = require('./internal/streams/stream'); -/**/ - -var Buffer = require('buffer').Buffer; -var OurUint8Array = (typeof global !== 'undefined' ? global : typeof window !== 'undefined' ? window : typeof self !== 'undefined' ? self : {}).Uint8Array || function () {}; -function _uint8ArrayToBuffer(chunk) { - return Buffer.from(chunk); -} -function _isUint8Array(obj) { - return Buffer.isBuffer(obj) || obj instanceof OurUint8Array; -} -var destroyImpl = require('./internal/streams/destroy'); -var _require = require('./internal/streams/state'), - getHighWaterMark = _require.getHighWaterMark; -var _require$codes = require('../errors').codes, - ERR_INVALID_ARG_TYPE = _require$codes.ERR_INVALID_ARG_TYPE, - ERR_METHOD_NOT_IMPLEMENTED = _require$codes.ERR_METHOD_NOT_IMPLEMENTED, - ERR_MULTIPLE_CALLBACK = _require$codes.ERR_MULTIPLE_CALLBACK, - ERR_STREAM_CANNOT_PIPE = _require$codes.ERR_STREAM_CANNOT_PIPE, - ERR_STREAM_DESTROYED = _require$codes.ERR_STREAM_DESTROYED, - ERR_STREAM_NULL_VALUES = _require$codes.ERR_STREAM_NULL_VALUES, - ERR_STREAM_WRITE_AFTER_END = _require$codes.ERR_STREAM_WRITE_AFTER_END, - ERR_UNKNOWN_ENCODING = _require$codes.ERR_UNKNOWN_ENCODING; -var errorOrDestroy = destroyImpl.errorOrDestroy; -require('inherits')(Writable, Stream); -function nop() {} -function WritableState(options, stream, isDuplex) { - Duplex = Duplex || require('./_stream_duplex'); - options = options || {}; - - // Duplex streams are both readable and writable, but share - // the same options object. - // However, some cases require setting options to different - // values for the readable and the writable sides of the duplex stream, - // e.g. options.readableObjectMode vs. options.writableObjectMode, etc. - if (typeof isDuplex !== 'boolean') isDuplex = stream instanceof Duplex; - - // object stream flag to indicate whether or not this stream - // contains buffers or objects. - this.objectMode = !!options.objectMode; - if (isDuplex) this.objectMode = this.objectMode || !!options.writableObjectMode; - - // the point at which write() starts returning false - // Note: 0 is a valid value, means that we always return false if - // the entire buffer is not flushed immediately on write() - this.highWaterMark = getHighWaterMark(this, options, 'writableHighWaterMark', isDuplex); - - // if _final has been called - this.finalCalled = false; - - // drain event flag. - this.needDrain = false; - // at the start of calling end() - this.ending = false; - // when end() has been called, and returned - this.ended = false; - // when 'finish' is emitted - this.finished = false; - - // has it been destroyed - this.destroyed = false; - - // should we decode strings into buffers before passing to _write? - // this is here so that some node-core streams can optimize string - // handling at a lower level. - var noDecode = options.decodeStrings === false; - this.decodeStrings = !noDecode; - - // Crypto is kind of old and crusty. Historically, its default string - // encoding is 'binary' so we have to make this configurable. - // Everything else in the universe uses 'utf8', though. - this.defaultEncoding = options.defaultEncoding || 'utf8'; - - // not an actual buffer we keep track of, but a measurement - // of how much we're waiting to get pushed to some underlying - // socket or file. - this.length = 0; - - // a flag to see when we're in the middle of a write. - this.writing = false; - - // when true all writes will be buffered until .uncork() call - this.corked = 0; - - // a flag to be able to tell if the onwrite cb is called immediately, - // or on a later tick. We set this to true at first, because any - // actions that shouldn't happen until "later" should generally also - // not happen before the first write call. - this.sync = true; - - // a flag to know if we're processing previously buffered items, which - // may call the _write() callback in the same tick, so that we don't - // end up in an overlapped onwrite situation. - this.bufferProcessing = false; - - // the callback that's passed to _write(chunk,cb) - this.onwrite = function (er) { - onwrite(stream, er); - }; - - // the callback that the user supplies to write(chunk,encoding,cb) - this.writecb = null; - - // the amount that is being written when _write is called. - this.writelen = 0; - this.bufferedRequest = null; - this.lastBufferedRequest = null; - - // number of pending user-supplied write callbacks - // this must be 0 before 'finish' can be emitted - this.pendingcb = 0; - - // emit prefinish if the only thing we're waiting for is _write cbs - // This is relevant for synchronous Transform streams - this.prefinished = false; - - // True if the error was already emitted and should not be thrown again - this.errorEmitted = false; - - // Should close be emitted on destroy. Defaults to true. - this.emitClose = options.emitClose !== false; - - // Should .destroy() be called after 'finish' (and potentially 'end') - this.autoDestroy = !!options.autoDestroy; - - // count buffered requests - this.bufferedRequestCount = 0; - - // allocate the first CorkedRequest, there is always - // one allocated and free to use, and we maintain at most two - this.corkedRequestsFree = new CorkedRequest(this); -} -WritableState.prototype.getBuffer = function getBuffer() { - var current = this.bufferedRequest; - var out = []; - while (current) { - out.push(current); - current = current.next; - } - return out; -}; -(function () { - try { - Object.defineProperty(WritableState.prototype, 'buffer', { - get: internalUtil.deprecate(function writableStateBufferGetter() { - return this.getBuffer(); - }, '_writableState.buffer is deprecated. Use _writableState.getBuffer ' + 'instead.', 'DEP0003') - }); - } catch (_) {} -})(); - -// Test _writableState for inheritance to account for Duplex streams, -// whose prototype chain only points to Readable. -var realHasInstance; -if (typeof Symbol === 'function' && Symbol.hasInstance && typeof Function.prototype[Symbol.hasInstance] === 'function') { - realHasInstance = Function.prototype[Symbol.hasInstance]; - Object.defineProperty(Writable, Symbol.hasInstance, { - value: function value(object) { - if (realHasInstance.call(this, object)) return true; - if (this !== Writable) return false; - return object && object._writableState instanceof WritableState; - } - }); -} else { - realHasInstance = function realHasInstance(object) { - return object instanceof this; - }; -} -function Writable(options) { - Duplex = Duplex || require('./_stream_duplex'); - - // Writable ctor is applied to Duplexes, too. - // `realHasInstance` is necessary because using plain `instanceof` - // would return false, as no `_writableState` property is attached. - - // Trying to use the custom `instanceof` for Writable here will also break the - // Node.js LazyTransform implementation, which has a non-trivial getter for - // `_writableState` that would lead to infinite recursion. - - // Checking for a Stream.Duplex instance is faster here instead of inside - // the WritableState constructor, at least with V8 6.5 - var isDuplex = this instanceof Duplex; - if (!isDuplex && !realHasInstance.call(Writable, this)) return new Writable(options); - this._writableState = new WritableState(options, this, isDuplex); - - // legacy. - this.writable = true; - if (options) { - if (typeof options.write === 'function') this._write = options.write; - if (typeof options.writev === 'function') this._writev = options.writev; - if (typeof options.destroy === 'function') this._destroy = options.destroy; - if (typeof options.final === 'function') this._final = options.final; - } - Stream.call(this); -} - -// Otherwise people can pipe Writable streams, which is just wrong. -Writable.prototype.pipe = function () { - errorOrDestroy(this, new ERR_STREAM_CANNOT_PIPE()); -}; -function writeAfterEnd(stream, cb) { - var er = new ERR_STREAM_WRITE_AFTER_END(); - // TODO: defer error events consistently everywhere, not just the cb - errorOrDestroy(stream, er); - process.nextTick(cb, er); -} - -// Checks that a user-supplied chunk is valid, especially for the particular -// mode the stream is in. Currently this means that `null` is never accepted -// and undefined/non-string values are only allowed in object mode. -function validChunk(stream, state, chunk, cb) { - var er; - if (chunk === null) { - er = new ERR_STREAM_NULL_VALUES(); - } else if (typeof chunk !== 'string' && !state.objectMode) { - er = new ERR_INVALID_ARG_TYPE('chunk', ['string', 'Buffer'], chunk); - } - if (er) { - errorOrDestroy(stream, er); - process.nextTick(cb, er); - return false; - } - return true; -} -Writable.prototype.write = function (chunk, encoding, cb) { - var state = this._writableState; - var ret = false; - var isBuf = !state.objectMode && _isUint8Array(chunk); - if (isBuf && !Buffer.isBuffer(chunk)) { - chunk = _uint8ArrayToBuffer(chunk); - } - if (typeof encoding === 'function') { - cb = encoding; - encoding = null; - } - if (isBuf) encoding = 'buffer';else if (!encoding) encoding = state.defaultEncoding; - if (typeof cb !== 'function') cb = nop; - if (state.ending) writeAfterEnd(this, cb);else if (isBuf || validChunk(this, state, chunk, cb)) { - state.pendingcb++; - ret = writeOrBuffer(this, state, isBuf, chunk, encoding, cb); - } - return ret; -}; -Writable.prototype.cork = function () { - this._writableState.corked++; -}; -Writable.prototype.uncork = function () { - var state = this._writableState; - if (state.corked) { - state.corked--; - if (!state.writing && !state.corked && !state.bufferProcessing && state.bufferedRequest) clearBuffer(this, state); - } -}; -Writable.prototype.setDefaultEncoding = function setDefaultEncoding(encoding) { - // node::ParseEncoding() requires lower case. - if (typeof encoding === 'string') encoding = encoding.toLowerCase(); - if (!(['hex', 'utf8', 'utf-8', 'ascii', 'binary', 'base64', 'ucs2', 'ucs-2', 'utf16le', 'utf-16le', 'raw'].indexOf((encoding + '').toLowerCase()) > -1)) throw new ERR_UNKNOWN_ENCODING(encoding); - this._writableState.defaultEncoding = encoding; - return this; -}; -Object.defineProperty(Writable.prototype, 'writableBuffer', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._writableState && this._writableState.getBuffer(); - } -}); -function decodeChunk(state, chunk, encoding) { - if (!state.objectMode && state.decodeStrings !== false && typeof chunk === 'string') { - chunk = Buffer.from(chunk, encoding); - } - return chunk; -} -Object.defineProperty(Writable.prototype, 'writableHighWaterMark', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._writableState.highWaterMark; - } -}); - -// if we're already writing something, then just put this -// in the queue, and wait our turn. Otherwise, call _write -// If we return false, then we need a drain event, so set that flag. -function writeOrBuffer(stream, state, isBuf, chunk, encoding, cb) { - if (!isBuf) { - var newChunk = decodeChunk(state, chunk, encoding); - if (chunk !== newChunk) { - isBuf = true; - encoding = 'buffer'; - chunk = newChunk; - } - } - var len = state.objectMode ? 1 : chunk.length; - state.length += len; - var ret = state.length < state.highWaterMark; - // we must ensure that previous needDrain will not be reset to false. - if (!ret) state.needDrain = true; - if (state.writing || state.corked) { - var last = state.lastBufferedRequest; - state.lastBufferedRequest = { - chunk: chunk, - encoding: encoding, - isBuf: isBuf, - callback: cb, - next: null - }; - if (last) { - last.next = state.lastBufferedRequest; - } else { - state.bufferedRequest = state.lastBufferedRequest; - } - state.bufferedRequestCount += 1; - } else { - doWrite(stream, state, false, len, chunk, encoding, cb); - } - return ret; -} -function doWrite(stream, state, writev, len, chunk, encoding, cb) { - state.writelen = len; - state.writecb = cb; - state.writing = true; - state.sync = true; - if (state.destroyed) state.onwrite(new ERR_STREAM_DESTROYED('write'));else if (writev) stream._writev(chunk, state.onwrite);else stream._write(chunk, encoding, state.onwrite); - state.sync = false; -} -function onwriteError(stream, state, sync, er, cb) { - --state.pendingcb; - if (sync) { - // defer the callback if we are being called synchronously - // to avoid piling up things on the stack - process.nextTick(cb, er); - // this can emit finish, and it will always happen - // after error - process.nextTick(finishMaybe, stream, state); - stream._writableState.errorEmitted = true; - errorOrDestroy(stream, er); - } else { - // the caller expect this to happen before if - // it is async - cb(er); - stream._writableState.errorEmitted = true; - errorOrDestroy(stream, er); - // this can emit finish, but finish must - // always follow error - finishMaybe(stream, state); - } -} -function onwriteStateUpdate(state) { - state.writing = false; - state.writecb = null; - state.length -= state.writelen; - state.writelen = 0; -} -function onwrite(stream, er) { - var state = stream._writableState; - var sync = state.sync; - var cb = state.writecb; - if (typeof cb !== 'function') throw new ERR_MULTIPLE_CALLBACK(); - onwriteStateUpdate(state); - if (er) onwriteError(stream, state, sync, er, cb);else { - // Check if we're actually ready to finish, but don't emit yet - var finished = needFinish(state) || stream.destroyed; - if (!finished && !state.corked && !state.bufferProcessing && state.bufferedRequest) { - clearBuffer(stream, state); - } - if (sync) { - process.nextTick(afterWrite, stream, state, finished, cb); - } else { - afterWrite(stream, state, finished, cb); - } - } -} -function afterWrite(stream, state, finished, cb) { - if (!finished) onwriteDrain(stream, state); - state.pendingcb--; - cb(); - finishMaybe(stream, state); -} - -// Must force callback to be called on nextTick, so that we don't -// emit 'drain' before the write() consumer gets the 'false' return -// value, and has a chance to attach a 'drain' listener. -function onwriteDrain(stream, state) { - if (state.length === 0 && state.needDrain) { - state.needDrain = false; - stream.emit('drain'); - } -} - -// if there's something in the buffer waiting, then process it -function clearBuffer(stream, state) { - state.bufferProcessing = true; - var entry = state.bufferedRequest; - if (stream._writev && entry && entry.next) { - // Fast case, write everything using _writev() - var l = state.bufferedRequestCount; - var buffer = new Array(l); - var holder = state.corkedRequestsFree; - holder.entry = entry; - var count = 0; - var allBuffers = true; - while (entry) { - buffer[count] = entry; - if (!entry.isBuf) allBuffers = false; - entry = entry.next; - count += 1; - } - buffer.allBuffers = allBuffers; - doWrite(stream, state, true, state.length, buffer, '', holder.finish); - - // doWrite is almost always async, defer these to save a bit of time - // as the hot path ends with doWrite - state.pendingcb++; - state.lastBufferedRequest = null; - if (holder.next) { - state.corkedRequestsFree = holder.next; - holder.next = null; - } else { - state.corkedRequestsFree = new CorkedRequest(state); - } - state.bufferedRequestCount = 0; - } else { - // Slow case, write chunks one-by-one - while (entry) { - var chunk = entry.chunk; - var encoding = entry.encoding; - var cb = entry.callback; - var len = state.objectMode ? 1 : chunk.length; - doWrite(stream, state, false, len, chunk, encoding, cb); - entry = entry.next; - state.bufferedRequestCount--; - // if we didn't call the onwrite immediately, then - // it means that we need to wait until it does. - // also, that means that the chunk and cb are currently - // being processed, so move the buffer counter past them. - if (state.writing) { - break; - } - } - if (entry === null) state.lastBufferedRequest = null; - } - state.bufferedRequest = entry; - state.bufferProcessing = false; -} -Writable.prototype._write = function (chunk, encoding, cb) { - cb(new ERR_METHOD_NOT_IMPLEMENTED('_write()')); -}; -Writable.prototype._writev = null; -Writable.prototype.end = function (chunk, encoding, cb) { - var state = this._writableState; - if (typeof chunk === 'function') { - cb = chunk; - chunk = null; - encoding = null; - } else if (typeof encoding === 'function') { - cb = encoding; - encoding = null; - } - if (chunk !== null && chunk !== undefined) this.write(chunk, encoding); - - // .end() fully uncorks - if (state.corked) { - state.corked = 1; - this.uncork(); - } - - // ignore unnecessary end() calls. - if (!state.ending) endWritable(this, state, cb); - return this; -}; -Object.defineProperty(Writable.prototype, 'writableLength', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._writableState.length; - } -}); -function needFinish(state) { - return state.ending && state.length === 0 && state.bufferedRequest === null && !state.finished && !state.writing; -} -function callFinal(stream, state) { - stream._final(function (err) { - state.pendingcb--; - if (err) { - errorOrDestroy(stream, err); - } - state.prefinished = true; - stream.emit('prefinish'); - finishMaybe(stream, state); - }); -} -function prefinish(stream, state) { - if (!state.prefinished && !state.finalCalled) { - if (typeof stream._final === 'function' && !state.destroyed) { - state.pendingcb++; - state.finalCalled = true; - process.nextTick(callFinal, stream, state); - } else { - state.prefinished = true; - stream.emit('prefinish'); - } - } -} -function finishMaybe(stream, state) { - var need = needFinish(state); - if (need) { - prefinish(stream, state); - if (state.pendingcb === 0) { - state.finished = true; - stream.emit('finish'); - if (state.autoDestroy) { - // In case of duplex streams we need a way to detect - // if the readable side is ready for autoDestroy as well - var rState = stream._readableState; - if (!rState || rState.autoDestroy && rState.endEmitted) { - stream.destroy(); - } - } - } - } - return need; -} -function endWritable(stream, state, cb) { - state.ending = true; - finishMaybe(stream, state); - if (cb) { - if (state.finished) process.nextTick(cb);else stream.once('finish', cb); - } - state.ended = true; - stream.writable = false; -} -function onCorkedFinish(corkReq, state, err) { - var entry = corkReq.entry; - corkReq.entry = null; - while (entry) { - var cb = entry.callback; - state.pendingcb--; - cb(err); - entry = entry.next; - } - - // reuse the free corkReq. - state.corkedRequestsFree.next = corkReq; -} -Object.defineProperty(Writable.prototype, 'destroyed', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - if (this._writableState === undefined) { - return false; - } - return this._writableState.destroyed; - }, - set: function set(value) { - // we ignore the value if the stream - // has not been initialized yet - if (!this._writableState) { - return; - } - - // backward compatibility, the user is explicitly - // managing destroyed - this._writableState.destroyed = value; - } -}); -Writable.prototype.destroy = destroyImpl.destroy; -Writable.prototype._undestroy = destroyImpl.undestroy; -Writable.prototype._destroy = function (err, cb) { - cb(err); -}; -}).call(this)}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"../errors":233,"./_stream_duplex":234,"./internal/streams/destroy":241,"./internal/streams/state":245,"./internal/streams/stream":246,"_process":199,"buffer":63,"inherits":155,"util-deprecate":249}],239:[function(require,module,exports){ -(function (process){(function (){ -'use strict'; - -var _Object$setPrototypeO; -function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } -function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } -function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } -var finished = require('./end-of-stream'); -var kLastResolve = Symbol('lastResolve'); -var kLastReject = Symbol('lastReject'); -var kError = Symbol('error'); -var kEnded = Symbol('ended'); -var kLastPromise = Symbol('lastPromise'); -var kHandlePromise = Symbol('handlePromise'); -var kStream = Symbol('stream'); -function createIterResult(value, done) { - return { - value: value, - done: done - }; -} -function readAndResolve(iter) { - var resolve = iter[kLastResolve]; - if (resolve !== null) { - var data = iter[kStream].read(); - // we defer if data is null - // we can be expecting either 'end' or - // 'error' - if (data !== null) { - iter[kLastPromise] = null; - iter[kLastResolve] = null; - iter[kLastReject] = null; - resolve(createIterResult(data, false)); - } - } -} -function onReadable(iter) { - // we wait for the next tick, because it might - // emit an error with process.nextTick - process.nextTick(readAndResolve, iter); -} -function wrapForNext(lastPromise, iter) { - return function (resolve, reject) { - lastPromise.then(function () { - if (iter[kEnded]) { - resolve(createIterResult(undefined, true)); - return; - } - iter[kHandlePromise](resolve, reject); - }, reject); - }; -} -var AsyncIteratorPrototype = Object.getPrototypeOf(function () {}); -var ReadableStreamAsyncIteratorPrototype = Object.setPrototypeOf((_Object$setPrototypeO = { - get stream() { - return this[kStream]; - }, - next: function next() { - var _this = this; - // if we have detected an error in the meanwhile - // reject straight away - var error = this[kError]; - if (error !== null) { - return Promise.reject(error); - } - if (this[kEnded]) { - return Promise.resolve(createIterResult(undefined, true)); - } - if (this[kStream].destroyed) { - // We need to defer via nextTick because if .destroy(err) is - // called, the error will be emitted via nextTick, and - // we cannot guarantee that there is no error lingering around - // waiting to be emitted. - return new Promise(function (resolve, reject) { - process.nextTick(function () { - if (_this[kError]) { - reject(_this[kError]); - } else { - resolve(createIterResult(undefined, true)); - } - }); - }); - } - - // if we have multiple next() calls - // we will wait for the previous Promise to finish - // this logic is optimized to support for await loops, - // where next() is only called once at a time - var lastPromise = this[kLastPromise]; - var promise; - if (lastPromise) { - promise = new Promise(wrapForNext(lastPromise, this)); - } else { - // fast path needed to support multiple this.push() - // without triggering the next() queue - var data = this[kStream].read(); - if (data !== null) { - return Promise.resolve(createIterResult(data, false)); - } - promise = new Promise(this[kHandlePromise]); - } - this[kLastPromise] = promise; - return promise; - } -}, _defineProperty(_Object$setPrototypeO, Symbol.asyncIterator, function () { - return this; -}), _defineProperty(_Object$setPrototypeO, "return", function _return() { - var _this2 = this; - // destroy(err, cb) is a private API - // we can guarantee we have that here, because we control the - // Readable class this is attached to - return new Promise(function (resolve, reject) { - _this2[kStream].destroy(null, function (err) { - if (err) { - reject(err); - return; - } - resolve(createIterResult(undefined, true)); - }); - }); -}), _Object$setPrototypeO), AsyncIteratorPrototype); -var createReadableStreamAsyncIterator = function createReadableStreamAsyncIterator(stream) { - var _Object$create; - var iterator = Object.create(ReadableStreamAsyncIteratorPrototype, (_Object$create = {}, _defineProperty(_Object$create, kStream, { - value: stream, - writable: true - }), _defineProperty(_Object$create, kLastResolve, { - value: null, - writable: true - }), _defineProperty(_Object$create, kLastReject, { - value: null, - writable: true - }), _defineProperty(_Object$create, kError, { - value: null, - writable: true - }), _defineProperty(_Object$create, kEnded, { - value: stream._readableState.endEmitted, - writable: true - }), _defineProperty(_Object$create, kHandlePromise, { - value: function value(resolve, reject) { - var data = iterator[kStream].read(); - if (data) { - iterator[kLastPromise] = null; - iterator[kLastResolve] = null; - iterator[kLastReject] = null; - resolve(createIterResult(data, false)); - } else { - iterator[kLastResolve] = resolve; - iterator[kLastReject] = reject; - } - }, - writable: true - }), _Object$create)); - iterator[kLastPromise] = null; - finished(stream, function (err) { - if (err && err.code !== 'ERR_STREAM_PREMATURE_CLOSE') { - var reject = iterator[kLastReject]; - // reject if we are waiting for data in the Promise - // returned by next() and store the error - if (reject !== null) { - iterator[kLastPromise] = null; - iterator[kLastResolve] = null; - iterator[kLastReject] = null; - reject(err); - } - iterator[kError] = err; - return; - } - var resolve = iterator[kLastResolve]; - if (resolve !== null) { - iterator[kLastPromise] = null; - iterator[kLastResolve] = null; - iterator[kLastReject] = null; - resolve(createIterResult(undefined, true)); - } - iterator[kEnded] = true; - }); - stream.on('readable', onReadable.bind(null, iterator)); - return iterator; -}; -module.exports = createReadableStreamAsyncIterator; -}).call(this)}).call(this,require('_process')) -},{"./end-of-stream":242,"_process":199}],240:[function(require,module,exports){ -'use strict'; - -function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } -function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } -function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } -function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } -function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } -function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } -function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } -var _require = require('buffer'), - Buffer = _require.Buffer; -var _require2 = require('util'), - inspect = _require2.inspect; -var custom = inspect && inspect.custom || 'inspect'; -function copyBuffer(src, target, offset) { - Buffer.prototype.copy.call(src, target, offset); -} -module.exports = /*#__PURE__*/function () { - function BufferList() { - _classCallCheck(this, BufferList); - this.head = null; - this.tail = null; - this.length = 0; - } - _createClass(BufferList, [{ - key: "push", - value: function push(v) { - var entry = { - data: v, - next: null - }; - if (this.length > 0) this.tail.next = entry;else this.head = entry; - this.tail = entry; - ++this.length; - } - }, { - key: "unshift", - value: function unshift(v) { - var entry = { - data: v, - next: this.head - }; - if (this.length === 0) this.tail = entry; - this.head = entry; - ++this.length; - } - }, { - key: "shift", - value: function shift() { - if (this.length === 0) return; - var ret = this.head.data; - if (this.length === 1) this.head = this.tail = null;else this.head = this.head.next; - --this.length; - return ret; - } - }, { - key: "clear", - value: function clear() { - this.head = this.tail = null; - this.length = 0; - } - }, { - key: "join", - value: function join(s) { - if (this.length === 0) return ''; - var p = this.head; - var ret = '' + p.data; - while (p = p.next) ret += s + p.data; - return ret; - } - }, { - key: "concat", - value: function concat(n) { - if (this.length === 0) return Buffer.alloc(0); - var ret = Buffer.allocUnsafe(n >>> 0); - var p = this.head; - var i = 0; - while (p) { - copyBuffer(p.data, ret, i); - i += p.data.length; - p = p.next; - } - return ret; - } - - // Consumes a specified amount of bytes or characters from the buffered data. - }, { - key: "consume", - value: function consume(n, hasStrings) { - var ret; - if (n < this.head.data.length) { - // `slice` is the same for buffers and strings. - ret = this.head.data.slice(0, n); - this.head.data = this.head.data.slice(n); - } else if (n === this.head.data.length) { - // First chunk is a perfect match. - ret = this.shift(); - } else { - // Result spans more than one buffer. - ret = hasStrings ? this._getString(n) : this._getBuffer(n); - } - return ret; - } - }, { - key: "first", - value: function first() { - return this.head.data; - } - - // Consumes a specified amount of characters from the buffered data. - }, { - key: "_getString", - value: function _getString(n) { - var p = this.head; - var c = 1; - var ret = p.data; - n -= ret.length; - while (p = p.next) { - var str = p.data; - var nb = n > str.length ? str.length : n; - if (nb === str.length) ret += str;else ret += str.slice(0, n); - n -= nb; - if (n === 0) { - if (nb === str.length) { - ++c; - if (p.next) this.head = p.next;else this.head = this.tail = null; - } else { - this.head = p; - p.data = str.slice(nb); - } - break; - } - ++c; - } - this.length -= c; - return ret; - } - - // Consumes a specified amount of bytes from the buffered data. - }, { - key: "_getBuffer", - value: function _getBuffer(n) { - var ret = Buffer.allocUnsafe(n); - var p = this.head; - var c = 1; - p.data.copy(ret); - n -= p.data.length; - while (p = p.next) { - var buf = p.data; - var nb = n > buf.length ? buf.length : n; - buf.copy(ret, ret.length - n, 0, nb); - n -= nb; - if (n === 0) { - if (nb === buf.length) { - ++c; - if (p.next) this.head = p.next;else this.head = this.tail = null; - } else { - this.head = p; - p.data = buf.slice(nb); - } - break; - } - ++c; - } - this.length -= c; - return ret; - } - - // Make sure the linked list only shows the minimal necessary information. - }, { - key: custom, - value: function value(_, options) { - return inspect(this, _objectSpread(_objectSpread({}, options), {}, { - // Only inspect one level. - depth: 0, - // It should not recurse. - customInspect: false - })); - } - }]); - return BufferList; -}(); -},{"buffer":63,"util":34}],241:[function(require,module,exports){ -(function (process){(function (){ -'use strict'; - -// undocumented cb() API, needed for core, not for public API -function destroy(err, cb) { - var _this = this; - var readableDestroyed = this._readableState && this._readableState.destroyed; - var writableDestroyed = this._writableState && this._writableState.destroyed; - if (readableDestroyed || writableDestroyed) { - if (cb) { - cb(err); - } else if (err) { - if (!this._writableState) { - process.nextTick(emitErrorNT, this, err); - } else if (!this._writableState.errorEmitted) { - this._writableState.errorEmitted = true; - process.nextTick(emitErrorNT, this, err); - } - } - return this; - } - - // we set destroyed to true before firing error callbacks in order - // to make it re-entrance safe in case destroy() is called within callbacks - - if (this._readableState) { - this._readableState.destroyed = true; - } - - // if this is a duplex stream mark the writable part as destroyed as well - if (this._writableState) { - this._writableState.destroyed = true; - } - this._destroy(err || null, function (err) { - if (!cb && err) { - if (!_this._writableState) { - process.nextTick(emitErrorAndCloseNT, _this, err); - } else if (!_this._writableState.errorEmitted) { - _this._writableState.errorEmitted = true; - process.nextTick(emitErrorAndCloseNT, _this, err); - } else { - process.nextTick(emitCloseNT, _this); - } - } else if (cb) { - process.nextTick(emitCloseNT, _this); - cb(err); - } else { - process.nextTick(emitCloseNT, _this); - } - }); - return this; -} -function emitErrorAndCloseNT(self, err) { - emitErrorNT(self, err); - emitCloseNT(self); -} -function emitCloseNT(self) { - if (self._writableState && !self._writableState.emitClose) return; - if (self._readableState && !self._readableState.emitClose) return; - self.emit('close'); -} -function undestroy() { - if (this._readableState) { - this._readableState.destroyed = false; - this._readableState.reading = false; - this._readableState.ended = false; - this._readableState.endEmitted = false; - } - if (this._writableState) { - this._writableState.destroyed = false; - this._writableState.ended = false; - this._writableState.ending = false; - this._writableState.finalCalled = false; - this._writableState.prefinished = false; - this._writableState.finished = false; - this._writableState.errorEmitted = false; - } -} -function emitErrorNT(self, err) { - self.emit('error', err); -} -function errorOrDestroy(stream, err) { - // We have tests that rely on errors being emitted - // in the same tick, so changing this is semver major. - // For now when you opt-in to autoDestroy we allow - // the error to be emitted nextTick. In a future - // semver major update we should change the default to this. - - var rState = stream._readableState; - var wState = stream._writableState; - if (rState && rState.autoDestroy || wState && wState.autoDestroy) stream.destroy(err);else stream.emit('error', err); -} -module.exports = { - destroy: destroy, - undestroy: undestroy, - errorOrDestroy: errorOrDestroy -}; -}).call(this)}).call(this,require('_process')) -},{"_process":199}],242:[function(require,module,exports){ -// Ported from https://github.com/mafintosh/end-of-stream with -// permission from the author, Mathias Buus (@mafintosh). - -'use strict'; - -var ERR_STREAM_PREMATURE_CLOSE = require('../../../errors').codes.ERR_STREAM_PREMATURE_CLOSE; -function once(callback) { - var called = false; - return function () { - if (called) return; - called = true; - for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { - args[_key] = arguments[_key]; - } - callback.apply(this, args); - }; -} -function noop() {} -function isRequest(stream) { - return stream.setHeader && typeof stream.abort === 'function'; -} -function eos(stream, opts, callback) { - if (typeof opts === 'function') return eos(stream, null, opts); - if (!opts) opts = {}; - callback = once(callback || noop); - var readable = opts.readable || opts.readable !== false && stream.readable; - var writable = opts.writable || opts.writable !== false && stream.writable; - var onlegacyfinish = function onlegacyfinish() { - if (!stream.writable) onfinish(); - }; - var writableEnded = stream._writableState && stream._writableState.finished; - var onfinish = function onfinish() { - writable = false; - writableEnded = true; - if (!readable) callback.call(stream); - }; - var readableEnded = stream._readableState && stream._readableState.endEmitted; - var onend = function onend() { - readable = false; - readableEnded = true; - if (!writable) callback.call(stream); - }; - var onerror = function onerror(err) { - callback.call(stream, err); - }; - var onclose = function onclose() { - var err; - if (readable && !readableEnded) { - if (!stream._readableState || !stream._readableState.ended) err = new ERR_STREAM_PREMATURE_CLOSE(); - return callback.call(stream, err); - } - if (writable && !writableEnded) { - if (!stream._writableState || !stream._writableState.ended) err = new ERR_STREAM_PREMATURE_CLOSE(); - return callback.call(stream, err); - } - }; - var onrequest = function onrequest() { - stream.req.on('finish', onfinish); - }; - if (isRequest(stream)) { - stream.on('complete', onfinish); - stream.on('abort', onclose); - if (stream.req) onrequest();else stream.on('request', onrequest); - } else if (writable && !stream._writableState) { - // legacy streams - stream.on('end', onlegacyfinish); - stream.on('close', onlegacyfinish); - } - stream.on('end', onend); - stream.on('finish', onfinish); - if (opts.error !== false) stream.on('error', onerror); - stream.on('close', onclose); - return function () { - stream.removeListener('complete', onfinish); - stream.removeListener('abort', onclose); - stream.removeListener('request', onrequest); - if (stream.req) stream.req.removeListener('finish', onfinish); - stream.removeListener('end', onlegacyfinish); - stream.removeListener('close', onlegacyfinish); - stream.removeListener('finish', onfinish); - stream.removeListener('end', onend); - stream.removeListener('error', onerror); - stream.removeListener('close', onclose); - }; -} -module.exports = eos; -},{"../../../errors":233}],243:[function(require,module,exports){ -module.exports = function () { - throw new Error('Readable.from is not available in the browser') -}; - -},{}],244:[function(require,module,exports){ -// Ported from https://github.com/mafintosh/pump with -// permission from the author, Mathias Buus (@mafintosh). - -'use strict'; - -var eos; -function once(callback) { - var called = false; - return function () { - if (called) return; - called = true; - callback.apply(void 0, arguments); - }; -} -var _require$codes = require('../../../errors').codes, - ERR_MISSING_ARGS = _require$codes.ERR_MISSING_ARGS, - ERR_STREAM_DESTROYED = _require$codes.ERR_STREAM_DESTROYED; -function noop(err) { - // Rethrow the error if it exists to avoid swallowing it - if (err) throw err; -} -function isRequest(stream) { - return stream.setHeader && typeof stream.abort === 'function'; -} -function destroyer(stream, reading, writing, callback) { - callback = once(callback); - var closed = false; - stream.on('close', function () { - closed = true; - }); - if (eos === undefined) eos = require('./end-of-stream'); - eos(stream, { - readable: reading, - writable: writing - }, function (err) { - if (err) return callback(err); - closed = true; - callback(); - }); - var destroyed = false; - return function (err) { - if (closed) return; - if (destroyed) return; - destroyed = true; - - // request.destroy just do .end - .abort is what we want - if (isRequest(stream)) return stream.abort(); - if (typeof stream.destroy === 'function') return stream.destroy(); - callback(err || new ERR_STREAM_DESTROYED('pipe')); - }; -} -function call(fn) { - fn(); -} -function pipe(from, to) { - return from.pipe(to); -} -function popCallback(streams) { - if (!streams.length) return noop; - if (typeof streams[streams.length - 1] !== 'function') return noop; - return streams.pop(); -} -function pipeline() { - for (var _len = arguments.length, streams = new Array(_len), _key = 0; _key < _len; _key++) { - streams[_key] = arguments[_key]; - } - var callback = popCallback(streams); - if (Array.isArray(streams[0])) streams = streams[0]; - if (streams.length < 2) { - throw new ERR_MISSING_ARGS('streams'); - } - var error; - var destroys = streams.map(function (stream, i) { - var reading = i < streams.length - 1; - var writing = i > 0; - return destroyer(stream, reading, writing, function (err) { - if (!error) error = err; - if (err) destroys.forEach(call); - if (reading) return; - destroys.forEach(call); - callback(error); - }); - }); - return streams.reduce(pipe); -} -module.exports = pipeline; -},{"../../../errors":233,"./end-of-stream":242}],245:[function(require,module,exports){ -'use strict'; - -var ERR_INVALID_OPT_VALUE = require('../../../errors').codes.ERR_INVALID_OPT_VALUE; -function highWaterMarkFrom(options, isDuplex, duplexKey) { - return options.highWaterMark != null ? options.highWaterMark : isDuplex ? options[duplexKey] : null; -} -function getHighWaterMark(state, options, duplexKey, isDuplex) { - var hwm = highWaterMarkFrom(options, isDuplex, duplexKey); - if (hwm != null) { - if (!(isFinite(hwm) && Math.floor(hwm) === hwm) || hwm < 0) { - var name = isDuplex ? duplexKey : 'highWaterMark'; - throw new ERR_INVALID_OPT_VALUE(name, hwm); - } - return Math.floor(hwm); - } - - // Default value - return state.objectMode ? 16 : 16 * 1024; -} -module.exports = { - getHighWaterMark: getHighWaterMark -}; -},{"../../../errors":233}],246:[function(require,module,exports){ -arguments[4][216][0].apply(exports,arguments) -},{"dup":216,"events":129}],247:[function(require,module,exports){ -arguments[4][218][0].apply(exports,arguments) -},{"dup":218,"safe-buffer":221}],248:[function(require,module,exports){ -(function (setImmediate,clearImmediate){(function (){ -var nextTick = require('process/browser.js').nextTick; -var apply = Function.prototype.apply; -var slice = Array.prototype.slice; -var immediateIds = {}; -var nextImmediateId = 0; - -// DOM APIs, for completeness - -exports.setTimeout = function() { - return new Timeout(apply.call(setTimeout, window, arguments), clearTimeout); -}; -exports.setInterval = function() { - return new Timeout(apply.call(setInterval, window, arguments), clearInterval); -}; -exports.clearTimeout = -exports.clearInterval = function(timeout) { timeout.close(); }; - -function Timeout(id, clearFn) { - this._id = id; - this._clearFn = clearFn; -} -Timeout.prototype.unref = Timeout.prototype.ref = function() {}; -Timeout.prototype.close = function() { - this._clearFn.call(window, this._id); -}; - -// Does not start the time, just sets up the members needed. -exports.enroll = function(item, msecs) { - clearTimeout(item._idleTimeoutId); - item._idleTimeout = msecs; -}; - -exports.unenroll = function(item) { - clearTimeout(item._idleTimeoutId); - item._idleTimeout = -1; -}; - -exports._unrefActive = exports.active = function(item) { - clearTimeout(item._idleTimeoutId); - - var msecs = item._idleTimeout; - if (msecs >= 0) { - item._idleTimeoutId = setTimeout(function onTimeout() { - if (item._onTimeout) - item._onTimeout(); - }, msecs); - } -}; - -// That's not how node.js implements it but the exposed api is the same. -exports.setImmediate = typeof setImmediate === "function" ? setImmediate : function(fn) { - var id = nextImmediateId++; - var args = arguments.length < 2 ? false : slice.call(arguments, 1); - - immediateIds[id] = true; - - nextTick(function onNextTick() { - if (immediateIds[id]) { - // fn.call() is faster so we optimize for the common use-case - // @see http://jsperf.com/call-apply-segu - if (args) { - fn.apply(null, args); - } else { - fn.call(null); - } - // Prevent ids from leaking - exports.clearImmediate(id); - } - }); - - return id; -}; - -exports.clearImmediate = typeof clearImmediate === "function" ? clearImmediate : function(id) { - delete immediateIds[id]; -}; -}).call(this)}).call(this,require("timers").setImmediate,require("timers").clearImmediate) -},{"process/browser.js":199,"timers":248}],249:[function(require,module,exports){ -(function (global){(function (){ - -/** - * Module exports. - */ - -module.exports = deprecate; - -/** - * Mark that a method should not be used. - * Returns a modified function which warns once by default. - * - * If `localStorage.noDeprecation = true` is set, then it is a no-op. - * - * If `localStorage.throwDeprecation = true` is set, then deprecated functions - * will throw an Error when invoked. - * - * If `localStorage.traceDeprecation = true` is set, then deprecated functions - * will invoke `console.trace()` instead of `console.error()`. - * - * @param {Function} fn - the function to deprecate - * @param {String} msg - the string to print to the console when `fn` is invoked - * @returns {Function} a new "deprecated" version of `fn` - * @api public - */ - -function deprecate (fn, msg) { - if (config('noDeprecation')) { - return fn; - } - - var warned = false; - function deprecated() { - if (!warned) { - if (config('throwDeprecation')) { - throw new Error(msg); - } else if (config('traceDeprecation')) { - console.trace(msg); - } else { - console.warn(msg); - } - warned = true; - } - return fn.apply(this, arguments); - } - - return deprecated; -} - -/** - * Checks `localStorage` for boolean values for the given `name`. - * - * @param {String} name - * @returns {Boolean} - * @api private - */ - -function config (name) { - // accessing global.localStorage can trigger a DOMException in sandboxed iframes - try { - if (!global.localStorage) return false; - } catch (_) { - return false; - } - var val = global.localStorage[name]; - if (null == val) return false; - return String(val).toLowerCase() === 'true'; -} - -}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{}],250:[function(require,module,exports){ -var indexOf = function (xs, item) { - if (xs.indexOf) return xs.indexOf(item); - else for (var i = 0; i < xs.length; i++) { - if (xs[i] === item) return i; - } - return -1; -}; -var Object_keys = function (obj) { - if (Object.keys) return Object.keys(obj) - else { - var res = []; - for (var key in obj) res.push(key) - return res; - } -}; - -var forEach = function (xs, fn) { - if (xs.forEach) return xs.forEach(fn) - else for (var i = 0; i < xs.length; i++) { - fn(xs[i], i, xs); - } -}; - -var defineProp = (function() { - try { - Object.defineProperty({}, '_', {}); - return function(obj, name, value) { - Object.defineProperty(obj, name, { - writable: true, - enumerable: false, - configurable: true, - value: value - }) - }; - } catch(e) { - return function(obj, name, value) { - obj[name] = value; - }; - } -}()); - -var globals = ['Array', 'Boolean', 'Date', 'Error', 'EvalError', 'Function', -'Infinity', 'JSON', 'Math', 'NaN', 'Number', 'Object', 'RangeError', -'ReferenceError', 'RegExp', 'String', 'SyntaxError', 'TypeError', 'URIError', -'decodeURI', 'decodeURIComponent', 'encodeURI', 'encodeURIComponent', 'escape', -'eval', 'isFinite', 'isNaN', 'parseFloat', 'parseInt', 'undefined', 'unescape']; - -function Context() {} -Context.prototype = {}; - -var Script = exports.Script = function NodeScript (code) { - if (!(this instanceof Script)) return new Script(code); - this.code = code; -}; - -Script.prototype.runInContext = function (context) { - if (!(context instanceof Context)) { - throw new TypeError("needs a 'context' argument."); - } - - var iframe = document.createElement('iframe'); - if (!iframe.style) iframe.style = {}; - iframe.style.display = 'none'; - - document.body.appendChild(iframe); - - var win = iframe.contentWindow; - var wEval = win.eval, wExecScript = win.execScript; - - if (!wEval && wExecScript) { - // win.eval() magically appears when this is called in IE: - wExecScript.call(win, 'null'); - wEval = win.eval; - } - - forEach(Object_keys(context), function (key) { - win[key] = context[key]; - }); - forEach(globals, function (key) { - if (context[key]) { - win[key] = context[key]; - } - }); - - var winKeys = Object_keys(win); - - var res = wEval.call(win, this.code); - - forEach(Object_keys(win), function (key) { - // Avoid copying circular objects like `top` and `window` by only - // updating existing context properties or new properties in the `win` - // that was only introduced after the eval. - if (key in context || indexOf(winKeys, key) === -1) { - context[key] = win[key]; - } - }); - - forEach(globals, function (key) { - if (!(key in context)) { - defineProp(context, key, win[key]); - } - }); - - document.body.removeChild(iframe); - - return res; -}; - -Script.prototype.runInThisContext = function () { - return eval(this.code); // maybe... -}; - -Script.prototype.runInNewContext = function (context) { - var ctx = Script.createContext(context); - var res = this.runInContext(ctx); - - if (context) { - forEach(Object_keys(ctx), function (key) { - context[key] = ctx[key]; - }); - } - - return res; -}; - -forEach(Object_keys(Script.prototype), function (name) { - exports[name] = Script[name] = function (code) { - var s = Script(code); - return s[name].apply(s, [].slice.call(arguments, 1)); - }; -}); - -exports.isContext = function (context) { - return context instanceof Context; -}; - -exports.createScript = function (code) { - return exports.Script(code); -}; - -exports.createContext = Script.createContext = function (context) { - var copy = new Context(); - if(typeof context === 'object') { - forEach(Object_keys(context), function (key) { - copy[key] = context[key]; - }); - } - return copy; -}; - -},{}]},{},[1]); diff --git a/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/assets/js/main.js b/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/assets/js/main.js deleted file mode 100644 index 3c5749421fb..00000000000 --- a/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/assets/js/main.js +++ /dev/null @@ -1,48 +0,0 @@ -const cose = require('cose-js'); - -/// Signs the message bytes and returns a COSE signature. -async function _signMessage(bytesHex) { - const bytes = _hexStringToUint8Array(bytesHex); - const headers = { - p: { alg: 'ES256' }, - u: { kid: '11' } - }; - const signer = { - key: { - d: Buffer.from('6c1382765aec5358f117733d281c1c7bdc39884d04a45a1e6c67c858bc206c19', 'hex') - } - }; - - const buf = await cose.sign.create(headers, bytes, signer); - return buf.toString('hex'); -} - - -// Converts a hex string into a byte array. -function _hexStringToUint8Array(hexString) { - // Ensure the hex string length is even - if (hexString.length % 2 !== 0) { - throw new Error('Invalid hex string'); - } - - // Create a Uint8Array - const byteArray = new Uint8Array(hexString.length / 2); - - // Parse the hex string into byte values - for (let i = 0; i < hexString.length; i += 2) { - byteArray[i / 2] = parseInt(hexString.substr(i, 2), 16); - } - - return byteArray; -} - -// A namespace containing the JS functions that -// can be executed from dart side -const catalyst_cose = { - signMessage: _signMessage, -} - -// Expose catalyst cose as globally accessible -// so that we can call it via catalyst_cose.function_name() from -// other scripts or dart without needing to care about module imports -window.catalyst_cose = catalyst_cose; \ No newline at end of file diff --git a/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/assets/js/package.json b/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/assets/js/package.json deleted file mode 100644 index 721b7002fdc..00000000000 --- a/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/assets/js/package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "catalyst-cose", - "version": "1.0.0", - "description": "", - "main": "main.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "keywords": [], - "author": "", - "license": "(MIT OR Apache-2.0)", - "dependencies": { - "browserify": "^17.0.0", - "cose-js": "^0.9.0" - } -} diff --git a/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/example/main.dart b/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/example/main.dart deleted file mode 100644 index ab33a448715..00000000000 --- a/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/example/main.dart +++ /dev/null @@ -1,4 +0,0 @@ -void main() { - // ignore: avoid_print - print('Example is located in catalyst_cose/example directory.'); -} diff --git a/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/lib/catalyst_cose_web.dart b/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/lib/catalyst_cose_web.dart deleted file mode 100644 index 916dded404a..00000000000 --- a/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/lib/catalyst_cose_web.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'dart:js_interop'; - -import 'package:catalyst_cose_platform_interface/catalyst_cose_platform_interface.dart'; -import 'package:catalyst_cose_web/src/interop/catalyst_cose_interop.dart' - as interop; -import 'package:cbor/cbor.dart'; -import 'package:convert/convert.dart'; -import 'package:flutter_web_plugins/flutter_web_plugins.dart' show Registrar; - -/// The web implementation of [CatalystCosePlatform]. -/// -/// This class implements the `package:catalyst_cose` functionality -/// for the web. -class CatalystCoseWeb extends CatalystCosePlatform { - /// A constructor that allows tests to override the window object used by the - /// plugin. - CatalystCoseWeb(); - - /// Registers this class as the default instance of - /// [CatalystCosePlatform]. - static void registerWith(Registrar registrar) { - CatalystCosePlatform.instance = CatalystCoseWeb(); - } - - @override - Future signMessage(List message) { - return interop - .signMessage(hex.encode(message).toJS) - .toDart - .then((e) => cbor.decode(hex.decode(e.toDart))); - } -} diff --git a/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/lib/src/interop/catalyst_cose_interop.dart b/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/lib/src/interop/catalyst_cose_interop.dart deleted file mode 100644 index 319ff1d7621..00000000000 --- a/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/lib/src/interop/catalyst_cose_interop.dart +++ /dev/null @@ -1,8 +0,0 @@ -@JS('catalyst_cose') -library catalyst_cose_interop; - -import 'dart:js_interop'; - -/// Signs the message [bytes] and returns a resulting COSE signature. -@JS() -external JSPromise signMessage(JSString bytes); diff --git a/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/pubspec.yaml b/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/pubspec.yaml deleted file mode 100644 index 221cf8a16e8..00000000000 --- a/catalyst_voices_packages/catalyst_cose/catalyst_cose_web/pubspec.yaml +++ /dev/null @@ -1,37 +0,0 @@ -name: catalyst_cose_web -description: Web platform implementation of catalyst_cose. A Flutter plugin implementing CBOR Object Signing and Encryption (RFC 8152). -repository: https://github.com/input-output-hk/catalyst-voices/tree/main/catalyst_voices_packages/catalyst_cose/catalyst_cose_web -issue_tracker: https://github.com/input-output-hk/catalyst-voices/issues -topics: [cryptography, encryption, codec] -version: 0.1.0 - -environment: - sdk: ">=3.3.4 <4.0.0" - flutter: ">=3.22.1" - -flutter: - plugin: - implements: catalyst_cose - platforms: - web: - pluginClass: CatalystCoseWeb - fileName: catalyst_cose_web.dart - - assets: - - assets/js/ - -dependencies: - catalyst_cose_platform_interface: ^0.1.1 - cbor: ^6.2.0 - convert: ^3.1.1 - flutter: - sdk: flutter - flutter_web_plugins: - sdk: flutter - web: ^0.5.0 - -dev_dependencies: - catalyst_analysis: - path: ../../catalyst_analysis - flutter_test: - sdk: flutter diff --git a/catalyst_voices_packages/catalyst_cose/example/main.dart b/catalyst_voices_packages/catalyst_cose/example/main.dart new file mode 100644 index 00000000000..5afbf2d0b1a --- /dev/null +++ b/catalyst_voices_packages/catalyst_cose/example/main.dart @@ -0,0 +1,38 @@ +// ignore_for_file: avoid_print + +import 'dart:convert'; + +import 'package:catalyst_cose/catalyst_cose.dart'; +import 'package:cbor/cbor.dart'; +import 'package:convert/convert.dart'; +import 'package:cryptography/cryptography.dart'; + +Future main() async { + final algorithm = Ed25519(); + final keyPair = await algorithm.newKeyPairFromSeed(List.filled(32, 0)); + final privateKey = await keyPair.extractPrivateKeyBytes(); + final publicKey = await keyPair.extractPublicKey().then((e) => e.bytes); + + final payload = utf8.encode('This is the content.'); + + final coseSign1 = await CatalystCose.sign1( + privateKey: privateKey, + payload: payload, + kid: CborBytes(publicKey), + ); + + final verified = await CatalystCose.verifyCoseSign1( + coseSign1: coseSign1, + publicKey: publicKey, + ); + + print('COSE_SIGN1:'); + print(hex.encode(cbor.encode(coseSign1))); + print('verified: $verified'); + + assert( + verified, + 'The signature proves that given COSE_SIGN1 structure has been ' + 'signed by the owner of the given public key', + ); +} diff --git a/catalyst_voices_packages/catalyst_cose/lib/catalyst_cose.dart b/catalyst_voices_packages/catalyst_cose/lib/catalyst_cose.dart new file mode 100644 index 00000000000..195f3996889 --- /dev/null +++ b/catalyst_voices_packages/catalyst_cose/lib/catalyst_cose.dart @@ -0,0 +1 @@ +export 'src/catalyst_cose.dart'; diff --git a/catalyst_voices_packages/catalyst_cose/lib/src/catalyst_cose.dart b/catalyst_voices_packages/catalyst_cose/lib/src/catalyst_cose.dart new file mode 100644 index 00000000000..d82a3a679f1 --- /dev/null +++ b/catalyst_voices_packages/catalyst_cose/lib/src/catalyst_cose.dart @@ -0,0 +1,149 @@ +import 'package:cbor/cbor.dart'; +import 'package:cryptography/cryptography.dart'; + +/// A dart plugin implementing CBOR Object Signing and Encryption +/// [RFC-9052](https://datatracker.ietf.org/doc/rfc9052/), +/// [RFC 9053](https://datatracker.ietf.org/doc/rfc9053/). +final class CatalystCose { + static const int _coseSign1Tag = 18; + static const int _algKey = 1; + static const int _kidKey = 4; + static const int _eddsaAlg = 3; + + CatalystCose._(); + + /// Signs the [payload] and returns a [CborValue] representing + /// a COSE_SIGN1 structure. + /// + /// This [kid] parameter identifies one piece of data that can be + /// used as input to find the needed cryptographic key. + /// + /// Limited to EdDSA algorithm with Ed25519 curve. + static Future sign1({ + required List privateKey, + required List payload, + CborValue? kid, + }) async { + final algorithm = Ed25519(); + final keyPair = await algorithm.newKeyPairFromSeed(privateKey); + + final protectedHeader = CborBytes( + cbor.encode( + CborMap({ + const CborSmallInt(_algKey): const CborSmallInt(_eddsaAlg), + if (kid != null) const CborSmallInt(_kidKey): kid, + }), + ), + ); + + final unprotectedHeader = CborMap({}); + + final sigStructure = _createCoseSign1SigStructure( + protectedHeader: protectedHeader, + payload: CborBytes(payload), + ); + + final toBeSigned = cbor.encode( + CborBytes( + cbor.encode(sigStructure), + ), + ); + + final signature = await algorithm.sign( + toBeSigned, + keyPair: keyPair, + ); + + final coseSign1Structure = CborList( + [ + protectedHeader, + unprotectedHeader, + CborBytes(payload), + CborBytes(signature.bytes), + ], + tags: [_coseSign1Tag], + ); + + return coseSign1Structure; + } + + /// Verifies whether the given COSE_SIGN1 structure's signature + /// was created using the provided public key. + /// + /// Limited to EdDSA algorithm with Ed25519 curve. + /// + /// Returns `true` if the signature is valid, `false` otherwise. + static Future verifyCoseSign1({ + required CborValue coseSign1, + required List publicKey, + }) async { + final algorithm = Ed25519(); + + if (coseSign1 is! CborList || + coseSign1.tags.contains(_coseSign1Tag) != true) { + return false; + } + + final cborList = coseSign1; + if (cborList.length != 4) { + return false; + } + + final protectedHeader = cborList[0]; + final unprotectedHeader = cborList[1]; + final payload = cborList[2]; + final signature = cborList[3]; + + if (protectedHeader is! CborBytes || + unprotectedHeader is! CborMap || + payload is! CborBytes || + signature is! CborBytes) { + return false; + } + + final signatureBytes = signature.bytes; + + final sigStructure = _createCoseSign1SigStructure( + protectedHeader: protectedHeader, + payload: payload, + ); + + final toBeSigned = cbor.encode( + CborBytes( + cbor.encode(sigStructure), + ), + ); + + try { + final verified = await algorithm.verify( + toBeSigned, + signature: Signature( + signatureBytes, + publicKey: SimplePublicKey(publicKey, type: KeyPairType.ed25519), + ), + ); + return verified; + } catch (e) { + return false; + } + } + + static CborValue _createCoseSign1SigStructure({ + required CborValue protectedHeader, + required CborValue payload, + }) { + return CborList([ + // Context text identifying the context of the signature + CborString('Signature1'), + + // The protected attributes from the body structure + protectedHeader, + + // External supplied data, empty since not supplied + CborBytes([]), + + // Payload to be signed + payload, + ]); + } +} diff --git a/catalyst_voices_packages/catalyst_cose/pubspec.yaml b/catalyst_voices_packages/catalyst_cose/pubspec.yaml new file mode 100644 index 00000000000..9499e4281f1 --- /dev/null +++ b/catalyst_voices_packages/catalyst_cose/pubspec.yaml @@ -0,0 +1,18 @@ +name: catalyst_cose +description: A dart plugin implementing CBOR Object Signing and Encryption (RFC 9052, RFC 9053). +repository: https://github.com/input-output-hk/catalyst-voices/tree/main/catalyst_voices_packages/catalyst_cose +issue_tracker: https://github.com/input-output-hk/catalyst-voices/issues +topics: [cryptography, encryption, codec] +version: 0.3.0 + +environment: + sdk: ">=3.5.0 <4.0.0" + +dependencies: + cbor: ^6.2.0 + convert: ^3.1.1 + cryptography: ^2.7.0 + +dev_dependencies: + catalyst_analysis: ^2.0.0 + test: ^1.24.9 diff --git a/catalyst_voices_packages/catalyst_cose/test/catalyst_cose_test.dart b/catalyst_voices_packages/catalyst_cose/test/catalyst_cose_test.dart new file mode 100644 index 00000000000..39a0f187ee4 --- /dev/null +++ b/catalyst_voices_packages/catalyst_cose/test/catalyst_cose_test.dart @@ -0,0 +1,157 @@ +import 'package:catalyst_cose/catalyst_cose.dart'; +import 'package:cbor/cbor.dart'; +import 'package:convert/convert.dart'; +import 'package:cryptography/cryptography.dart'; +import 'package:test/test.dart'; + +const int _coseSign1Tag = 18; + +void main() { + group('CatalystCose', () { + late Ed25519 algorithm; + late SimpleKeyPair keyPair; + late List privateKey; + late SimplePublicKey publicKey; + + setUp(() async { + // Initialize the Ed25519 algorithm and generate a key pair + algorithm = Ed25519(); + keyPair = await algorithm.newKeyPairFromSeed(List.filled(32, 0)); + privateKey = await keyPair.extractPrivateKeyBytes(); + publicKey = await keyPair.extractPublicKey(); + }); + + test('sign1 generates a valid COSE_SIGN1 structure', () async { + final payload = List.generate(10, (i) => i); // Example payload + + // Call the sign1 method + final coseSign1 = await CatalystCose.sign1( + privateKey: privateKey, + payload: payload, + kid: CborBytes(publicKey.bytes), + ); + + // Verify that the COSE_SIGN1 structure is a valid CborList + expect(coseSign1, isA()); + + final cborList = coseSign1 as CborList; + expect(cborList.length, 4); // Should contain 4 items + + final protectedHeader = cborList[0]; + expect(protectedHeader, isA()); + + final unprotectedHeader = cborList[1]; + expect(unprotectedHeader, isA()); + + final signedPayload = cborList[2]; + expect(signedPayload, isA()); + expect( + (signedPayload as CborBytes).bytes, + payload, + ); // Check that the payload is as expected + + final signature = cborList[3]; + // The actual signature bytes are not known in advance; + // just verify the type + expect(signature, isA()); + }); + + test('sign1 generates a valid cbor', () async { + final payload = List.generate(10, (i) => i); // Example payload + + // Call the sign1 method + final coseSign1 = await CatalystCose.sign1( + privateKey: privateKey, + payload: payload, + kid: CborBytes(publicKey.bytes), + ); + + expect( + hex.encode(cbor.encode(coseSign1)), + equals( + 'd2845826a201030458203b6a27bcceb6a42d62a3a8d02a6f0d736532157' + '71de243a63ac048a18b59da29a04a00010203040506070809584007ed6c' + '8a0b9bad9c375329a1d2de50d777f7f348c5597e3d963b80b9fd3488715' + '1dc8f0b2a4690f10f3256a7c883b6bd559be4195ca78fccc694f986ed45' + 'b80e', + ), + ); + }); + + test('verifyCoseSign1 validates correct signature', () async { + final payload = List.generate(10, (i) => i); // Example payload + + // Call the sign1 method + final coseSign1 = await CatalystCose.sign1( + privateKey: privateKey, + payload: payload, + kid: CborBytes(publicKey.bytes), + ); + + // Verify the signature + final isValid = await CatalystCose.verifyCoseSign1( + coseSign1: coseSign1, + publicKey: publicKey.bytes, + ); + + expect(isValid, true); // Check that the signature is valid + }); + + test('verifyCoseSign1 rejects invalid signatures', () async { + final payload = List.generate(10, (i) => i); // Example payload + + // Call the sign1 method + final coseSign1 = await CatalystCose.sign1( + privateKey: privateKey, + payload: payload, + kid: CborBytes(publicKey.bytes), + ); + + // Tamper with the signature to invalidate it + final cborList = coseSign1 as CborList; + final tamperedSignature = CborBytes( + List.generate(64, (i) => i), + ); // Example tampered signature + final tamperedCoseSign1 = CborList( + [ + cborList[0], // protected header + cborList[1], // unprotected header + cborList[2], // payload + tamperedSignature, + ], + tags: [_coseSign1Tag], + ); + + // Verify the tampered signature + final isValid = await CatalystCose.verifyCoseSign1( + coseSign1: tamperedCoseSign1, + publicKey: publicKey.bytes, + ); + + expect(isValid, false); // Check that the signature is invalid + }); + + test('verifyCoseSign1 handles invalid COSE_SIGN1 structure', () async { + // Construct an invalid COSE_SIGN1 structure + final invalidCoseSign1 = CborList( + [ + CborBytes( + List.generate(1, (i) => i), + ), // Invalid protected header + CborMap({}), + CborBytes([]), + CborBytes(List.generate(64, (i) => i)), // Invalid signature + ], + tags: [_coseSign1Tag], + ); + + // Verify the invalid COSE_SIGN1 structure + final isValid = await CatalystCose.verifyCoseSign1( + coseSign1: invalidCoseSign1, + publicKey: publicKey.bytes, + ); + + expect(isValid, false); // Check that the verification fails + }); + }); +} diff --git a/cspell.json b/cspell.json index b76b0da34e4..c42691faf8f 100644 --- a/cspell.json +++ b/cspell.json @@ -174,8 +174,9 @@ "styles.min.css", "web-components.min.js", "**/generated/**", + "**/GeneratedPluginRegistrant.swift", "utilities/catalyst_voices_remote_widgets/example/**/**", - "catalyst_voices_packages/catalyst_cose/catalyst_cose_web/assets/js/catalyst_cose.js", + "utilities/poc_local_storage/**/**", "**/*.svg" ], "enableFiletypes": [ diff --git a/melos.yaml b/melos.yaml index 067d6ca79ad..8c95322cce0 100644 --- a/melos.yaml +++ b/melos.yaml @@ -6,7 +6,7 @@ packages: - catalyst_voices/uikit_example - catalyst_voices/packages/** - catalyst_voices_packages/** - - utilities/* + - utilities/** command: version: @@ -15,15 +15,17 @@ command: bootstrap: runPubGetInParallel: true environment: - sdk: ">=3.3.4 <4.0.0" - flutter: ">=3.22.1" + sdk: ">=3.5.0 <4.0.0" + flutter: ">=3.24.1" dependencies: bloc_concurrency: ^0.2.2 - bloc: ^8.1.2 collection: ^1.18.0 cryptography: ^2.7.0 equatable: ^2.0.5 + flutter_bloc: ^8.1.5 flutter_localized_locales: ^2.0.5 + flutter_quill: ^10.5.0 + flutter_quill_extensions: ^10.5.0 formz: ^0.7.0 meta: ^1.10.0 result_type: ^0.2.0 @@ -33,9 +35,15 @@ command: cbor: ^6.2.0 convert: ^3.1.1 pinenacl: ^0.6.0 + ulid: ^2.0.0 + sentry_flutter: ^8.8.0 + # TODO(dtscalac): win32 dependency is just a transitive dependency and shouldn't be imported + # but here we import it explicitly to make sure the latest version is used which addresses + # the problem from here: https://github.com/jonataslaw/get_cli/issues/263 + win32: ^5.5.4 dev_dependencies: test: ^1.24.9 - build_runner: ^2.3.3 + build_runner: ^2.4.12 mocktail: ^1.0.1 scripts: diff --git a/utilities/catalyst_voices_remote_widgets/example/pubspec.yaml b/utilities/catalyst_voices_remote_widgets/example/pubspec.yaml index 797c8fde870..1c39718d09f 100644 --- a/utilities/catalyst_voices_remote_widgets/example/pubspec.yaml +++ b/utilities/catalyst_voices_remote_widgets/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: 'none' version: 1.0.0+1 environment: - sdk: '>=3.3.1 <4.0.0' + sdk: ">=3.5.0 <4.0.0" dependencies: catalyst_voices_remote_widgets: @@ -15,8 +15,7 @@ dependencies: rfw: ^1.0.26 dev_dependencies: - catalyst_analysis: - path: ../../../catalyst_voices_packages/catalyst_analysis + catalyst_analysis: ^2.0.0 flutter: uses-material-design: true \ No newline at end of file diff --git a/utilities/catalyst_voices_remote_widgets/pubspec.yaml b/utilities/catalyst_voices_remote_widgets/pubspec.yaml index 896b72d3834..c065b3537c5 100644 --- a/utilities/catalyst_voices_remote_widgets/pubspec.yaml +++ b/utilities/catalyst_voices_remote_widgets/pubspec.yaml @@ -4,8 +4,8 @@ version: 0.1.0+1 publish_to: none environment: - sdk: ">=3.3.4 <4.0.0" - flutter: ">=3.22.1" + sdk: ">=3.5.0 <4.0.0" + flutter: ">=3.24.1" dependencies: flutter: @@ -14,8 +14,7 @@ dependencies: rfw: ^1.0.26 dev_dependencies: - catalyst_analysis: - path: ../../catalyst_voices_packages/catalyst_analysis + catalyst_analysis: ^2.0.0 flutter: uses-material-design: true diff --git a/utilities/poc_local_storage/.firebaserc b/utilities/poc_local_storage/.firebaserc new file mode 100644 index 00000000000..40aa011de48 --- /dev/null +++ b/utilities/poc_local_storage/.firebaserc @@ -0,0 +1,5 @@ +{ + "projects": { + "default": "poc-catalyst-local-web-storage" + } +} diff --git a/utilities/poc_local_storage/.gitignore b/utilities/poc_local_storage/.gitignore new file mode 100644 index 00000000000..afb7bc1a687 --- /dev/null +++ b/utilities/poc_local_storage/.gitignore @@ -0,0 +1,44 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.pub-cache/ +.pub/ +/build/** +.firebase/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/utilities/poc_local_storage/.metadata b/utilities/poc_local_storage/.metadata new file mode 100644 index 00000000000..6eb54a17b48 --- /dev/null +++ b/utilities/poc_local_storage/.metadata @@ -0,0 +1,45 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "761747bfc538b5af34aa0d3fac380f1bc331ec49" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: android + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: ios + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: linux + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: macos + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: web + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: windows + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/utilities/poc_local_storage/README.md b/utilities/poc_local_storage/README.md new file mode 100644 index 00000000000..de93ae4b22b --- /dev/null +++ b/utilities/poc_local_storage/README.md @@ -0,0 +1,3 @@ +# Poc Local Storage + +For more read #644. diff --git a/utilities/poc_local_storage/analysis_options.yaml b/utilities/poc_local_storage/analysis_options.yaml new file mode 100644 index 00000000000..73ff5052f88 --- /dev/null +++ b/utilities/poc_local_storage/analysis_options.yaml @@ -0,0 +1,8 @@ +include: package:catalyst_analysis/analysis_options.yaml + +analyzer: + exclude: [build/**, lib/*.g.dart, lib/generated/**] + +linter: + rules: + public_member_api_docs: false diff --git a/utilities/poc_local_storage/android/.gitignore b/utilities/poc_local_storage/android/.gitignore new file mode 100644 index 00000000000..6f568019d3c --- /dev/null +++ b/utilities/poc_local_storage/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/utilities/poc_local_storage/android/app/build.gradle b/utilities/poc_local_storage/android/app/build.gradle new file mode 100644 index 00000000000..91409fe7893 --- /dev/null +++ b/utilities/poc_local_storage/android/app/build.gradle @@ -0,0 +1,58 @@ +plugins { + id "com.android.application" + id "kotlin-android" + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id "dev.flutter.flutter-gradle-plugin" +} + +def localProperties = new Properties() +def localPropertiesFile = rootProject.file("local.properties") +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader("UTF-8") { reader -> + localProperties.load(reader) + } +} + +def flutterVersionCode = localProperties.getProperty("flutter.versionCode") +if (flutterVersionCode == null) { + flutterVersionCode = "1" +} + +def flutterVersionName = localProperties.getProperty("flutter.versionName") +if (flutterVersionName == null) { + flutterVersionName = "1.0" +} + +android { + namespace = "com.example.poc_local_storage" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "com.example.poc_local_storage" + // You can update the following values to match your application needs. + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. + minSdk = flutter.minSdkVersion + targetSdk = flutter.targetSdkVersion + versionCode = flutterVersionCode.toInteger() + versionName = flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig = signingConfigs.debug + } + } +} + +flutter { + source = "../.." +} diff --git a/utilities/poc_local_storage/android/app/src/debug/AndroidManifest.xml b/utilities/poc_local_storage/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 00000000000..399f6981d5d --- /dev/null +++ b/utilities/poc_local_storage/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/utilities/poc_local_storage/android/app/src/main/AndroidManifest.xml b/utilities/poc_local_storage/android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000000..1206bd2b008 --- /dev/null +++ b/utilities/poc_local_storage/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/utilities/poc_local_storage/android/app/src/main/kotlin/com/example/poc_local_storage/MainActivity.kt b/utilities/poc_local_storage/android/app/src/main/kotlin/com/example/poc_local_storage/MainActivity.kt new file mode 100644 index 00000000000..f626d2112be --- /dev/null +++ b/utilities/poc_local_storage/android/app/src/main/kotlin/com/example/poc_local_storage/MainActivity.kt @@ -0,0 +1,5 @@ +package com.example.poc_local_storage + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() diff --git a/utilities/poc_local_storage/android/app/src/main/res/drawable-v21/launch_background.xml b/utilities/poc_local_storage/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 00000000000..f74085f3f6a --- /dev/null +++ b/utilities/poc_local_storage/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/utilities/poc_local_storage/android/app/src/main/res/drawable/launch_background.xml b/utilities/poc_local_storage/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 00000000000..304732f8842 --- /dev/null +++ b/utilities/poc_local_storage/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/utilities/poc_local_storage/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/utilities/poc_local_storage/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 00000000000..db77bb4b7b0 Binary files /dev/null and b/utilities/poc_local_storage/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/utilities/poc_local_storage/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/utilities/poc_local_storage/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 00000000000..17987b79bb8 Binary files /dev/null and b/utilities/poc_local_storage/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/utilities/poc_local_storage/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/utilities/poc_local_storage/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 00000000000..09d4391482b Binary files /dev/null and b/utilities/poc_local_storage/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/utilities/poc_local_storage/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/utilities/poc_local_storage/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 00000000000..d5f1c8d34e7 Binary files /dev/null and b/utilities/poc_local_storage/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/utilities/poc_local_storage/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/utilities/poc_local_storage/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 00000000000..4d6372eebdb Binary files /dev/null and b/utilities/poc_local_storage/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/utilities/poc_local_storage/android/app/src/main/res/values-night/styles.xml b/utilities/poc_local_storage/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 00000000000..06952be745f --- /dev/null +++ b/utilities/poc_local_storage/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/utilities/poc_local_storage/android/app/src/main/res/values/styles.xml b/utilities/poc_local_storage/android/app/src/main/res/values/styles.xml new file mode 100644 index 00000000000..cb1ef88056e --- /dev/null +++ b/utilities/poc_local_storage/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/utilities/poc_local_storage/android/app/src/profile/AndroidManifest.xml b/utilities/poc_local_storage/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 00000000000..399f6981d5d --- /dev/null +++ b/utilities/poc_local_storage/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/utilities/poc_local_storage/android/build.gradle b/utilities/poc_local_storage/android/build.gradle new file mode 100644 index 00000000000..d2ffbffa4cd --- /dev/null +++ b/utilities/poc_local_storage/android/build.gradle @@ -0,0 +1,18 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = "../build" +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean", Delete) { + delete rootProject.buildDir +} diff --git a/utilities/poc_local_storage/android/gradle.properties b/utilities/poc_local_storage/android/gradle.properties new file mode 100644 index 00000000000..3b5b324f6e3 --- /dev/null +++ b/utilities/poc_local_storage/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError +android.useAndroidX=true +android.enableJetifier=true diff --git a/utilities/poc_local_storage/android/gradle/wrapper/gradle-wrapper.properties b/utilities/poc_local_storage/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000000..e1ca574ef01 --- /dev/null +++ b/utilities/poc_local_storage/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip diff --git a/utilities/poc_local_storage/android/settings.gradle b/utilities/poc_local_storage/android/settings.gradle new file mode 100644 index 00000000000..536165d35a4 --- /dev/null +++ b/utilities/poc_local_storage/android/settings.gradle @@ -0,0 +1,25 @@ +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "7.3.0" apply false + id "org.jetbrains.kotlin.android" version "1.7.10" apply false +} + +include ":app" diff --git a/utilities/poc_local_storage/firebase.json b/utilities/poc_local_storage/firebase.json new file mode 100644 index 00000000000..cda2fac4f6a --- /dev/null +++ b/utilities/poc_local_storage/firebase.json @@ -0,0 +1,52 @@ +{ + "hosting": { + "source": ".", + "ignore": ["firebase.json", "**/.*", "**/node_modules/**"], + "frameworksBackend": { + "region": "europe-west1" + }, + "rewrites": [ + { + "source": "**", + "destination": "/index.html" + } + ], + "cleanUrls": true, + "trailingSlash": false, + "headers": [ + { + "source": "**", + "headers": [ + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "Cache-Control", + "value": "no-store, no-cache, must-revalidate, max-age=3600, private" + }, + { + "key": "X-Content-Type-Options", + "value": "nosniff" + }, + { + "key": "X-Frame-Options", + "value": "DENY" + }, + { + "key": "X-XSS-Protection", + "value": "1; mode=block" + }, + { + "key": "Referrer-Policy", + "value": "no-referrer" + }, + { + "key": "Strict-Transport-Security", + "value": "max-age=1;includeSubDomains;preload" + } + ] + } + ] + } +} diff --git a/utilities/poc_local_storage/ios/.gitignore b/utilities/poc_local_storage/ios/.gitignore new file mode 100644 index 00000000000..7a7f9873ad7 --- /dev/null +++ b/utilities/poc_local_storage/ios/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/utilities/poc_local_storage/ios/Flutter/AppFrameworkInfo.plist b/utilities/poc_local_storage/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 00000000000..7c569640062 --- /dev/null +++ b/utilities/poc_local_storage/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 12.0 + + diff --git a/utilities/poc_local_storage/ios/Flutter/Debug.xcconfig b/utilities/poc_local_storage/ios/Flutter/Debug.xcconfig new file mode 100644 index 00000000000..ec97fc6f302 --- /dev/null +++ b/utilities/poc_local_storage/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/utilities/poc_local_storage/ios/Flutter/Release.xcconfig b/utilities/poc_local_storage/ios/Flutter/Release.xcconfig new file mode 100644 index 00000000000..c4855bfe200 --- /dev/null +++ b/utilities/poc_local_storage/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/utilities/poc_local_storage/ios/Podfile b/utilities/poc_local_storage/ios/Podfile new file mode 100644 index 00000000000..d97f17e223f --- /dev/null +++ b/utilities/poc_local_storage/ios/Podfile @@ -0,0 +1,44 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '12.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/utilities/poc_local_storage/ios/Runner.xcodeproj/project.pbxproj b/utilities/poc_local_storage/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 00000000000..ad93f67106c --- /dev/null +++ b/utilities/poc_local_storage/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,616 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C8082294A63A400263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C807B294A618700263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 331C8082294A63A400263BE5 /* RunnerTests */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 331C8081294A63A400263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C8080294A63A400263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 331C807D294A63A400263BE5 /* Sources */, + 331C807F294A63A400263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C8086294A63A400263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C8080294A63A400263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + 331C8080294A63A400263BE5 /* RunnerTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C807F294A63A400263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C807D294A63A400263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.pocLocalStorage; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 331C8088294A63A400263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.pocLocalStorage.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Debug; + }; + 331C8089294A63A400263BE5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.pocLocalStorage.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Release; + }; + 331C808A294A63A400263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.pocLocalStorage.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.pocLocalStorage; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.pocLocalStorage; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C8088294A63A400263BE5 /* Debug */, + 331C8089294A63A400263BE5 /* Release */, + 331C808A294A63A400263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/utilities/poc_local_storage/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/utilities/poc_local_storage/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000000..919434a6254 --- /dev/null +++ b/utilities/poc_local_storage/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/utilities/poc_local_storage/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/utilities/poc_local_storage/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000000..18d981003d6 --- /dev/null +++ b/utilities/poc_local_storage/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/utilities/poc_local_storage/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/utilities/poc_local_storage/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000000..f9b0d7c5ea1 --- /dev/null +++ b/utilities/poc_local_storage/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/utilities/poc_local_storage/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/utilities/poc_local_storage/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 00000000000..8e3ca5dfe19 --- /dev/null +++ b/utilities/poc_local_storage/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/utilities/poc_local_storage/ios/Runner.xcworkspace/contents.xcworkspacedata b/utilities/poc_local_storage/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000000..1d526a16ed0 --- /dev/null +++ b/utilities/poc_local_storage/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/utilities/poc_local_storage/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/utilities/poc_local_storage/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000000..18d981003d6 --- /dev/null +++ b/utilities/poc_local_storage/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/utilities/poc_local_storage/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/utilities/poc_local_storage/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000000..f9b0d7c5ea1 --- /dev/null +++ b/utilities/poc_local_storage/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/utilities/poc_local_storage/ios/Runner/AppDelegate.swift b/utilities/poc_local_storage/ios/Runner/AppDelegate.swift new file mode 100644 index 00000000000..9074fee9290 --- /dev/null +++ b/utilities/poc_local_storage/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import Flutter +import UIKit + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000000..d36b1fab2d9 --- /dev/null +++ b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 00000000000..dc9ada4725e Binary files /dev/null and b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 00000000000..7353c41ecf9 Binary files /dev/null and b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 00000000000..797d452e458 Binary files /dev/null and b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 00000000000..6ed2d933e11 Binary files /dev/null and b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 00000000000..4cd7b0099ca Binary files /dev/null and b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 00000000000..fe730945a01 Binary files /dev/null and b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 00000000000..321773cd857 Binary files /dev/null and b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 00000000000..797d452e458 Binary files /dev/null and b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 00000000000..502f463a9bc Binary files /dev/null and b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 00000000000..0ec30343922 Binary files /dev/null and b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 00000000000..0ec30343922 Binary files /dev/null and b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 00000000000..e9f5fea27c7 Binary files /dev/null and b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 00000000000..84ac32ae7d9 Binary files /dev/null and b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 00000000000..8953cba0906 Binary files /dev/null and b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 00000000000..0467bf12aa4 Binary files /dev/null and b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/utilities/poc_local_storage/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 00000000000..0bedcf2fd46 --- /dev/null +++ b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/utilities/poc_local_storage/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 00000000000..9da19eacad3 Binary files /dev/null and b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/utilities/poc_local_storage/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 00000000000..9da19eacad3 Binary files /dev/null and b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/utilities/poc_local_storage/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 00000000000..9da19eacad3 Binary files /dev/null and b/utilities/poc_local_storage/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/utilities/poc_local_storage/ios/Runner/Base.lproj/LaunchScreen.storyboard b/utilities/poc_local_storage/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000000..f2e259c7c93 --- /dev/null +++ b/utilities/poc_local_storage/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/utilities/poc_local_storage/ios/Runner/Base.lproj/Main.storyboard b/utilities/poc_local_storage/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 00000000000..f3c28516fb3 --- /dev/null +++ b/utilities/poc_local_storage/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/utilities/poc_local_storage/ios/Runner/Info.plist b/utilities/poc_local_storage/ios/Runner/Info.plist new file mode 100644 index 00000000000..6b8537a3061 --- /dev/null +++ b/utilities/poc_local_storage/ios/Runner/Info.plist @@ -0,0 +1,49 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Poc Local Storage + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + poc_local_storage + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + + + diff --git a/utilities/poc_local_storage/ios/Runner/Runner-Bridging-Header.h b/utilities/poc_local_storage/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 00000000000..308a2a560b4 --- /dev/null +++ b/utilities/poc_local_storage/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/utilities/poc_local_storage/ios/RunnerTests/RunnerTests.swift b/utilities/poc_local_storage/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 00000000000..86a7c3b1b61 --- /dev/null +++ b/utilities/poc_local_storage/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Flutter +import UIKit +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/utilities/poc_local_storage/lib/app.dart b/utilities/poc_local_storage/lib/app.dart new file mode 100644 index 00000000000..d25a541d5f0 --- /dev/null +++ b/utilities/poc_local_storage/lib/app.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:poc_local_storage/home_screen.dart'; +import 'package:poc_local_storage/main.dart'; +import 'package:poc_local_storage/password_entry_screen.dart'; +import 'package:poc_local_storage/protected_screen.dart'; + +final GoRouter _router = GoRouter( + routes: [ + GoRoute( + path: '/', + builder: (context, state) => const HomeScreen(), + ), + GoRoute( + path: '/password', + builder: (context, state) => PasswordEntryScreen( + then: state.extra as String? ?? '/protected', + ), + ), + GoRoute( + path: '/protected', + builder: (context, state) => const ProtectedScreen(), + redirect: (context, state) async { + if (!certificateRepo.isAuthenticated) { + final hasPassword = await certificateRepo.hasPassword; + if (!hasPassword) { + return '/'; + } + return '/password'; + } + return null; + }, + ), + ], +); + +class App extends StatelessWidget { + const App({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp.router( + routerConfig: _router, + title: 'Password Protected App', + theme: ThemeData( + colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue), + useMaterial3: true, + ), + ); + } +} diff --git a/utilities/poc_local_storage/lib/crypto_service.dart b/utilities/poc_local_storage/lib/crypto_service.dart new file mode 100644 index 00000000000..320aa685cbe --- /dev/null +++ b/utilities/poc_local_storage/lib/crypto_service.dart @@ -0,0 +1,235 @@ +// ignore_for_file: inference_failure_on_instance_creation + +import 'dart:convert'; +import 'dart:math'; +import 'dart:typed_data'; + +import 'package:collection/collection.dart'; +import 'package:pointycastle/export.dart'; + +/// A service for encrypting and decrypting data using AES-GCM. +final class CryptoService { + /// GCM standard IV length is 12 bytes + static const int _ivLength = 12; + + /// Salt length for Argon2 key derivation + static const int _saltLength = 16; + + /// AES-256 key length + static const int _keyLength = 32; + + /// Versioning for future improvements + static const int _version = 1; + + final SecureRandom _secureRandom; + + factory CryptoService({SecureRandom? secureRandom}) { + return CryptoService._(secureRandom ?? _initSecureRandom()); + } + + CryptoService._(this._secureRandom); + + /// Decrypts the [encryptedData] using the provided [password]. + Uint8List decrypt( + Uint8List encryptedData, + String password, { + Uint8List? aad, + }) { + // Extract the version, algorithm ID, salt, and IV + final version = encryptedData[0]; + final algorithmId = encryptedData[1]; + if (version != _version || algorithmId != 0x01) { + throw Exception('Unsupported version or algorithm'); + } + + final salt = encryptedData.sublist(2, 2 + _saltLength); + final iv = encryptedData.sublist( + 2 + _saltLength, + 2 + _saltLength + _ivLength, + ); + final data = encryptedData.sublist( + 2 + _saltLength + _ivLength, + ); + + final key = _deriveKey(password, salt); + + final cipher = GCMBlockCipher(AESEngine()); + final aeadParams = AEADParameters( + KeyParameter(key), + 128, + iv, + aad ?? Uint8List(0), + ); + + cipher.init(false, aeadParams); + + try { + final decryptedData = cipher.process(data); + + // Verify checksum/marker + final checksum = utf8.encode('CHK'); // 3-byte marker + if (decryptedData.length < checksum.length) { + throw Exception('Decrypted data is too short'); + } + final originalData = decryptedData.sublist( + 0, + decryptedData.length - checksum.length, + ); + final extractedChecksum = decryptedData.sublist( + decryptedData.length - checksum.length, + ); + + if (!const ListEquality().equals(checksum, extractedChecksum)) { + throw Exception('Decryption failed: Checksum mismatch'); + } + + return originalData; + } catch (e) { + throw Exception('Decryption failed: $e'); + } finally { + // Erase the key from memory + _securelyErase(key); + } + } + + /// Encrypts the [data] using the provided [password]. + Uint8List encrypt( + Uint8List data, + String password, { + Uint8List? aad, + }) { + final salt = _generateSalt(); + final key = _deriveKey(password, salt); + final iv = _generateIV(); + + final cipher = GCMBlockCipher(AESEngine()); + final aeadParams = AEADParameters( + KeyParameter(key), + 128, + iv, + aad ?? Uint8List(0), + ); + + cipher.init(true, aeadParams); + + // Add a known marker or checksum at the end of the plaintext + // before encryption + final checksum = utf8.encode('CHK'); // 3-byte marker + final combinedData = Uint8List.fromList([...data, ...checksum]); + + final encryptedData = cipher.process(combinedData); + + // Combine version, salt, IV, and encrypted data + // Version 1, Algorithm ID 1 (AES-GCM) + final metadata = Uint8List.fromList([_version, 0x01]); + final result = Uint8List.fromList([ + ...metadata, + ...salt, + ...iv, + ...encryptedData, + ]); + + // Erase the key from memory + _securelyErase(key); + + return result; + } + + /// Hashes a password using Argon2id + Uint8List hashPassword(String password, {Uint8List? salt}) { + salt ??= _generateSalt(); + final argon2 = Argon2BytesGenerator(); + final params = Argon2Parameters( + Argon2Parameters.ARGON2_id, + salt, + memoryPowerOf2: 12, // 12 MiB + desiredKeyLength: _keyLength, + ); + + argon2.init(params); + + final passwordBytes = Uint8List.fromList(utf8.encode(password)); + final hashedPassword = argon2.process(passwordBytes); + + // Combine salt and hashed password for storage + return Uint8List.fromList([...salt, ...hashedPassword]); + } + + /// Re-encrypts the [encryptedData] using the provided [oldPassword] + /// and [newPassword]. + Uint8List reEncrypt( + Uint8List encryptedData, + String oldPassword, + String newPassword, { + Uint8List? aad, + }) { + // Decrypt using the old password + final decryptedData = decrypt(encryptedData, oldPassword, aad: aad); + + // Encrypt using the new password + return encrypt(decryptedData, newPassword, aad: aad); + } + + /// Verifies a password against a stored hash + bool verifyPassword(String password, Uint8List storedHash) { + final salt = storedHash.sublist(0, _saltLength); + final hashedPassword = hashPassword(password, salt: salt); + return const ListEquality().equals(hashedPassword, storedHash); + } + + /// Derives a key from the [password] and [salt] using Argon2. + /// Argon2 is a modern, secure key derivation function designed to resist + /// brute-force attacks and side-channel attacks. + Uint8List _deriveKey(String password, Uint8List salt) { + final argon2 = Argon2BytesGenerator(); + final params = Argon2Parameters( + Argon2Parameters.ARGON2_id, + salt, + memoryPowerOf2: 12, // 12 MiB + desiredKeyLength: _keyLength, + ); + + argon2.init(params); + + // Convert the password to Uint8List + final passwordBytes = Uint8List.fromList(utf8.encode(password)); + + // Derive the key using the password and salt + final key = argon2.process(passwordBytes); + + // Securely erase password from memory + _securelyErase(Uint8List.fromList(utf8.encode(password))); + + return key; + } + + /// Generates a random IV for AES-GCM. + Uint8List _generateIV() { + final iv = Uint8List(_ivLength); + _secureRandom.nextBytes(iv.length); + return iv; + } + + /// Generates a random salt for Argon2. + Uint8List _generateSalt() { + final salt = Uint8List(_saltLength); + _secureRandom.nextBytes(salt.length); + return salt; + } + + /// Attempts to securely erase sensitive data from memory. + void _securelyErase(Uint8List data) => data.fillRange(0, data.length, 0); + + /// Initializes a secure random number generator. + static SecureRandom _initSecureRandom() { + final secureRandom = SecureRandom('Fortuna'); + final seed = Uint8List(32); + + for (var i = 0; i < seed.length; i++) { + seed[i] = Random.secure().nextInt(256); + } + + secureRandom.seed(KeyParameter(seed)); + return secureRandom; + } +} diff --git a/utilities/poc_local_storage/lib/file_picker_service.dart b/utilities/poc_local_storage/lib/file_picker_service.dart new file mode 100644 index 00000000000..7d8f686f4e2 --- /dev/null +++ b/utilities/poc_local_storage/lib/file_picker_service.dart @@ -0,0 +1,81 @@ +// ignore_for_file: inference_failure_on_untyped_parameter + +import 'dart:async'; + +import 'package:file_picker/file_picker.dart'; + +/// A service for picking files from the device. +final class FilePickerService { + static final FilePickerService _instance = FilePickerService._(); + + factory FilePickerService() => _instance; + + FilePickerService._(); + + Future> pickMultipleFiles() async { + final result = await FilePicker.platform.pickFiles( + allowMultiple: true, + withData: true, + ); + + return result?.files ?? []; + } + + Future>> pickMultipleFilesAsBytes() async { + final result = await FilePicker.platform.pickFiles( + allowMultiple: true, + withData: true, + ); + + if (result != null) { + return result.files + .map((file) => file.bytes ?? List.empty()) + .toList(); + } + return []; + } + + Future?> pickSingleFileAsBytes() async { + final result = await FilePicker.platform.pickFiles( + withData: true, + ); + + if (result != null && result.files.isNotEmpty) { + return result.files.first.bytes; + } + return null; + } +} + +extension PlatformFileExtension on PlatformFile { + bool get isCertificate => ['pem'].contains(extension?.toLowerCase()); + + Future> readAsBytes() async { + if (bytes != null) { + return bytes!; + } else if (readStream != null) { + final completer = Completer>(); + final chunks = []; + + readStream!.listen( + chunks.addAll, + onError: (error) { + completer.completeError(error as Object); + }, + onDone: () { + completer.complete(chunks); + }, + cancelOnError: true, + ); + + return completer.future; + } else { + throw Exception('No data available for file: $name'); + } + } + + Future readAsString() async { + final fileBytes = await readAsBytes(); + return String.fromCharCodes(fileBytes); + } +} diff --git a/utilities/poc_local_storage/lib/home_screen.dart b/utilities/poc_local_storage/lib/home_screen.dart new file mode 100644 index 00000000000..04408d3c97c --- /dev/null +++ b/utilities/poc_local_storage/lib/home_screen.dart @@ -0,0 +1,90 @@ +// ignore_for_file: discarded_futures + +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:poc_local_storage/main.dart'; + +class HomeScreen extends StatefulWidget { + const HomeScreen({super.key}); + + @override + State createState() => _HomeScreenState(); +} + +class _HomeScreenState extends State { + bool _hasPassword = false; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Home'), + backgroundColor: Theme.of(context).colorScheme.inversePrimary, + ), + body: Center( + child: _hasPassword + ? ElevatedButton( + onPressed: () => _navigateToPasswordScreen(context), + child: const Text('Go to Protected Screen'), + ) + : ElevatedButton( + onPressed: () async => _showCreatePasswordDialog(context), + child: const Text('Create Your Password'), + ), + ), + ); + } + + @override + void initState() { + super.initState(); + _checkPassword(); + } + + Future _checkPassword() async { + final hasPassword = await certificateRepo.hasPassword; + setState(() { + _hasPassword = hasPassword; + }); + } + + void _navigateToPasswordScreen(BuildContext context) { + context.go('/password'); + } + + Future _showCreatePasswordDialog(BuildContext context) { + var tempPassword = ''; + return showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Create Password'), + content: TextField( + obscureText: true, + onChanged: (value) => tempPassword = value, + decoration: const InputDecoration( + hintText: 'Enter your new password', + ), + ), + actions: [ + TextButton( + child: const Text('Cancel'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + TextButton( + child: const Text('Save'), + onPressed: () async { + Navigator.of(context).pop(); + await certificateRepo.setPassword(tempPassword); + tempPassword = ''; + await _checkPassword(); + }, + ), + ], + ); + }, + ); + } +} diff --git a/utilities/poc_local_storage/lib/main.dart b/utilities/poc_local_storage/lib/main.dart new file mode 100644 index 00000000000..4f9cce3a1c5 --- /dev/null +++ b/utilities/poc_local_storage/lib/main.dart @@ -0,0 +1,13 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_web_plugins/flutter_web_plugins.dart'; +import 'package:poc_local_storage/app.dart'; +import 'package:poc_local_storage/secure_certificate_repository.dart'; + +void main() { + WidgetsFlutterBinding.ensureInitialized(); + usePathUrlStrategy(); + runApp(const App()); +} + +final SecureCertificateRepository certificateRepo = + SecureCertificateRepository(); diff --git a/utilities/poc_local_storage/lib/password_entry_screen.dart b/utilities/poc_local_storage/lib/password_entry_screen.dart new file mode 100644 index 00000000000..186d7acdaa4 --- /dev/null +++ b/utilities/poc_local_storage/lib/password_entry_screen.dart @@ -0,0 +1,123 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:poc_local_storage/secure_storage_service.dart'; + +class PasswordEntryScreen extends StatefulWidget { + final String then; + + const PasswordEntryScreen({super.key, required this.then}); + + @override + State createState() => _PasswordEntryScreenState(); +} + +class _PasswordEntryScreenState extends State { + final _authService = SecureStorageService(); + final _passwordController = TextEditingController(); + bool _isPasswordVisible = false; + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(24), + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 500), + child: Card( + elevation: 8, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + child: Padding( + padding: const EdgeInsets.all(32), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon( + Icons.lock_outline, + size: 64, + color: Colors.blue, + ), + const SizedBox(height: 32), + const Text( + 'Enter Password', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 24), + TextField( + controller: _passwordController, + obscureText: !_isPasswordVisible, + decoration: InputDecoration( + labelText: 'Password', + hintText: 'Enter your password', + prefixIcon: const Icon(Icons.vpn_key), + suffixIcon: IconButton( + icon: Icon( + _isPasswordVisible + ? Icons.visibility_off + : Icons.visibility, + ), + onPressed: () { + setState(() { + _isPasswordVisible = !_isPasswordVisible; + }); + }, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + ), + const SizedBox(height: 32), + ElevatedButton( + onPressed: _verifyPassword, + style: ElevatedButton.styleFrom( + minimumSize: const Size.fromHeight(50), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + child: const Text( + 'Enter', + style: TextStyle(fontSize: 18), + ), + ), + ], + ), + ), + ), + ), + ), + ), + ), + ); + } + + @override + void dispose() { + _passwordController.dispose(); + super.dispose(); + } + + Future _verifyPassword() async { + final isAuthenticated = await _authService.login(_passwordController.text); + if (isAuthenticated) { + if (mounted) { + context.go(widget.then); + } + } else { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Incorrect password. Please try again.'), + ), + ); + } + } + } +} diff --git a/utilities/poc_local_storage/lib/protected_screen.dart b/utilities/poc_local_storage/lib/protected_screen.dart new file mode 100644 index 00000000000..21b8facd1a7 --- /dev/null +++ b/utilities/poc_local_storage/lib/protected_screen.dart @@ -0,0 +1,315 @@ +// ignore_for_file: use_build_context_synchronously, discarded_futures + +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:poc_local_storage/main.dart'; + +class ProtectedScreen extends StatefulWidget { + const ProtectedScreen({super.key}); + + @override + State createState() => _ProtectedScreenState(); +} + +class _ProtectedScreenState extends State { + List _certificates = []; + bool _isLoading = true; + final _passwordController = TextEditingController(); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Protected Screen'), + backgroundColor: Theme.of(context).colorScheme.inversePrimary, + automaticallyImplyLeading: false, + actions: [ + IconButton( + icon: const Icon(Icons.lock_reset), + onPressed: _showResetPasswordConfirmation, + tooltip: 'Reset Password', + ), + ], + ), + body: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Welcome to the protected screen!', + style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 20), + ElevatedButton.icon( + onPressed: _pickAndStoreCertificates, + icon: const Icon(Icons.add), + label: const Text('Pick and Store Certificates'), + ), + const SizedBox(height: 20), + const Text( + 'Stored Certificates:', + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 10), + Expanded( + child: _isLoading + ? const Center(child: CircularProgressIndicator()) + : _certificates.isEmpty + ? const Center(child: Text('No certificates stored yet.')) + : ListView.builder( + itemCount: _certificates.length, + itemBuilder: (context, index) { + return ListTile( + title: Text(_certificates[index]), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: const Icon(Icons.info), + onPressed: () async => + _showCertificateDetails( + _certificates[index], + ), + ), + IconButton( + icon: const Icon(Icons.delete), + onPressed: () async => _deleteCertificate( + _certificates[index], + ), + ), + ], + ), + ); + }, + ), + ), + ], + ), + ), + ); + } + + @override + void dispose() { + _passwordController.dispose(); + super.dispose(); + } + + @override + void initState() { + super.initState(); + _loadCertificates(); + } + + Future _deleteCertificate(String certificateName) async { + try { + await certificateRepo.deleteCertificate(certificateName); + await _loadCertificates(); + } catch (e) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Failed to delete certificate')), + ); + } + } + + Future _loadCertificates() async { + setState(() { + _isLoading = true; + }); + try { + final certificates = await certificateRepo.getStoredCertificateNames(); + setState(() { + _certificates = certificates; + _isLoading = false; + }); + } catch (e) { + setState(() { + _isLoading = false; + }); + } + } + + Future _pickAndStoreCertificates() async { + final password = await _promptForPassword( + 'Enter a password to encrypt the certificates', + ); + if (password != null && password.isNotEmpty) { + try { + if (await certificateRepo.verifyPassword(password)) { + final storedCertificates = + await certificateRepo.pickAndStoreCertificates(password); + if (storedCertificates.isNotEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + '${storedCertificates.length} certificate(s) stored', + ), + ), + ); + await _loadCertificates(); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text( + 'No certificates were selected or stored', + ), + ), + ); + } + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Authentication failed. Incorrect password.'), + ), + ); + } + } catch (e) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Error: $e')), + ); + } + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Password is required to store certificates'), + ), + ); + } + } + + Future _promptForPassword(String message) async { + _passwordController.clear(); + return showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: Text(message), + content: TextField( + controller: _passwordController, + obscureText: true, + decoration: const InputDecoration(hintText: 'Enter password'), + ), + actions: [ + TextButton( + child: const Text('Cancel'), + onPressed: () => Navigator.of(context).pop(), + ), + TextButton( + child: const Text('OK'), + onPressed: () => + Navigator.of(context).pop(_passwordController.text), + ), + ], + ); + }, + ); + } + + Future _resetPassword() async { + try { + await certificateRepo.deleteAllCertificates(); + await certificateRepo.deletePassword(); + if (mounted) { + context.go('/'); + } + } catch (e) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Failed to reset password and delete certificates'), + ), + ); + } + } + + Future _showCertificateDetails(String certificateName) async { + final password = await _promptForPassword( + 'Enter the password to view certificate details', + ); + if (password != null) { + try { + if (await certificateRepo.verifyPassword(password)) { + final certificateContent = await certificateRepo.getCertificate( + certificateName, + password, + ); + if (certificateContent != null && certificateContent.isNotEmpty) { + await showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text('Certificate Details: $certificateName'), + content: + SingleChildScrollView(child: Text(certificateContent)), + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () => Navigator.of(context).pop(), + ), + ], + ); + }, + ); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text( + '''Failed to load certificate. Certificate might be empty or not found.''', + ), + ), + ); + } + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Incorrect password')), + ); + } + } catch (e) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Failed to load certificate. Error: $e'), + ), + ); + } + } + } + + Future _showResetPasswordConfirmation() async { + return showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Reset Password'), + content: const SingleChildScrollView( + child: ListBody( + children: [ + Text('Are you sure you want to reset your password?'), + SizedBox(height: 10), + Text('This action will:'), + Text('• Delete your current password'), + Text('• Delete all stored certificates'), + Text('• Log you out of the application'), + ], + ), + ), + actions: [ + TextButton( + child: const Text('Cancel'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + TextButton( + child: const Text('Reset', style: TextStyle(color: Colors.red)), + onPressed: () { + Navigator.of(context).pop(); + _resetPassword(); + }, + ), + ], + ); + }, + ); + } +} diff --git a/utilities/poc_local_storage/lib/secure_certificate_repository.dart b/utilities/poc_local_storage/lib/secure_certificate_repository.dart new file mode 100644 index 00000000000..d86db178714 --- /dev/null +++ b/utilities/poc_local_storage/lib/secure_certificate_repository.dart @@ -0,0 +1,144 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:file_picker/file_picker.dart'; +import 'package:poc_local_storage/crypto_service.dart'; +import 'package:poc_local_storage/file_picker_service.dart'; +import 'package:poc_local_storage/secure_storage_service.dart'; + +class SecureCertificateRepository { + static const String _certificateKeyPrefix = 'certificate_'; + static const String _certificateListKey = 'certificate_list'; + + final SecureStorageService _storageService; + final FilePickerService _filePickerService; + final CryptoService _cryptoService; + + SecureCertificateRepository({ + SecureStorageService? storageService, + FilePickerService? filePickerService, + CryptoService? cryptoService, + }) : _storageService = storageService ?? SecureStorageService(), + _filePickerService = filePickerService ?? FilePickerService(), + _cryptoService = cryptoService ?? CryptoService(); + + Future get hasPassword async => _storageService.hasPassword; + + bool get isAuthenticated => _storageService.isAuthenticated; + + Future deleteAllCertificates() async { + final certificateNames = await getStoredCertificateNames(); + for (final name in certificateNames) { + await deleteCertificate(name); + } + await _storageService.delete(_certificateListKey); + } + + Future deleteCertificate(String certificateName) async { + final certificateKey = _generateCertificateKey(certificateName); + await _storageService.delete(certificateKey); + final certificateList = await getStoredCertificateNames(); + certificateList.remove(certificateName); + await _storeCertificateList(certificateList); + } + + Future deletePassword() async { + await _storageService.deletePassword(); + } + + Future getCertificate( + String certificateName, + String password, + ) async { + if (!_storageService.isAuthenticated) { + throw Exception('Not authenticated'); + } + + final certificateKey = _generateCertificateKey(certificateName); + final encryptedCertificate = await _storageService.getBytes(certificateKey); + if (encryptedCertificate != null) { + try { + final decryptedBytes = + _cryptoService.decrypt(encryptedCertificate, password); + final decodedCertificate = utf8.decode(decryptedBytes); + return decodedCertificate; + } catch (e) { + rethrow; + } + } else { + return null; + } + } + + Future> getStoredCertificateNames() async { + final storedList = await _storageService.getString(_certificateListKey); + if (storedList != null && storedList.isNotEmpty) { + return storedList.split(','); + } + return []; + } + + Future> pickAndStoreCertificates(String password) async { + if (!_storageService.isAuthenticated) { + throw Exception('Not authenticated'); + } + + final files = await _filePickerService.pickMultipleFiles(); + final storedCertificates = []; + + for (final file in files) { + if (_isCertificate(file)) { + final certificateBytes = await _readFileAsBytes(file); + + final encryptedBytes = + _cryptoService.encrypt(certificateBytes, password); + final certificateKey = _generateCertificateKey(file.name); + + await _storageService.saveBytes(certificateKey, encryptedBytes); + storedCertificates.add(file.name); + + await _storageService.getBytes(certificateKey); + } + } + + await _addToCertificateList(storedCertificates); + return storedCertificates; + } + + Future setPassword(String password) async { + await _storageService.setPassword(password); + } + + Future verifyPassword(String password) async { + return _storageService.login(password); + } + + Future _addToCertificateList(List newCertificates) async { + final existingList = await getStoredCertificateNames(); + existingList.addAll(newCertificates); + await _storeCertificateList(existingList); + } + + String _generateCertificateKey(String certificateName) { + return '$_certificateKeyPrefix$certificateName'; + } + + bool _isCertificate(PlatformFile file) { + return ['pem', 'crt', 'cer', 'der'].contains(file.extension?.toLowerCase()); + } + + Future _readFileAsBytes(PlatformFile file) async { + if (file.bytes != null) { + return file.bytes!; + } else { + throw Exception('No data available for file: ${file.name}'); + } + } + + Future _storeCertificateList(List certificateList) async { + await _storageService.saveString( + _certificateListKey, + certificateList.join(','), + ); + } +} diff --git a/utilities/poc_local_storage/lib/secure_storage_service.dart b/utilities/poc_local_storage/lib/secure_storage_service.dart new file mode 100644 index 00000000000..ff37898c203 --- /dev/null +++ b/utilities/poc_local_storage/lib/secure_storage_service.dart @@ -0,0 +1,104 @@ +// ignore_for_file: avoid_positional_boolean_parameters + +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:poc_local_storage/crypto_service.dart'; + +/// A service for securely storing and retrieving data. +final class SecureStorageService { + static final _instance = SecureStorageService._(); + + static const String _passwordKey = 'user_password'; + final FlutterSecureStorage _secureStorage; + + final CryptoService _cryptoService; + bool _isAuthenticated = false; + + factory SecureStorageService() => _instance; + + SecureStorageService._() + : _secureStorage = const FlutterSecureStorage(), + _cryptoService = CryptoService(); + + Future get deleteAll async { + await _secureStorage.deleteAll(); + } + + Future get hasPassword async { + final storedPassword = await _secureStorage.read(key: _passwordKey); + return storedPassword != null; + } + + bool get isAuthenticated => _isAuthenticated; + + void get logout => _isAuthenticated = false; + + Future delete(String key) async { + await _secureStorage.delete(key: key); + } + + Future deletePassword() async { + await _secureStorage.delete(key: _passwordKey); + _isAuthenticated = false; + } + + Future getBool(String key) async { + final value = await _secureStorage.read(key: key); + return value != null ? value.toLowerCase() == 'true' : null; + } + + Future getBytes(String key) async { + final base64String = await _secureStorage.read(key: key); + if (base64String == null) return null; + return Uint8List.fromList(base64Decode(base64String)); + } + + Future getInt(String key) async { + final value = await _secureStorage.read(key: key); + return value != null ? int.tryParse(value) : null; + } + + Future getString(String key) async { + return _secureStorage.read(key: key); + } + + Future login(String password) async { + final storedHashedPassword = await _secureStorage.read(key: _passwordKey); + if (storedHashedPassword != null) { + final storedHash = base64Decode(storedHashedPassword); + final isValid = _cryptoService.verifyPassword(password, storedHash); + if (isValid) { + _isAuthenticated = true; + return true; + } + } + return false; + } + + Future saveBool(String key, bool value) async { + await _secureStorage.write(key: key, value: value.toString()); + } + + Future saveBytes(String key, Uint8List bytes) async { + final base64String = base64Encode(bytes); + await _secureStorage.write(key: key, value: base64String); + } + + Future saveInt(String key, int value) async { + await _secureStorage.write(key: key, value: value.toString()); + } + + Future saveString(String key, String value) async { + await _secureStorage.write(key: key, value: value); + } + + Future setPassword(String password) async { + final hashedPassword = _cryptoService.hashPassword(password); + await _secureStorage.write( + key: _passwordKey, + value: base64Encode(hashedPassword), + ); + } +} diff --git a/utilities/poc_local_storage/linux/.gitignore b/utilities/poc_local_storage/linux/.gitignore new file mode 100644 index 00000000000..d3896c98444 --- /dev/null +++ b/utilities/poc_local_storage/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/utilities/poc_local_storage/linux/CMakeLists.txt b/utilities/poc_local_storage/linux/CMakeLists.txt new file mode 100644 index 00000000000..0402ba6e4ae --- /dev/null +++ b/utilities/poc_local_storage/linux/CMakeLists.txt @@ -0,0 +1,145 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "poc_local_storage") +# The unique GTK application identifier for this application. See: +# https://wiki.gnome.org/HowDoI/ChooseApplicationID +set(APPLICATION_ID "com.example.poc_local_storage") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Load bundled libraries from the lib/ directory relative to the binary. +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Root filesystem for cross-building. +if(FLUTTER_TARGET_PLATFORM_SYSROOT) + set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +endif() + +# Define build configuration options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Define the application target. To change its name, change BINARY_NAME above, +# not the value here, or `flutter run` will no longer work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add dependency libraries. Add any application-specific dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) + +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) + install(FILES "${bundled_library}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endforeach(bundled_library) + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/utilities/poc_local_storage/linux/flutter/CMakeLists.txt b/utilities/poc_local_storage/linux/flutter/CMakeLists.txt new file mode 100644 index 00000000000..d5bd01648a9 --- /dev/null +++ b/utilities/poc_local_storage/linux/flutter/CMakeLists.txt @@ -0,0 +1,88 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/utilities/poc_local_storage/linux/flutter/generated_plugin_registrant.cc b/utilities/poc_local_storage/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 00000000000..d0e7f797894 --- /dev/null +++ b/utilities/poc_local_storage/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include + +void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin"); + flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar); +} diff --git a/utilities/poc_local_storage/linux/flutter/generated_plugin_registrant.h b/utilities/poc_local_storage/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 00000000000..e0f0a47bc08 --- /dev/null +++ b/utilities/poc_local_storage/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/utilities/poc_local_storage/linux/flutter/generated_plugins.cmake b/utilities/poc_local_storage/linux/flutter/generated_plugins.cmake new file mode 100644 index 00000000000..b29e9ba06bc --- /dev/null +++ b/utilities/poc_local_storage/linux/flutter/generated_plugins.cmake @@ -0,0 +1,24 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + flutter_secure_storage_linux +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/utilities/poc_local_storage/linux/main.cc b/utilities/poc_local_storage/linux/main.cc new file mode 100644 index 00000000000..e7c5c543703 --- /dev/null +++ b/utilities/poc_local_storage/linux/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/utilities/poc_local_storage/linux/my_application.cc b/utilities/poc_local_storage/linux/my_application.cc new file mode 100644 index 00000000000..b23176ff794 --- /dev/null +++ b/utilities/poc_local_storage/linux/my_application.cc @@ -0,0 +1,124 @@ +#include "my_application.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen* screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "poc_local_storage"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } else { + gtk_window_set_title(window, "poc_local_storage"); + } + + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GApplication::startup. +static void my_application_startup(GApplication* application) { + //MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application startup. + + G_APPLICATION_CLASS(my_application_parent_class)->startup(application); +} + +// Implements GApplication::shutdown. +static void my_application_shutdown(GApplication* application) { + //MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application shutdown. + + G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application); +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject* object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; + G_APPLICATION_CLASS(klass)->startup = my_application_startup; + G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, + "flags", G_APPLICATION_NON_UNIQUE, + nullptr)); +} diff --git a/utilities/poc_local_storage/linux/my_application.h b/utilities/poc_local_storage/linux/my_application.h new file mode 100644 index 00000000000..72271d5e417 --- /dev/null +++ b/utilities/poc_local_storage/linux/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/utilities/poc_local_storage/macos/.gitignore b/utilities/poc_local_storage/macos/.gitignore new file mode 100644 index 00000000000..746adbb6b9e --- /dev/null +++ b/utilities/poc_local_storage/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/utilities/poc_local_storage/macos/Flutter/Flutter-Debug.xcconfig b/utilities/poc_local_storage/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 00000000000..4b81f9b2d20 --- /dev/null +++ b/utilities/poc_local_storage/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/utilities/poc_local_storage/macos/Flutter/Flutter-Release.xcconfig b/utilities/poc_local_storage/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 00000000000..5caa9d1579e --- /dev/null +++ b/utilities/poc_local_storage/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/utilities/poc_local_storage/macos/Flutter/GeneratedPluginRegistrant.swift b/utilities/poc_local_storage/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 00000000000..15a1671b02c --- /dev/null +++ b/utilities/poc_local_storage/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,14 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + +import flutter_secure_storage_macos +import path_provider_foundation + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) +} diff --git a/utilities/poc_local_storage/macos/Podfile b/utilities/poc_local_storage/macos/Podfile new file mode 100644 index 00000000000..c795730db8e --- /dev/null +++ b/utilities/poc_local_storage/macos/Podfile @@ -0,0 +1,43 @@ +platform :osx, '10.14' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/utilities/poc_local_storage/macos/Runner.xcodeproj/project.pbxproj b/utilities/poc_local_storage/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 00000000000..e1db443d14d --- /dev/null +++ b/utilities/poc_local_storage/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,705 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC10EC2044A3C60003C045; + remoteInfo = Runner; + }; + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* poc_local_storage.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "poc_local_storage.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 331C80D2294CF70F00263BE5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C80D6294CF71000263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C80D7294CF71000263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 331C80D6294CF71000263BE5 /* RunnerTests */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* poc_local_storage.app */, + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C80D4294CF70F00263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 331C80D1294CF70F00263BE5 /* Sources */, + 331C80D2294CF70F00263BE5 /* Frameworks */, + 331C80D3294CF70F00263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C80DA294CF71000263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* poc_local_storage.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C80D4294CF70F00263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 33CC10EC2044A3C60003C045; + }; + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 331C80D4294CF70F00263BE5 /* RunnerTests */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C80D3294CF70F00263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C80D1294CF70F00263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC10EC2044A3C60003C045 /* Runner */; + targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */; + }; + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 331C80DB294CF71000263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.pocLocalStorage.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/poc_local_storage.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/poc_local_storage"; + }; + name = Debug; + }; + 331C80DC294CF71000263BE5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.pocLocalStorage.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/poc_local_storage.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/poc_local_storage"; + }; + name = Release; + }; + 331C80DD294CF71000263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.pocLocalStorage.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/poc_local_storage.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/poc_local_storage"; + }; + name = Profile; + }; + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C80DB294CF71000263BE5 /* Debug */, + 331C80DC294CF71000263BE5 /* Release */, + 331C80DD294CF71000263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/utilities/poc_local_storage/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/utilities/poc_local_storage/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000000..18d981003d6 --- /dev/null +++ b/utilities/poc_local_storage/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/utilities/poc_local_storage/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/utilities/poc_local_storage/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 00000000000..df8c75070e6 --- /dev/null +++ b/utilities/poc_local_storage/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/utilities/poc_local_storage/macos/Runner.xcworkspace/contents.xcworkspacedata b/utilities/poc_local_storage/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000000..1d526a16ed0 --- /dev/null +++ b/utilities/poc_local_storage/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/utilities/poc_local_storage/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/utilities/poc_local_storage/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000000..18d981003d6 --- /dev/null +++ b/utilities/poc_local_storage/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/utilities/poc_local_storage/macos/Runner/AppDelegate.swift b/utilities/poc_local_storage/macos/Runner/AppDelegate.swift new file mode 100644 index 00000000000..d53ef643772 --- /dev/null +++ b/utilities/poc_local_storage/macos/Runner/AppDelegate.swift @@ -0,0 +1,9 @@ +import Cocoa +import FlutterMacOS + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/utilities/poc_local_storage/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/utilities/poc_local_storage/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000000..a2ec33f19f1 --- /dev/null +++ b/utilities/poc_local_storage/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/utilities/poc_local_storage/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/utilities/poc_local_storage/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 00000000000..82b6f9d9a33 Binary files /dev/null and b/utilities/poc_local_storage/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ diff --git a/utilities/poc_local_storage/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/utilities/poc_local_storage/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 00000000000..13b35eba55c Binary files /dev/null and b/utilities/poc_local_storage/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ diff --git a/utilities/poc_local_storage/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/utilities/poc_local_storage/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 00000000000..0a3f5fa40fb Binary files /dev/null and b/utilities/poc_local_storage/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ diff --git a/utilities/poc_local_storage/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/utilities/poc_local_storage/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png new file mode 100644 index 00000000000..bdb57226d5f Binary files /dev/null and b/utilities/poc_local_storage/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ diff --git a/utilities/poc_local_storage/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/utilities/poc_local_storage/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png new file mode 100644 index 00000000000..f083318e09c Binary files /dev/null and b/utilities/poc_local_storage/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ diff --git a/utilities/poc_local_storage/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/utilities/poc_local_storage/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png new file mode 100644 index 00000000000..326c0e72c9d Binary files /dev/null and b/utilities/poc_local_storage/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ diff --git a/utilities/poc_local_storage/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/utilities/poc_local_storage/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 00000000000..2f1632cfddf Binary files /dev/null and b/utilities/poc_local_storage/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ diff --git a/utilities/poc_local_storage/macos/Runner/Base.lproj/MainMenu.xib b/utilities/poc_local_storage/macos/Runner/Base.lproj/MainMenu.xib new file mode 100644 index 00000000000..80e867a4e06 --- /dev/null +++ b/utilities/poc_local_storage/macos/Runner/Base.lproj/MainMenu.xib @@ -0,0 +1,343 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/utilities/poc_local_storage/macos/Runner/Configs/AppInfo.xcconfig b/utilities/poc_local_storage/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 00000000000..b55816894cf --- /dev/null +++ b/utilities/poc_local_storage/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = poc_local_storage + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.example.pocLocalStorage + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2024 com.example. All rights reserved. diff --git a/utilities/poc_local_storage/macos/Runner/Configs/Debug.xcconfig b/utilities/poc_local_storage/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 00000000000..36b0fd9464f --- /dev/null +++ b/utilities/poc_local_storage/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/utilities/poc_local_storage/macos/Runner/Configs/Release.xcconfig b/utilities/poc_local_storage/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 00000000000..dff4f49561c --- /dev/null +++ b/utilities/poc_local_storage/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/utilities/poc_local_storage/macos/Runner/Configs/Warnings.xcconfig b/utilities/poc_local_storage/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 00000000000..42bcbf4780b --- /dev/null +++ b/utilities/poc_local_storage/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/utilities/poc_local_storage/macos/Runner/DebugProfile.entitlements b/utilities/poc_local_storage/macos/Runner/DebugProfile.entitlements new file mode 100644 index 00000000000..dddb8a30c85 --- /dev/null +++ b/utilities/poc_local_storage/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + + diff --git a/utilities/poc_local_storage/macos/Runner/Info.plist b/utilities/poc_local_storage/macos/Runner/Info.plist new file mode 100644 index 00000000000..4789daa6a44 --- /dev/null +++ b/utilities/poc_local_storage/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/utilities/poc_local_storage/macos/Runner/MainFlutterWindow.swift b/utilities/poc_local_storage/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 00000000000..3cc05eb2349 --- /dev/null +++ b/utilities/poc_local_storage/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/utilities/poc_local_storage/macos/Runner/Release.entitlements b/utilities/poc_local_storage/macos/Runner/Release.entitlements new file mode 100644 index 00000000000..852fa1a4728 --- /dev/null +++ b/utilities/poc_local_storage/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/utilities/poc_local_storage/macos/RunnerTests/RunnerTests.swift b/utilities/poc_local_storage/macos/RunnerTests/RunnerTests.swift new file mode 100644 index 00000000000..61f3bd1fc50 --- /dev/null +++ b/utilities/poc_local_storage/macos/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Cocoa +import FlutterMacOS +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/utilities/poc_local_storage/package-lock.json b/utilities/poc_local_storage/package-lock.json new file mode 100644 index 00000000000..af493e4dd8a --- /dev/null +++ b/utilities/poc_local_storage/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "poc_local_storage", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} diff --git a/utilities/poc_local_storage/pubspec.yaml b/utilities/poc_local_storage/pubspec.yaml new file mode 100644 index 00000000000..9a0b93a197a --- /dev/null +++ b/utilities/poc_local_storage/pubspec.yaml @@ -0,0 +1,29 @@ +name: poc_local_storage +description: "A new Flutter project." +publish_to: "none" # Remove this line if you wish to publish to pub.dev + +version: 1.0.0+1 + +environment: + sdk: ">=3.5.0 <4.0.0" + flutter: ">=3.24.1" + +dependencies: + collection: ^1.18.0 + file_picker: ^8.0.7 + flutter: + sdk: flutter + flutter_secure_storage: ^9.2.2 + flutter_web_plugins: + sdk: flutter + go_router: ^14.2.3 + pointycastle: ^3.9.1 + +dev_dependencies: + catalyst_analysis: ^2.0.0 + +dependency_overrides: + flutter_secure_storage_web: ^2.0.0-beta.1 + +flutter: + uses-material-design: true diff --git a/utilities/poc_local_storage/web/favicon.png b/utilities/poc_local_storage/web/favicon.png new file mode 100644 index 00000000000..8aaa46ac1ae Binary files /dev/null and b/utilities/poc_local_storage/web/favicon.png differ diff --git a/utilities/poc_local_storage/web/icons/Icon-192.png b/utilities/poc_local_storage/web/icons/Icon-192.png new file mode 100644 index 00000000000..b749bfef074 Binary files /dev/null and b/utilities/poc_local_storage/web/icons/Icon-192.png differ diff --git a/utilities/poc_local_storage/web/icons/Icon-512.png b/utilities/poc_local_storage/web/icons/Icon-512.png new file mode 100644 index 00000000000..88cfd48dff1 Binary files /dev/null and b/utilities/poc_local_storage/web/icons/Icon-512.png differ diff --git a/utilities/poc_local_storage/web/icons/Icon-maskable-192.png b/utilities/poc_local_storage/web/icons/Icon-maskable-192.png new file mode 100644 index 00000000000..eb9b4d76e52 Binary files /dev/null and b/utilities/poc_local_storage/web/icons/Icon-maskable-192.png differ diff --git a/utilities/poc_local_storage/web/icons/Icon-maskable-512.png b/utilities/poc_local_storage/web/icons/Icon-maskable-512.png new file mode 100644 index 00000000000..d69c56691fb Binary files /dev/null and b/utilities/poc_local_storage/web/icons/Icon-maskable-512.png differ diff --git a/utilities/poc_local_storage/web/index.html b/utilities/poc_local_storage/web/index.html new file mode 100644 index 00000000000..992d45d70b0 --- /dev/null +++ b/utilities/poc_local_storage/web/index.html @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + poc_local_storage + + + + + + diff --git a/utilities/poc_local_storage/web/manifest.json b/utilities/poc_local_storage/web/manifest.json new file mode 100644 index 00000000000..18ee8edd6ee --- /dev/null +++ b/utilities/poc_local_storage/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "poc_local_storage", + "short_name": "poc_local_storage", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} diff --git a/utilities/poc_local_storage/windows/.gitignore b/utilities/poc_local_storage/windows/.gitignore new file mode 100644 index 00000000000..d492d0d98c8 --- /dev/null +++ b/utilities/poc_local_storage/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/utilities/poc_local_storage/windows/CMakeLists.txt b/utilities/poc_local_storage/windows/CMakeLists.txt new file mode 100644 index 00000000000..b3a62eda7d3 --- /dev/null +++ b/utilities/poc_local_storage/windows/CMakeLists.txt @@ -0,0 +1,108 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(poc_local_storage LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "poc_local_storage") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(VERSION 3.14...3.25) + +# Define build configuration option. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() +# Define settings for the Profile build mode. +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/utilities/poc_local_storage/windows/flutter/CMakeLists.txt b/utilities/poc_local_storage/windows/flutter/CMakeLists.txt new file mode 100644 index 00000000000..903f4899d6f --- /dev/null +++ b/utilities/poc_local_storage/windows/flutter/CMakeLists.txt @@ -0,0 +1,109 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + ${FLUTTER_TARGET_PLATFORM} $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/utilities/poc_local_storage/windows/flutter/generated_plugin_registrant.cc b/utilities/poc_local_storage/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 00000000000..0c507538547 --- /dev/null +++ b/utilities/poc_local_storage/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,14 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include + +void RegisterPlugins(flutter::PluginRegistry* registry) { + FlutterSecureStorageWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); +} diff --git a/utilities/poc_local_storage/windows/flutter/generated_plugin_registrant.h b/utilities/poc_local_storage/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 00000000000..dc139d85a93 --- /dev/null +++ b/utilities/poc_local_storage/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/utilities/poc_local_storage/windows/flutter/generated_plugins.cmake b/utilities/poc_local_storage/windows/flutter/generated_plugins.cmake new file mode 100644 index 00000000000..4fc759c48f3 --- /dev/null +++ b/utilities/poc_local_storage/windows/flutter/generated_plugins.cmake @@ -0,0 +1,24 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + flutter_secure_storage_windows +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/utilities/poc_local_storage/windows/runner/CMakeLists.txt b/utilities/poc_local_storage/windows/runner/CMakeLists.txt new file mode 100644 index 00000000000..394917c053a --- /dev/null +++ b/utilities/poc_local_storage/windows/runner/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the build version. +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/utilities/poc_local_storage/windows/runner/Runner.rc b/utilities/poc_local_storage/windows/runner/Runner.rc new file mode 100644 index 00000000000..11407457cba --- /dev/null +++ b/utilities/poc_local_storage/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD +#else +#define VERSION_AS_NUMBER 1,0,0,0 +#endif + +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.example" "\0" + VALUE "FileDescription", "poc_local_storage" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "poc_local_storage" "\0" + VALUE "LegalCopyright", "Copyright (C) 2024 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "poc_local_storage.exe" "\0" + VALUE "ProductName", "poc_local_storage" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/utilities/poc_local_storage/windows/runner/flutter_window.cpp b/utilities/poc_local_storage/windows/runner/flutter_window.cpp new file mode 100644 index 00000000000..955ee3038f9 --- /dev/null +++ b/utilities/poc_local_storage/windows/runner/flutter_window.cpp @@ -0,0 +1,71 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + + flutter_controller_->engine()->SetNextFrameCallback([&]() { + this->Show(); + }); + + // Flutter can complete the first frame before the "show window" callback is + // registered. The following call ensures a frame is pending to ensure the + // window is shown. It is a no-op if the first frame hasn't completed yet. + flutter_controller_->ForceRedraw(); + + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/utilities/poc_local_storage/windows/runner/flutter_window.h b/utilities/poc_local_storage/windows/runner/flutter_window.h new file mode 100644 index 00000000000..6da0652f05f --- /dev/null +++ b/utilities/poc_local_storage/windows/runner/flutter_window.h @@ -0,0 +1,33 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/utilities/poc_local_storage/windows/runner/main.cpp b/utilities/poc_local_storage/windows/runner/main.cpp new file mode 100644 index 00000000000..34297c267af --- /dev/null +++ b/utilities/poc_local_storage/windows/runner/main.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.Create(L"poc_local_storage", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/utilities/poc_local_storage/windows/runner/resource.h b/utilities/poc_local_storage/windows/runner/resource.h new file mode 100644 index 00000000000..66a65d1e4a7 --- /dev/null +++ b/utilities/poc_local_storage/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/utilities/poc_local_storage/windows/runner/resources/app_icon.ico b/utilities/poc_local_storage/windows/runner/resources/app_icon.ico new file mode 100644 index 00000000000..c04e20caf63 Binary files /dev/null and b/utilities/poc_local_storage/windows/runner/resources/app_icon.ico differ diff --git a/utilities/poc_local_storage/windows/runner/runner.exe.manifest b/utilities/poc_local_storage/windows/runner/runner.exe.manifest new file mode 100644 index 00000000000..a42ea7687cb --- /dev/null +++ b/utilities/poc_local_storage/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/utilities/poc_local_storage/windows/runner/utils.cpp b/utilities/poc_local_storage/windows/runner/utils.cpp new file mode 100644 index 00000000000..3a0b46511a7 --- /dev/null +++ b/utilities/poc_local_storage/windows/runner/utils.cpp @@ -0,0 +1,65 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + unsigned int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr) + -1; // remove the trailing null character + int input_length = (int)wcslen(utf16_string); + std::string utf8_string; + if (target_length == 0 || target_length > utf8_string.max_size()) { + return utf8_string; + } + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + input_length, utf8_string.data(), target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/utilities/poc_local_storage/windows/runner/utils.h b/utilities/poc_local_storage/windows/runner/utils.h new file mode 100644 index 00000000000..3879d547557 --- /dev/null +++ b/utilities/poc_local_storage/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/utilities/poc_local_storage/windows/runner/win32_window.cpp b/utilities/poc_local_storage/windows/runner/win32_window.cpp new file mode 100644 index 00000000000..60608d0fe5b --- /dev/null +++ b/utilities/poc_local_storage/windows/runner/win32_window.cpp @@ -0,0 +1,288 @@ +#include "win32_window.h" + +#include +#include + +#include "resource.h" + +namespace { + +/// Window attribute that enables dark mode window decorations. +/// +/// Redefined in case the developer's machine has a Windows SDK older than +/// version 10.0.22000.0. +/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +/// Registry key for app theme preference. +/// +/// A value of 0 indicates apps should use dark mode. A non-zero or missing +/// value indicates apps should use light mode. +constexpr const wchar_t kGetPreferredBrightnessRegKey[] = + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; +constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + } + FreeLibrary(user32_module); +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registrar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::Create(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + UpdateTheme(window); + + return OnCreate(); +} + +bool Win32Window::Show() { + return ShowWindow(window_handle_, SW_SHOWNORMAL); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + + case WM_DWMCOLORIZATIONCOLORCHANGED: + UpdateTheme(hwnd); + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} + +void Win32Window::UpdateTheme(HWND const window) { + DWORD light_mode; + DWORD light_mode_size = sizeof(light_mode); + LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, + kGetPreferredBrightnessRegValue, + RRF_RT_REG_DWORD, nullptr, &light_mode, + &light_mode_size); + + if (result == ERROR_SUCCESS) { + BOOL enable_dark_mode = light_mode == 0; + DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, + &enable_dark_mode, sizeof(enable_dark_mode)); + } +} diff --git a/utilities/poc_local_storage/windows/runner/win32_window.h b/utilities/poc_local_storage/windows/runner/win32_window.h new file mode 100644 index 00000000000..e901dde684e --- /dev/null +++ b/utilities/poc_local_storage/windows/runner/win32_window.h @@ -0,0 +1,102 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates a win32 window with |title| that is positioned and sized using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size this function will scale the inputted width and height as + // as appropriate for the default monitor. The window is invisible until + // |Show| is called. Returns true if the window was created successfully. + bool Create(const std::wstring& title, const Point& origin, const Size& size); + + // Show the current window. Returns true if the window was successfully shown. + bool Show(); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + // Update the window frame's theme to match the system theme. + static void UpdateTheme(HWND const window); + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/utilities/wallet-tester/package-lock.json b/utilities/wallet-tester/package-lock.json index 1785f2e448a..0cce2b825c9 100644 --- a/utilities/wallet-tester/package-lock.json +++ b/utilities/wallet-tester/package-lock.json @@ -5184,12 +5184,12 @@ } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "devOptional": true, "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": {