├── .cargo └── audit.toml ├── .github └── workflows │ ├── main.yaml │ ├── rust_audit.yaml │ └── rust_periodic_audit.yaml ├── .gitignore ├── Cargo.toml ├── FUNDING.json ├── LICENSE ├── README.md ├── ledger-apdu ├── Cargo.toml ├── README.md └── src │ ├── lib.rs │ └── tests.rs ├── ledger-transport-hid ├── Cargo.toml ├── README.md └── src │ ├── errors.rs │ └── lib.rs ├── ledger-transport ├── .gitignore ├── Cargo.toml ├── README.md └── src │ └── lib.rs ├── ledger-zondax-generic ├── Cargo.toml ├── README.md └── src │ ├── errors.rs │ └── lib.rs ├── rustfmt.toml └── tests ├── .gitignore ├── Makefile ├── package.json ├── test.js └── yarn.lock /.cargo/audit.toml: -------------------------------------------------------------------------------- 1 | [advisories] 2 | ignore = [] 3 | -------------------------------------------------------------------------------- /.github/workflows/main.yaml: -------------------------------------------------------------------------------- 1 | name: "Main workflow" 2 | on: 3 | - push 4 | 5 | jobs: 6 | configure: 7 | runs-on: ubuntu-latest 8 | outputs: 9 | uid_gid: ${{ steps.get-user.outputs.uid_gid }} 10 | steps: 11 | - id: get-user 12 | run: echo "::set-output name=uid_gid::$(id -u):$(id -g)" 13 | 14 | lint: 15 | runs-on: ubuntu-latest 16 | container: 17 | image: zondax/rust-ci:latest 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v4 21 | with: 22 | submodules: true 23 | - run: sudo apt-get install -y libudev-dev libusb-1.0-0-dev protobuf-compiler 24 | - name: show versions 25 | run: | 26 | rustup show 27 | - name: Install rustfmt 28 | run: rustup component add rustfmt --toolchain nightly 29 | - name: rustfmt 30 | run: | 31 | cargo +nightly fmt -- --check 32 | - name: rust cache 33 | uses: Swatinem/rust-cache@v1 34 | with: 35 | # setup sharedKey to share cache with other jobs 36 | sharedKey: ${{ github.run_id }}-${{ github.run_attempt }} 37 | 38 | - name: clippy 39 | run: | 40 | cargo clippy --version 41 | cargo clippy --all-features 42 | 43 | build: 44 | runs-on: ubuntu-latest 45 | needs: lint 46 | container: 47 | image: zondax/rust-ci:latest 48 | steps: 49 | - name: Checkout 50 | uses: actions/checkout@v4 51 | with: 52 | submodules: true 53 | - run: sudo apt-get install -y libudev-dev libusb-1.0-0-dev protobuf-compiler 54 | - name: show versions 55 | run: | 56 | rustup show 57 | 58 | - name: rust cache 59 | uses: Swatinem/rust-cache@v1 60 | with: 61 | # setup sharedKey to share cache with other jobs 62 | sharedKey: ${{ github.run_id }}-${{ github.run_attempt }} 63 | 64 | - name: build 65 | run: cargo build 66 | 67 | - name: build --release 68 | run: cargo build --release 69 | 70 | tests: 71 | runs-on: ubuntu-latest 72 | needs: build 73 | container: 74 | image: zondax/rust-ci:latest 75 | steps: 76 | - name: Checkout 77 | uses: actions/checkout@v4 78 | with: 79 | submodules: true 80 | - run: sudo apt-get install -y libudev-dev libusb-1.0-0-dev protobuf-compiler 81 | 82 | - name: rust cache 83 | uses: Swatinem/rust-cache@v1 84 | with: 85 | # setup sharedKey to share cache with other jobs 86 | sharedKey: ${{ github.run_id }}-${{ github.run_attempt }} 87 | 88 | - name: test --all-features 89 | run: | 90 | cargo test --all --all-features -- --test-threads 1 --skip integration 91 | -------------------------------------------------------------------------------- /.github/workflows/rust_audit.yaml: -------------------------------------------------------------------------------- 1 | name: Security audit 2 | 3 | on: 4 | push: 5 | paths: 6 | - '**/Cargo.toml' 7 | - '**/Cargo.lock' 8 | 9 | jobs: 10 | security_audit: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | with: 15 | submodules: true 16 | 17 | 18 | - name: Run audit 19 | uses: actions-rs/audit-check@v1 20 | with: 21 | token: ${{ secrets.GITHUB_TOKEN }} 22 | -------------------------------------------------------------------------------- /.github/workflows/rust_periodic_audit.yaml: -------------------------------------------------------------------------------- 1 | name: Scheduled security audit 2 | 3 | on: 4 | schedule: 5 | # run everyday at midnight 6 | - cron: '0 0 * * *' 7 | 8 | jobs: 9 | security_audit: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | 14 | - name: Run audit 15 | uses: actions-rs/audit-check@v1 16 | with: 17 | token: ${{ secrets.GITHUB_TOKEN }} 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | \.idea/ 13 | 14 | /target 15 | **/*.rs.bk 16 | Cargo.lock 17 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "ledger-apdu", 5 | "ledger-transport", 6 | "ledger-transport-hid", 7 | "ledger-zondax-generic", 8 | ] 9 | 10 | exclude = [] 11 | 12 | [profile.release] 13 | opt-level = "s" 14 | overflow-checks = true 15 | 16 | [patch.crates-io] 17 | ledger-apdu = { path = "ledger-apdu" } 18 | ledger-transport = { path = "ledger-transport" } 19 | ledger-transport-hid = { path = "ledger-transport-hid" } 20 | ledger-zondax-generic = { path = "ledger-zondax-generic" } 21 | -------------------------------------------------------------------------------- /FUNDING.json: -------------------------------------------------------------------------------- 1 | { 2 | "drips": { 3 | "ethereum": { 4 | "ownedBy": "0x8D8D391c6690C0d0BDe73CB7Ef8B7655e68efe76" 5 | }, 6 | "filecoin": { 7 | "ownedBy": "0x8D8D391c6690C0d0BDe73CB7Ef8B7655e68efe76" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2018 Zondax AG 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ledger-rs 2 | 3 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 4 | [![Github Actions](https://github.com/Zondax/ledger-rs/actions/workflows/main.yaml/badge.svg)](https://github.com/Zondax/ledger-rs) 5 | 6 | Communication library between Rust and Ledger Nano S/X devices 7 | 8 | # How to use 9 | 10 | ## Developing an App interface 11 | 12 | To develop an app interface it's recommended to depend on `ledger-transport` and make the API generic over the an `Exchange` (trait). 13 | An example can be found in [`ledger-zondax-hid` tests](./ledger-zondax-hid/src/lib.rs#L380) (provided by `ledger-zondax-generic`) where `get_device_info` is independent of the transport used. 14 | 15 | ## Using an App interface 16 | 17 | To use an app interface, so when communicating with a ledger device (or emulator) the transports available are: 18 | * `ledger-transport-hid` 19 | * `ledger-transport-zemu` 20 | 21 | # How to publish to crates.io 22 | 23 | Obviously only members of the Zondax/crates team are allowed to publish. 24 | 25 | Afterwards, there's a correct order to publish the crates, based on the crate dependencies: 26 | 27 | * ledger-apdu 28 | * ledger-transport 29 | * ledger-zondax-generic 30 | 31 | Then, the rest of the crates can be published in any order. 32 | 33 | ``sh 34 | cargo login 35 | cargo package -p ledger-apdu 36 | cargo publish -p ledger-apdu 37 | 38 | cargo package -p ledger-transport 39 | cargo publish -p ledger-transport 40 | 41 | cargo package -p ledger-zondax-generic 42 | cargo publish -p ledger-zondax-generic 43 | 44 | cargo package -p ledger-transport-hid 45 | cargo publish -p ledger-transport-hid 46 | `` -------------------------------------------------------------------------------- /ledger-apdu/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ledger-apdu" 3 | description = "Ledger Hardware Wallet - Common APDU Protocol Types" 4 | version = "0.11.0" 5 | license = "Apache-2.0" 6 | authors = ["Zondax AG "] 7 | homepage = "https://github.com/zondax/ledger-rs" 8 | repository = "https://github.com/zondax/ledger-rs" 9 | readme = "README.md" 10 | categories = ["authentication", "cryptography"] 11 | keywords = ["ledger", "nano", "blue", "apdu"] 12 | edition = "2021" 13 | 14 | [features] 15 | std = ["snafu/std", "no-std-compat/std"] 16 | default = ["std"] 17 | 18 | [dependencies] 19 | arrayref = "0.3" 20 | no-std-compat = "0.4" 21 | snafu = { version = "0.8", default-features = false } 22 | -------------------------------------------------------------------------------- /ledger-apdu/README.md: -------------------------------------------------------------------------------- 1 | # ledger-generic 2 | 3 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 4 | 5 | Generic utilitary rust library used to communicate with Nano S/X devices 6 | -------------------------------------------------------------------------------- /ledger-apdu/src/lib.rs: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * (c) 2022 Zondax AG 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ********************************************************************************/ 16 | //! This crate contains a couple of utilities to talk via the APDU protocol to Ledger devices 17 | 18 | #![no_std] 19 | #![deny(missing_docs)] 20 | 21 | extern crate no_std_compat as std; 22 | use core::ops::Deref; 23 | use std::convert::{TryFrom, TryInto}; 24 | 25 | use snafu::prelude::*; 26 | 27 | #[cfg(test)] 28 | mod tests; 29 | 30 | #[derive(Debug, Clone)] 31 | /// An APDU command 32 | pub struct APDUCommand { 33 | ///APDU Class 34 | /// 35 | /// An incorrect APDU Class will prevent you from communicating with the device 36 | pub cla: u8, 37 | ///APDU Instruction 38 | pub ins: u8, 39 | ///First parameter of instruction 40 | pub p1: u8, 41 | ///Second parameter of instruction 42 | pub p2: u8, 43 | ///Payload of the instruction, can be empty 44 | pub data: B, 45 | } 46 | 47 | #[cfg(feature = "std")] 48 | impl APDUCommand 49 | where 50 | B: Deref, 51 | { 52 | /// Serialize this [APDUCommand] to be sent to the device 53 | pub fn serialize(&self) -> std::vec::Vec { 54 | let mut v = std::vec![self.cla, self.ins, self.p1, self.p2, self.data.len() as u8]; 55 | v.extend(self.data.iter()); 56 | v 57 | } 58 | } 59 | 60 | #[derive(Debug)] 61 | /// An APDU answer, whole last 2 bytes are interpreted as `retcode` 62 | pub struct APDUAnswer { 63 | data: B, 64 | retcode: u16, 65 | } 66 | 67 | #[derive(Debug, Snafu, PartialEq, Eq)] 68 | /// Error interpreting bytes as an APDU answer 69 | pub enum APDUAnswerError { 70 | #[snafu(display("answer too short (< 2 bytes)"))] 71 | /// Passed APDU answer was less than the minimum 2 bytes required for the return code 72 | TooShort, 73 | } 74 | 75 | impl APDUAnswer 76 | where 77 | B: Deref, 78 | { 79 | /// Attempt to interpret the given slice as an APDU answer 80 | pub fn from_answer(answer: B) -> Result { 81 | ensure!(answer.len() >= 2, TooShortSnafu); 82 | let retcode = arrayref::array_ref!(answer, answer.len() - 2, 2); 83 | let retcode = u16::from_be_bytes(*retcode); 84 | 85 | Ok(APDUAnswer { data: answer, retcode }) 86 | } 87 | 88 | /// Will return the answer's payload 89 | #[inline(always)] 90 | pub fn apdu_data(&self) -> &[u8] { 91 | &self.data[.. self.data.len() - 2] 92 | } 93 | 94 | /// Will return the answer's payload 95 | #[inline(always)] 96 | pub fn data(&self) -> &[u8] { 97 | self.apdu_data() 98 | } 99 | 100 | /// Will attempt to interpret the error code as an [APDUErrorCode], 101 | /// returning the code as is otherwise 102 | pub fn error_code(&self) -> Result { 103 | self.retcode 104 | .try_into() 105 | .map_err(|_| self.retcode) 106 | } 107 | 108 | /// Returns the raw return code 109 | #[inline(always)] 110 | pub fn retcode(&self) -> u16 { 111 | self.retcode 112 | } 113 | } 114 | 115 | #[derive(Copy, Clone, Debug, Snafu, PartialEq, Eq)] 116 | #[repr(u16)] 117 | /// Common known APDU error codes 118 | pub enum APDUErrorCode { 119 | ///success 120 | NoError = 0x9000, 121 | ///error during apdu execution 122 | ExecutionError = 0x6400, 123 | ///apdu command wrong length 124 | WrongLength = 0x6700, 125 | ///empty apdu buffer 126 | EmptyBuffer = 0x6982, 127 | ///apdu buffer too small 128 | OutputBufferTooSmall = 0x6983, 129 | ///apdu parameters invalid 130 | DataInvalid = 0x6984, 131 | ///apdu preconditions not satisfied 132 | ConditionsNotSatisfied = 0x6985, 133 | ///apdu command not allowed 134 | CommandNotAllowed = 0x6986, 135 | ///apdu data field incorrect (bad key) 136 | BadKeyHandle = 0x6A80, 137 | ///apdu p1 or p2 incorrect 138 | InvalidP1P2 = 0x6B00, 139 | ///apdu instruction not supported or invalid 140 | InsNotSupported = 0x6D00, 141 | ///apdu class not supported or invalid 142 | ClaNotSupported = 0x6E00, 143 | ///unknown apdu error 144 | Unknown = 0x6F00, 145 | ///apdu sign verify error 146 | SignVerifyError = 0x6F01, 147 | } 148 | 149 | #[cfg(feature = "std")] 150 | impl APDUErrorCode { 151 | /// Quick-hand to retrieve the error code's description / display 152 | pub fn description(&self) -> std::string::String { 153 | std::format!("{}", self) 154 | } 155 | } 156 | 157 | impl From for u16 { 158 | fn from(code: APDUErrorCode) -> Self { 159 | code as u16 160 | } 161 | } 162 | 163 | impl TryFrom for APDUErrorCode { 164 | type Error = (); 165 | 166 | fn try_from(value: u16) -> Result { 167 | let this = match value { 168 | 0x9000 => Self::NoError, 169 | 0x6400 => Self::ExecutionError, 170 | 0x6700 => Self::WrongLength, 171 | 0x6982 => Self::EmptyBuffer, 172 | 0x6983 => Self::OutputBufferTooSmall, 173 | 0x6984 => Self::DataInvalid, 174 | 0x6985 => Self::ConditionsNotSatisfied, 175 | 0x6986 => Self::CommandNotAllowed, 176 | 0x6A80 => Self::BadKeyHandle, 177 | 0x6B00 => Self::InvalidP1P2, 178 | 0x6D00 => Self::InsNotSupported, 179 | 0x6E00 => Self::ClaNotSupported, 180 | 0x6F00 => Self::Unknown, 181 | 0x6F01 => Self::SignVerifyError, 182 | _ => return Err(()), 183 | }; 184 | 185 | Ok(this) 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /ledger-apdu/src/tests.rs: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * (c) 2022 Zondax AG 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ********************************************************************************/ 16 | use super::*; 17 | 18 | const SERIALIZED_APDU: &[u8] = &[0xFF, 0x00, 0, 0, 3, 0x42, 0x42, 0x42]; 19 | const APDU_RESPONSE: &[u8] = &[0xDE, 0xAD, 0xBE, 0xEF, 0x90, 0x00]; 20 | 21 | #[test] 22 | #[cfg(feature = "std")] 23 | fn apdu_command_vec() { 24 | let data = std::vec![SERIALIZED_APDU[5]; 3]; 25 | 26 | let command = APDUCommand { cla: 0xFF, ins: 0x00, p1: 0, p2: 0, data }; 27 | 28 | assert_eq!(SERIALIZED_APDU, &command.serialize()[..]) 29 | } 30 | 31 | #[test] 32 | fn apdu_command_slice() { 33 | let data = &SERIALIZED_APDU[5 ..]; 34 | 35 | let _ = APDUCommand { cla: 0xFF, ins: 0x00, p1: 0, p2: 0, data }; 36 | } 37 | 38 | #[test] 39 | fn apdu_answer_success() { 40 | let answer = APDUAnswer::from_answer(APDU_RESPONSE).expect("valid answer length >= 2"); 41 | 42 | let code = answer 43 | .error_code() 44 | .expect("valid error code"); 45 | assert_eq!(code, APDUErrorCode::NoError); 46 | 47 | assert_eq!(answer.apdu_data(), &APDU_RESPONSE[.. 4]); 48 | } 49 | 50 | #[test] 51 | fn apdu_answer_vec() { 52 | let answer = APDUAnswer::from_answer(APDU_RESPONSE.to_vec()).expect("valid answer length >= 2"); 53 | 54 | let code = answer 55 | .error_code() 56 | .expect("valid error code"); 57 | assert_eq!(code, APDUErrorCode::NoError); 58 | 59 | assert_eq!(answer.apdu_data(), &APDU_RESPONSE[.. 4]); 60 | } 61 | 62 | #[test] 63 | fn apdu_answer_error() { 64 | let answer = APDUAnswer::from_answer(&[0x64, 0x00][..]).expect("valid answer length >= 2"); 65 | 66 | let code = answer 67 | .error_code() 68 | .expect("valid error code"); 69 | assert_eq!(code, APDUErrorCode::ExecutionError); 70 | 71 | assert_eq!(answer.apdu_data(), &[]); 72 | } 73 | 74 | #[test] 75 | fn apdu_answer_unknown() { 76 | let answer = APDUAnswer::from_answer(&APDU_RESPONSE[.. 4]).expect("valid answer length >= 2"); 77 | 78 | let code = answer 79 | .error_code() 80 | .expect_err("invalid error code"); 81 | assert_eq!(code, 0xBEEF); 82 | 83 | assert_eq!(answer.apdu_data(), &[0xDE, 0xAD]); 84 | } 85 | 86 | #[test] 87 | fn apdu_answer_too_short() { 88 | let answer = APDUAnswer::from_answer(&[][..]).expect_err("empty answer"); 89 | 90 | assert_eq!(answer, APDUAnswerError::TooShort); 91 | } 92 | -------------------------------------------------------------------------------- /ledger-transport-hid/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ledger-transport-hid" 3 | description = "Ledger Hardware Wallet - HID Transport" 4 | version = "0.11.0" 5 | license = "Apache-2.0" 6 | authors = ["Zondax AG "] 7 | homepage = "https://github.com/zondax/ledger-rs" 8 | repository = "https://github.com/zondax/ledger-rs" 9 | readme = "README.md" 10 | categories = ["authentication", "cryptography"] 11 | keywords = ["ledger", "nano", "blue", "apdu"] 12 | edition = "2021" 13 | 14 | [dependencies] 15 | libc = "0.2" 16 | byteorder = "1.5" 17 | cfg-if = "1" 18 | thiserror = "1" 19 | hex = "0.4" 20 | log = "0.4" 21 | 22 | ledger-transport = "0.11.0" 23 | hidapi = { version = "2.6.1", features = ["linux-static-hidraw"], default-features = false } 24 | 25 | [dev-dependencies] 26 | once_cell = "1" 27 | ledger-zondax-generic = "0.11.0" 28 | serial_test = "3" 29 | env_logger = "0.11" 30 | futures = "0.3" 31 | -------------------------------------------------------------------------------- /ledger-transport-hid/README.md: -------------------------------------------------------------------------------- 1 | # ledger-transport-hid 2 | 3 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 4 | 5 | Ledger APDU transport - HID backend 6 | -------------------------------------------------------------------------------- /ledger-transport-hid/src/errors.rs: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * (c) 2022 Zondax AG 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ********************************************************************************/ 16 | use thiserror::Error; 17 | 18 | #[derive(Error, Debug)] 19 | pub enum LedgerHIDError { 20 | /// Device not found error 21 | #[error("Ledger device not found")] 22 | DeviceNotFound, 23 | /// Communication error 24 | #[error("Ledger device: communication error `{0}`")] 25 | Comm(&'static str), 26 | /// i/o error 27 | #[error("Ledger device: i/o error")] 28 | Io(#[from] std::io::Error), 29 | /// HID error 30 | #[error("Ledger device: Io error")] 31 | Hid(#[from] hidapi::HidError), 32 | /// UT8F error 33 | #[error("Ledger device: UTF8 error")] 34 | UTF8(#[from] std::str::Utf8Error), 35 | } 36 | -------------------------------------------------------------------------------- /ledger-transport-hid/src/lib.rs: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * (c) 2022 Zondax AG 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ********************************************************************************/ 16 | mod errors; 17 | use std::{io::Cursor, ops::Deref, sync::Mutex}; 18 | 19 | use byteorder::{BigEndian, ReadBytesExt}; 20 | pub use errors::LedgerHIDError; 21 | pub use hidapi; 22 | use hidapi::{DeviceInfo, HidApi, HidDevice}; 23 | use ledger_transport::{async_trait, APDUAnswer, APDUCommand, Exchange}; 24 | use log::info; 25 | 26 | const LEDGER_VID: u16 = 0x2c97; 27 | const LEDGER_USAGE_PAGE: u16 = 0xFFA0; 28 | const LEDGER_CHANNEL: u16 = 0x0101; 29 | // for Windows compatability, we prepend the buffer with a 0x00 30 | // so the actual buffer is 64 bytes 31 | const LEDGER_PACKET_WRITE_SIZE: u8 = 65; 32 | const LEDGER_PACKET_READ_SIZE: u8 = 64; 33 | const LEDGER_TIMEOUT: i32 = 10_000_000; 34 | 35 | pub struct TransportNativeHID { 36 | device: Mutex, 37 | } 38 | 39 | impl TransportNativeHID { 40 | fn is_ledger(dev: &DeviceInfo) -> bool { 41 | dev.vendor_id() == LEDGER_VID && dev.usage_page() == LEDGER_USAGE_PAGE 42 | } 43 | 44 | /// Get a list of ledger devices available 45 | pub fn list_ledgers(api: &HidApi) -> impl Iterator { 46 | api.device_list() 47 | .filter(|dev| Self::is_ledger(dev)) 48 | } 49 | 50 | /// Create a new HID transport, connecting to the first ledger found 51 | /// # Warning 52 | /// Opening the same device concurrently will lead to device lock after the first handle is closed 53 | /// see [issue](https://github.com/ruabmbua/hidapi-rs/issues/81) 54 | pub fn new(api: &HidApi) -> Result { 55 | let first_ledger = Self::list_ledgers(api) 56 | .next() 57 | .ok_or(LedgerHIDError::DeviceNotFound)?; 58 | 59 | Self::open_device(api, first_ledger) 60 | } 61 | 62 | /// Open a specific ledger device 63 | /// 64 | /// # Note 65 | /// No checks are made to ensure the device is a ledger device 66 | /// 67 | /// # Warning 68 | /// Opening the same device concurrently will lead to device lock after the first handle is closed 69 | /// see [issue](https://github.com/ruabmbua/hidapi-rs/issues/81) 70 | pub fn open_device( 71 | api: &HidApi, 72 | device: &DeviceInfo, 73 | ) -> Result { 74 | let device = device.open_device(api)?; 75 | let _ = device.set_blocking_mode(true); 76 | 77 | let ledger = TransportNativeHID { device: Mutex::new(device) }; 78 | 79 | Ok(ledger) 80 | } 81 | 82 | fn write_apdu( 83 | device: &HidDevice, 84 | channel: u16, 85 | apdu_command: &[u8], 86 | ) -> Result { 87 | let command_length = apdu_command.len(); 88 | let mut in_data = Vec::with_capacity(command_length + 2); 89 | in_data.push(((command_length >> 8) & 0xFF) as u8); 90 | in_data.push((command_length & 0xFF) as u8); 91 | in_data.extend_from_slice(apdu_command); 92 | 93 | let mut buffer = vec![0u8; LEDGER_PACKET_WRITE_SIZE as usize]; 94 | // Windows platform requires 0x00 prefix and Linux/Mac tolerate this as well 95 | buffer[0] = 0x00; 96 | buffer[1] = ((channel >> 8) & 0xFF) as u8; // channel big endian 97 | buffer[2] = (channel & 0xFF) as u8; // channel big endian 98 | buffer[3] = 0x05u8; 99 | 100 | for (sequence_idx, chunk) in in_data 101 | .chunks((LEDGER_PACKET_WRITE_SIZE - 6) as usize) 102 | .enumerate() 103 | { 104 | buffer[4] = ((sequence_idx >> 8) & 0xFF) as u8; // sequence_idx big endian 105 | buffer[5] = (sequence_idx & 0xFF) as u8; // sequence_idx big endian 106 | buffer[6 .. 6 + chunk.len()].copy_from_slice(chunk); 107 | 108 | info!("[{:3}] << {:}", buffer.len(), hex::encode(&buffer)); 109 | 110 | let result = device.write(&buffer); 111 | 112 | match result { 113 | Ok(size) => { 114 | if size < buffer.len() { 115 | return Err(LedgerHIDError::Comm("USB write error. Could not send whole message")); 116 | } 117 | }, 118 | Err(x) => return Err(LedgerHIDError::Hid(x)), 119 | } 120 | } 121 | Ok(1) 122 | } 123 | 124 | fn read_apdu( 125 | device: &HidDevice, 126 | channel: u16, 127 | apdu_answer: &mut Vec, 128 | ) -> Result { 129 | let mut buffer = vec![0u8; LEDGER_PACKET_READ_SIZE as usize]; 130 | let mut sequence_idx = 0u16; 131 | let mut expected_apdu_len = 0usize; 132 | 133 | loop { 134 | let res = device.read_timeout(&mut buffer, LEDGER_TIMEOUT)?; 135 | 136 | if (sequence_idx == 0 && res < 7) || res < 5 { 137 | return Err(LedgerHIDError::Comm("Read error. Incomplete header")); 138 | } 139 | 140 | let mut rdr = Cursor::new(&buffer); 141 | 142 | let rcv_channel = rdr.read_u16::()?; 143 | let rcv_tag = rdr.read_u8()?; 144 | let rcv_seq_idx = rdr.read_u16::()?; 145 | 146 | if rcv_channel != channel { 147 | return Err(LedgerHIDError::Comm("Invalid channel")); 148 | } 149 | if rcv_tag != 0x05u8 { 150 | return Err(LedgerHIDError::Comm("Invalid tag")); 151 | } 152 | 153 | if rcv_seq_idx != sequence_idx { 154 | return Err(LedgerHIDError::Comm("Invalid sequence idx")); 155 | } 156 | 157 | if rcv_seq_idx == 0 { 158 | expected_apdu_len = rdr.read_u16::()? as usize; 159 | } 160 | 161 | let available: usize = buffer.len() - rdr.position() as usize; 162 | let missing: usize = expected_apdu_len - apdu_answer.len(); 163 | let end_p = rdr.position() as usize + std::cmp::min(available, missing); 164 | 165 | let new_chunk = &buffer[rdr.position() as usize .. end_p]; 166 | 167 | info!("[{:3}] << {:}", new_chunk.len(), hex::encode(new_chunk)); 168 | 169 | apdu_answer.extend_from_slice(new_chunk); 170 | 171 | if apdu_answer.len() >= expected_apdu_len { 172 | return Ok(apdu_answer.len()); 173 | } 174 | 175 | sequence_idx += 1; 176 | } 177 | } 178 | 179 | pub fn exchange>( 180 | &self, 181 | command: &APDUCommand, 182 | ) -> Result>, LedgerHIDError> { 183 | let device = self 184 | .device 185 | .lock() 186 | .expect("HID device poisoned"); 187 | 188 | Self::write_apdu(&device, LEDGER_CHANNEL, &command.serialize())?; 189 | 190 | let mut answer: Vec = Vec::with_capacity(256); 191 | Self::read_apdu(&device, LEDGER_CHANNEL, &mut answer)?; 192 | 193 | APDUAnswer::from_answer(answer).map_err(|_| LedgerHIDError::Comm("response was too short")) 194 | } 195 | } 196 | 197 | #[async_trait] 198 | impl Exchange for TransportNativeHID { 199 | type Error = LedgerHIDError; 200 | type AnswerType = Vec; 201 | 202 | async fn exchange( 203 | &self, 204 | command: &APDUCommand, 205 | ) -> Result, Self::Error> 206 | where 207 | I: Deref + Send + Sync, 208 | { 209 | self.exchange(command) 210 | } 211 | } 212 | 213 | #[cfg(test)] 214 | mod integration_tests { 215 | use hidapi::HidApi; 216 | use log::info; 217 | use once_cell::sync::Lazy; 218 | use serial_test::serial; 219 | 220 | use crate::{APDUCommand, TransportNativeHID}; 221 | 222 | fn init_logging() { 223 | let _ = env_logger::builder() 224 | .is_test(true) 225 | .try_init(); 226 | } 227 | 228 | fn hidapi() -> &'static HidApi { 229 | static HIDAPI: Lazy = Lazy::new(|| HidApi::new().expect("unable to get HIDAPI")); 230 | 231 | &HIDAPI 232 | } 233 | 234 | #[test] 235 | #[serial] 236 | fn list_all_devices() { 237 | init_logging(); 238 | let api = hidapi(); 239 | 240 | for device_info in api.device_list() { 241 | info!( 242 | "{:#?} - {:#x}/{:#x}/{:#x}/{:#x} {:#} {:#}", 243 | device_info.path(), 244 | device_info.vendor_id(), 245 | device_info.product_id(), 246 | device_info.usage_page(), 247 | device_info.interface_number(), 248 | device_info 249 | .manufacturer_string() 250 | .unwrap_or_default(), 251 | device_info 252 | .product_string() 253 | .unwrap_or_default() 254 | ); 255 | } 256 | } 257 | 258 | #[test] 259 | #[serial] 260 | fn ledger_device_path() { 261 | init_logging(); 262 | let api = hidapi(); 263 | 264 | let mut ledgers = TransportNativeHID::list_ledgers(api); 265 | 266 | let a_ledger = ledgers 267 | .next() 268 | .expect("could not find any ledger device"); 269 | info!("{:?}", a_ledger.path()); 270 | } 271 | 272 | #[test] 273 | #[serial] 274 | fn serialize() { 275 | let data = vec![0, 0, 0, 1, 0, 0, 0, 1]; 276 | 277 | let command = APDUCommand { cla: 0x56, ins: 0x01, p1: 0x00, p2: 0x00, data }; 278 | 279 | let serialized_command = command.serialize(); 280 | 281 | let expected = vec![86, 1, 0, 0, 8, 0, 0, 0, 1, 0, 0, 0, 1]; 282 | 283 | assert_eq!(serialized_command, expected) 284 | } 285 | 286 | #[test] 287 | #[serial] 288 | fn exchange() { 289 | use ledger_zondax_generic::{App, AppExt}; 290 | struct Dummy; 291 | impl App for Dummy { 292 | const CLA: u8 = 0; 293 | } 294 | 295 | init_logging(); 296 | 297 | let ledger = TransportNativeHID::new(hidapi()).expect("Could not get a device"); 298 | 299 | // use device info command that works in the dashboard 300 | let result = futures::executor::block_on(Dummy::get_device_info(&ledger)).expect("Error during exchange"); 301 | info!("{:x?}", result); 302 | } 303 | 304 | #[test] 305 | #[serial] 306 | #[ignore] //see https://github.com/ruabmbua/hidapi-rs/issues/81 307 | fn open_same_device_twice() { 308 | use ledger_zondax_generic::{App, AppExt}; 309 | struct Dummy; 310 | impl App for Dummy { 311 | const CLA: u8 = 0; 312 | } 313 | 314 | init_logging(); 315 | 316 | let api = hidapi(); 317 | let ledger = TransportNativeHID::list_ledgers(api) 318 | .next() 319 | .expect("could not get a device"); 320 | 321 | let t1 = TransportNativeHID::open_device(api, ledger).expect("Could not open device"); 322 | let t2 = TransportNativeHID::open_device(api, ledger).expect("Could not open device"); 323 | 324 | // use device info command that works in the dashboard 325 | let (r1, r2) = futures::executor::block_on(futures::future::join( 326 | Dummy::get_device_info(&t1), 327 | Dummy::get_device_info(&t2), 328 | )); 329 | 330 | let (r1, r2) = (r1.expect("error during exchange (t1)"), r2.expect("error during exchange (t2)")); 331 | 332 | info!("r1: {:x?}", r1); 333 | info!("r2: {:x?}", r2); 334 | 335 | assert_eq!(r1, r2); 336 | } 337 | } 338 | -------------------------------------------------------------------------------- /ledger-transport/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /ledger-transport/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ledger-transport" 3 | description = "Ledger Hardware Wallet - Generic Transport" 4 | version = "0.11.0" 5 | license = "Apache-2.0" 6 | authors = ["Zondax AG "] 7 | homepage = "https://github.com/zondax/ledger-rs" 8 | repository = "https://github.com/zondax/ledger-rs" 9 | readme = "README.md" 10 | categories = ["authentication", "cryptography"] 11 | keywords = ["ledger", "nano", "blue", "apdu"] 12 | edition = "2021" 13 | 14 | [dependencies] 15 | async-trait = "0.1.80" 16 | ledger-apdu = "0.11.0" 17 | -------------------------------------------------------------------------------- /ledger-transport/README.md: -------------------------------------------------------------------------------- 1 | # Rust transport library for Ledger 2 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 3 | 4 | This package provides the interface that transports will use so the app libraries can depend on this definition and accept any transport given by a user crate 5 | -------------------------------------------------------------------------------- /ledger-transport/src/lib.rs: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * (c) 2022 Zondax AG 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ********************************************************************************/ 16 | //! Generic APDU transport library for Ledger Nano S/X apps 17 | 18 | #![deny(trivial_casts, trivial_numeric_casts)] 19 | #![deny(unused_import_braces, unused_qualifications)] 20 | #![deny(missing_docs)] 21 | 22 | use std::ops::Deref; 23 | 24 | pub use async_trait::async_trait; 25 | pub use ledger_apdu::{APDUAnswer, APDUCommand, APDUErrorCode}; 26 | 27 | /// Use to talk to the ledger device 28 | #[async_trait] 29 | pub trait Exchange { 30 | /// Error defined by Transport used 31 | type Error; 32 | 33 | /// The concrete type containing the APDUAnswer 34 | type AnswerType: Deref + Send; 35 | 36 | /// Send a command with the given transport and retrieve an answer or a transport error 37 | async fn exchange( 38 | &self, 39 | command: &APDUCommand, 40 | ) -> Result, Self::Error> 41 | where 42 | I: Deref + Send + Sync; 43 | } 44 | -------------------------------------------------------------------------------- /ledger-zondax-generic/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ledger-zondax-generic" 3 | description = "Ledger Hardware Wallet - Common APDU Protocol Types" 4 | version = "0.11.2" 5 | license = "Apache-2.0" 6 | authors = ["Zondax AG "] 7 | homepage = "https://github.com/zondax/ledger-rs" 8 | repository = "https://github.com/zondax/ledger-rs" 9 | readme = "README.md" 10 | categories = ["authentication", "cryptography"] 11 | keywords = ["ledger", "nano", "blue", "apdu"] 12 | edition = "2021" 13 | 14 | [dependencies] 15 | serde = { version = "1", features = ["derive"] } 16 | thiserror = "1" 17 | 18 | ledger-transport = "0.11.0" 19 | async-trait = "0.1" 20 | -------------------------------------------------------------------------------- /ledger-zondax-generic/README.md: -------------------------------------------------------------------------------- 1 | # ledger-zondax-generic 2 | 3 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 4 | 5 | Common APDU commands typically used in Ledger Apps developed by Zondax 6 | -------------------------------------------------------------------------------- /ledger-zondax-generic/src/errors.rs: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * (c) 2022 Zondax AG 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ********************************************************************************/ 16 | use serde::{Deserialize, Serialize}; 17 | use thiserror::Error; 18 | 19 | /// App Error 20 | #[derive(Clone, Debug, Eq, Error, PartialEq, Deserialize, Serialize)] 21 | pub enum LedgerAppError { 22 | /// Invalid version error 23 | #[error("This version is not supported")] 24 | InvalidVersion, 25 | /// The message cannot be empty 26 | #[error("message cannot be empty")] 27 | InvalidEmptyMessage, 28 | /// Invalid payload type in chunk 29 | #[error("The chunk payload type was invalid. First message should be Init")] 30 | InvalidChunkPayloadType, 31 | /// The size fo the message to sign is invalid 32 | #[error("message size is invalid (too big)")] 33 | InvalidMessageSize, 34 | /// Public Key is invalid 35 | #[error("received an invalid PK")] 36 | InvalidPK, 37 | /// No signature has been returned 38 | #[error("received no signature back")] 39 | NoSignature, 40 | /// The signature is not valid 41 | #[error("received an invalid signature")] 42 | InvalidSignature, 43 | /// The derivation is invalid 44 | #[error("invalid derivation path")] 45 | InvalidDerivationPath, 46 | /// The derivation is invalid 47 | #[error("Transport | {0}")] 48 | TransportError(#[from] E), 49 | /// Crypto related errors 50 | #[error("Crypto")] 51 | Crypto, 52 | /// Utf8 related errors 53 | #[error("Utf8 conversion error")] 54 | Utf8, 55 | /// Format ID error 56 | #[error("response format ID not recognized")] 57 | InvalidFormatID, 58 | /// HexEncode 59 | #[error("Couldn't encode string to HEX")] 60 | HexEncode, 61 | /// Application specific error 62 | #[error("App Error: | {0} {1}")] 63 | AppSpecific(u16, String), 64 | ///Unknown error has occurred 65 | #[error("Unknown error: {0}")] 66 | Unknown(u16), 67 | } 68 | -------------------------------------------------------------------------------- /ledger-zondax-generic/src/lib.rs: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * (c) 2022 Zondax AG 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ********************************************************************************/ 16 | //! Support library for Ledger Nano S/X apps developed by Zondax 17 | //! 18 | //! Contains common commands 19 | 20 | #![deny(warnings, trivial_casts)] 21 | #![deny(unused_import_braces, unused_qualifications)] 22 | #![deny(missing_docs)] 23 | 24 | mod errors; 25 | use std::str; 26 | 27 | use async_trait::async_trait; 28 | pub use errors::*; 29 | use ledger_transport::{APDUAnswer, APDUCommand, APDUErrorCode, Exchange}; 30 | use serde::{Deserialize, Serialize}; 31 | 32 | const INS_GET_VERSION: u8 = 0x00; 33 | const CLA_APP_INFO: u8 = 0xb0; 34 | const INS_APP_INFO: u8 = 0x01; 35 | const CLA_DEVICE_INFO: u8 = 0xe0; 36 | const INS_DEVICE_INFO: u8 = 0x01; 37 | const USER_MESSAGE_CHUNK_SIZE: usize = 250; 38 | 39 | /// Chunk payload type 40 | pub enum ChunkPayloadType { 41 | /// First chunk 42 | Init = 0x00, 43 | /// Append chunk 44 | Add = 0x01, 45 | /// Last chunk 46 | Last = 0x02, 47 | } 48 | 49 | #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] 50 | /// App Version 51 | pub struct Version { 52 | /// Application Mode 53 | #[serde(rename(serialize = "testMode"))] 54 | pub mode: u8, 55 | /// Version Major 56 | pub major: u16, 57 | /// Version Minor 58 | pub minor: u16, 59 | /// Version Patch 60 | pub patch: u16, 61 | /// Device is locked 62 | pub locked: bool, 63 | /// Target ID 64 | pub target_id: [u8; 4], 65 | } 66 | 67 | #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] 68 | /// App Information 69 | pub struct AppInfo { 70 | /// Name of the application 71 | #[serde(rename(serialize = "appName"))] 72 | pub app_name: String, 73 | /// App version 74 | #[serde(rename(serialize = "appVersion"))] 75 | pub app_version: String, 76 | /// Flag length 77 | #[serde(rename(serialize = "flagLen"))] 78 | pub flag_len: u8, 79 | /// Flag value 80 | #[serde(rename(serialize = "flagsValue"))] 81 | pub flags_value: u8, 82 | /// Flag Recovery 83 | #[serde(rename(serialize = "flagsRecovery"))] 84 | pub flag_recovery: bool, 85 | /// Flag Signed MCU code 86 | #[serde(rename(serialize = "flagsSignedMCUCode"))] 87 | pub flag_signed_mcu_code: bool, 88 | /// Flag Onboarded 89 | #[serde(rename(serialize = "flagsOnboarded"))] 90 | pub flag_onboarded: bool, 91 | /// Flag Pin Validated 92 | #[serde(rename(serialize = "flagsPINValidated"))] 93 | pub flag_pin_validated: bool, 94 | } 95 | 96 | #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] 97 | /// App Device Info 98 | pub struct DeviceInfo { 99 | /// Target ID 100 | #[serde(rename(serialize = "targetId"))] 101 | pub target_id: [u8; 4], 102 | /// Secure Element Version 103 | #[serde(rename(serialize = "seVersion"))] 104 | pub se_version: String, 105 | /// Device Flag 106 | pub flag: Vec, 107 | /// MCU Version 108 | #[serde(rename(serialize = "mcuVersion"))] 109 | pub mcu_version: String, 110 | } 111 | 112 | /// Defines what we can consider an "App" 113 | pub trait App { 114 | /// App's APDU CLA 115 | const CLA: u8; 116 | } 117 | 118 | #[async_trait] 119 | /// Common commands for any given APP 120 | /// 121 | /// This trait is automatically implemented for any type that implements [App] 122 | pub trait AppExt: App 123 | where 124 | E: Exchange + Send + Sync, 125 | E::Error: std::error::Error, 126 | { 127 | /// Handles the error response from APDU exchange. 128 | /// 129 | /// # Arguments 130 | /// * `response` - The response from the APDU exchange. 131 | /// 132 | /// # Returns 133 | /// A result indicating success or containing a specific ledger application error. 134 | fn handle_response_error(response: &APDUAnswer) -> Result<(), LedgerAppError> { 135 | match response.error_code() { 136 | Ok(APDUErrorCode::NoError) => Ok(()), 137 | Ok(err) => Err(LedgerAppError::AppSpecific(err as _, err.description())), 138 | Err(err) => Err(LedgerAppError::Unknown(err)), 139 | } 140 | } 141 | 142 | /// Handles the error response from APDU exchange. 143 | /// 144 | /// # Arguments 145 | /// * `response` - The response from the APDU exchange. 146 | /// 147 | /// # Returns 148 | /// A result indicating success or containing a specific ledger application error. 149 | fn handle_response_error_signature(response: &APDUAnswer) -> Result<(), LedgerAppError> { 150 | match response.error_code() { 151 | Ok(APDUErrorCode::NoError) if response.data().is_empty() => Err(LedgerAppError::NoSignature), 152 | Ok(APDUErrorCode::NoError) => Ok(()), 153 | Ok(err) => Err(LedgerAppError::AppSpecific(err as _, err.description())), 154 | Err(err) => Err(LedgerAppError::AppSpecific(err, "[APDU_ERROR] Unknown".to_string())), 155 | } 156 | } 157 | 158 | /// Retrieve the device info 159 | /// 160 | /// Works only in the dashboard 161 | async fn get_device_info(transport: &E) -> Result> { 162 | let command = APDUCommand { cla: CLA_DEVICE_INFO, ins: INS_DEVICE_INFO, p1: 0x00, p2: 0x00, data: Vec::new() }; 163 | 164 | let response = transport.exchange(&command).await?; 165 | match response.error_code() { 166 | Ok(APDUErrorCode::NoError) => {}, 167 | Ok(err) => return Err(LedgerAppError::Unknown(err as _)), 168 | Err(err) => return Err(LedgerAppError::Unknown(err)), 169 | } 170 | 171 | let response_data = response.data(); 172 | 173 | let target_id_slice = &response_data[0 .. 4]; 174 | let mut idx = 4; 175 | let se_version_len: usize = response_data[idx] as usize; 176 | idx += 1; 177 | let se_version_bytes = &response_data[idx .. idx + se_version_len]; 178 | 179 | idx += se_version_len; 180 | 181 | let flags_len: usize = response_data[idx] as usize; 182 | idx += 1; 183 | let flag = &response_data[idx .. idx + flags_len]; 184 | idx += flags_len; 185 | 186 | let mcu_version_len: usize = response_data[idx] as usize; 187 | idx += 1; 188 | let mut tmp = &response_data[idx .. idx + mcu_version_len]; 189 | if tmp[mcu_version_len - 1] == 0 { 190 | tmp = &response_data[idx .. idx + mcu_version_len - 1]; 191 | } 192 | 193 | let mut target_id = [Default::default(); 4]; 194 | target_id.copy_from_slice(target_id_slice); 195 | 196 | let se_version = str::from_utf8(se_version_bytes).map_err(|_e| LedgerAppError::Utf8)?; 197 | let mcu_version = str::from_utf8(tmp).map_err(|_e| LedgerAppError::Utf8)?; 198 | 199 | let device_info = DeviceInfo { 200 | target_id, 201 | se_version: se_version.to_string(), 202 | flag: flag.to_vec(), 203 | mcu_version: mcu_version.to_string(), 204 | }; 205 | 206 | Ok(device_info) 207 | } 208 | 209 | /// Retrieve the app info 210 | /// 211 | /// Works only in app (TOOD: dashboard support) 212 | async fn get_app_info(transport: &E) -> Result> { 213 | let command = APDUCommand { cla: CLA_APP_INFO, ins: INS_APP_INFO, p1: 0x00, p2: 0x00, data: Vec::new() }; 214 | 215 | let response = transport.exchange(&command).await?; 216 | match response.error_code() { 217 | Ok(APDUErrorCode::NoError) => {}, 218 | Ok(err) => return Err(LedgerAppError::AppSpecific(err as _, err.description())), 219 | Err(err) => return Err(LedgerAppError::Unknown(err as _)), 220 | } 221 | 222 | let response_data = response.data(); 223 | 224 | if response_data[0] != 1 { 225 | return Err(LedgerAppError::InvalidFormatID); 226 | } 227 | 228 | let app_name_len: usize = response_data[1] as usize; 229 | let app_name_bytes = &response_data[2 .. app_name_len]; 230 | 231 | let mut idx = 2 + app_name_len; 232 | let app_version_len: usize = response_data[idx] as usize; 233 | idx += 1; 234 | let app_version_bytes = &response_data[idx .. idx + app_version_len]; 235 | 236 | idx += app_version_len; 237 | 238 | let app_flags_len = response_data[idx]; 239 | idx += 1; 240 | let flags_value = response_data[idx]; 241 | 242 | let app_name = str::from_utf8(app_name_bytes).map_err(|_e| LedgerAppError::Utf8)?; 243 | let app_version = str::from_utf8(app_version_bytes).map_err(|_e| LedgerAppError::Utf8)?; 244 | 245 | let app_info = AppInfo { 246 | app_name: app_name.to_string(), 247 | app_version: app_version.to_string(), 248 | flag_len: app_flags_len, 249 | flags_value, 250 | flag_recovery: (flags_value & 1) != 0, 251 | flag_signed_mcu_code: (flags_value & 2) != 0, 252 | flag_onboarded: (flags_value & 4) != 0, 253 | flag_pin_validated: (flags_value & 128) != 0, 254 | }; 255 | 256 | Ok(app_info) 257 | } 258 | 259 | /// Retrieve the app version 260 | async fn get_version(transport: &E) -> Result> { 261 | let command = APDUCommand { cla: Self::CLA, ins: INS_GET_VERSION, p1: 0x00, p2: 0x00, data: Vec::new() }; 262 | 263 | let response = transport.exchange(&command).await?; 264 | match response.error_code() { 265 | Ok(APDUErrorCode::NoError) => {}, 266 | Ok(err) => return Err(LedgerAppError::Unknown(err as _)), 267 | Err(err) => return Err(LedgerAppError::Unknown(err)), 268 | } 269 | 270 | let response_data = response.data(); 271 | 272 | let version = match response_data.len() { 273 | // single byte version numbers 274 | 4 => Version { 275 | mode: response_data[0], 276 | major: response_data[1] as u16, 277 | minor: response_data[2] as u16, 278 | patch: response_data[3] as u16, 279 | locked: false, 280 | target_id: [0, 0, 0, 0], 281 | }, 282 | // double byte version numbers 283 | 7 => Version { 284 | mode: response_data[0], 285 | major: response_data[1] as u16 * 256 + response_data[2] as u16, 286 | minor: response_data[3] as u16 * 256 + response_data[4] as u16, 287 | patch: response_data[5] as u16 * 256 + response_data[6] as u16, 288 | locked: false, 289 | target_id: [0, 0, 0, 0], 290 | }, 291 | // double byte version numbers + lock + target id 292 | 9 => Version { 293 | mode: response_data[0], 294 | major: response_data[1] as u16, 295 | minor: response_data[2] as u16, 296 | patch: response_data[3] as u16, 297 | locked: response_data[4] != 0, 298 | target_id: [response_data[5], response_data[6], response_data[7], response_data[8]], 299 | }, 300 | // double byte version numbers + lock + target id 301 | 12 => Version { 302 | mode: response_data[0], 303 | major: response_data[1] as u16 * 256 + response_data[2] as u16, 304 | minor: response_data[3] as u16 * 256 + response_data[4] as u16, 305 | patch: response_data[5] as u16 * 256 + response_data[6] as u16, 306 | locked: response_data[7] != 0, 307 | target_id: [response_data[8], response_data[9], response_data[10], response_data[11]], 308 | }, 309 | _ => return Err(LedgerAppError::InvalidVersion), 310 | }; 311 | Ok(version) 312 | } 313 | 314 | /// Stream a long request in chunks, with an option to set P2 for all chunks. 315 | /// This method combines the functionality of sending chunks with or without P2 set for all chunks. 316 | /// 317 | /// # Arguments 318 | /// * `transport` - The transport layer used for communication. 319 | /// * `command` - The initial APDU command. 320 | /// * `message` - The message to be sent in chunks. 321 | /// * `set_p2_for_all` - A boolean to determine if P2 should be set for all chunks. 322 | /// 323 | /// # Returns 324 | /// A result containing the final APDU answer or a ledger application error. 325 | async fn send_chunks + Send + Sync>( 326 | transport: &E, 327 | command: APDUCommand, 328 | message: &[u8], 329 | ) -> Result, LedgerAppError> { 330 | let chunks = message.chunks(USER_MESSAGE_CHUNK_SIZE); 331 | match chunks.len() { 332 | 0 => return Err(LedgerAppError::InvalidEmptyMessage), 333 | n if n > 255 => return Err(LedgerAppError::InvalidMessageSize), 334 | _ => (), 335 | } 336 | 337 | if command.p1 != ChunkPayloadType::Init as u8 { 338 | return Err(LedgerAppError::InvalidChunkPayloadType); 339 | } 340 | 341 | let mut response = transport.exchange(&command).await?; 342 | Self::handle_response_error(&response)?; 343 | 344 | // Send message chunks 345 | let last_chunk_index = chunks.len() - 1; 346 | for (packet_idx, chunk) in chunks.enumerate() { 347 | let mut p1 = ChunkPayloadType::Add as u8; 348 | if packet_idx == last_chunk_index { 349 | p1 = ChunkPayloadType::Last as u8; 350 | } 351 | 352 | let command = APDUCommand { cla: command.cla, ins: command.ins, p1, p2: command.p2, data: chunk.to_vec() }; 353 | 354 | response = transport.exchange(&command).await?; 355 | Self::handle_response_error(&response)?; 356 | } 357 | 358 | Ok(response) 359 | } 360 | } 361 | 362 | impl AppExt for T 363 | where 364 | T: App, 365 | E: Exchange + Send + Sync, 366 | E::Error: std::error::Error, 367 | { 368 | } 369 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | # rustfmt.toml example for strict formatting 2 | 3 | # General 4 | edition = "2021" # Target edition of Rust 5 | max_width = 120 # Max width of each line 6 | hard_tabs = false # Use spaces instead of tabs 7 | tab_spaces = 4 # Number of spaces per tab 8 | newline_style = "Unix" # Line ending style 9 | use_small_heuristics = "Max" # Be aggressive with formatting small items 10 | 11 | # Imports 12 | reorder_imports = true # Reorder import statements 13 | reorder_modules = true # Reorder module declarations 14 | group_imports = "StdExternalCrate" # Group imports by type 15 | 16 | # Spaces around punctuation 17 | spaces_around_ranges = true # Spaces around range operators for readability 18 | space_before_colon = false # No space before colons 19 | space_after_colon = true # Space after colons 20 | 21 | # Blank lines 22 | blank_lines_upper_bound = 1 # Max blank lines in a row 23 | blank_lines_lower_bound = 0 # Min blank lines in a row 24 | 25 | # Control flow 26 | brace_style = "SameLineWhere" # Opening brace on the same line 27 | control_brace_style = "AlwaysSameLine" # Control flow braces on the same line 28 | 29 | # Function calls and definitions 30 | fn_params_layout = "Vertical" # Function arguments layout to favor visual alignment 31 | 32 | # Structs, enums, and unions 33 | struct_variant_width = 18 # Max width of struct variants before falling back to vertical formatting, corrected to integer 34 | 35 | # Misc 36 | match_block_trailing_comma = true # Trailing comma in match blocks for easier editing 37 | chain_width = 40 # Max width for a chain of method calls before breaking to enhance readability 38 | overflow_delimited_expr = true # Allow certain expressions to overflow delimiters 39 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | ledger-node 3 | -------------------------------------------------------------------------------- /tests/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | wasm-pack build -t nodejs --no-typescript --out-dir ../../tests/ledger-node ../examples/wasm 3 | -------------------------------------------------------------------------------- /tests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ledger-test", 3 | "scripts": { 4 | "test": "mocha test.js" 5 | }, 6 | "dependencies": { 7 | "@zondax/zemu": "^0.30.0", 8 | "mocha": "^10.0.0" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tests/test.js: -------------------------------------------------------------------------------- 1 | const ledger = require('./ledger-node'); 2 | const Zemu = require('@zondax/zemu'); 3 | const path = require('path'); 4 | const assert = require('assert'); 5 | 6 | const catchExit = async () => { 7 | process.on("SIGINT", () => { 8 | Zemu.stopAllEmuContainers(function () { 9 | process.exit(); 10 | }); 11 | }); 12 | }; 13 | 14 | describe("LEDGER TEST", function () { 15 | this.timeout(50000); 16 | 17 | var sim, 18 | transport; 19 | 20 | before(async function() { 21 | // runs before tests start 22 | await catchExit(); 23 | await Zemu.default.checkAndPullImage(); 24 | await Zemu.default.stopAllEmuContainers(); 25 | 26 | sim = new Zemu.default(path.join(__dirname,'/node_modules/@zondax/zemu/bin/demoAppS.elf')); 27 | const APP_SEED = "equip will roof matter pink blind book anxiety banner elbow sun young"; 28 | const sim_options = { 29 | ...Zemu.DEFAULT_START_OPTIONS, 30 | logging: true, 31 | custom: `-s "${APP_SEED}"`, 32 | press_delay: 150, 33 | model: 'nanos', 34 | //,X11: true 35 | }; 36 | 37 | await sim.start(sim_options); 38 | 39 | transport = sim.getTransport(); 40 | }); 41 | 42 | after(async function() { 43 | // runs after all the test are done 44 | await sim.close(); 45 | // reset 46 | transport = null; 47 | }) 48 | 49 | it("#deviceInfo()", async function() { 50 | const resp = await ledger.deviceInfo(transport); 51 | 52 | console.log(resp); 53 | 54 | assert("targetId" in resp); 55 | assert("seVersion" in resp); 56 | assert("flag" in resp); 57 | assert("mcuVersion" in resp); 58 | }); 59 | }) 60 | -------------------------------------------------------------------------------- /tests/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@babel/runtime@^7.15.4": 6 | version "7.17.8" 7 | resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.8.tgz" 8 | integrity sha512-dQpEpK0O9o6lj6oPu0gRDbbnk+4LeHlNcBpspf6Olzt3GIX4P1lWF1gS+pHLDFlaJvbR6q7jCfQ08zA4QJBnmA== 9 | dependencies: 10 | regenerator-runtime "^0.13.4" 11 | 12 | "@grpc/grpc-js@^1.5.5": 13 | version "1.8.22" 14 | resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.8.22.tgz#847930c9af46e14df05b57fc12325db140ceff1d" 15 | integrity sha512-oAjDdN7fzbUi+4hZjKG96MR6KTEubAeMpQEb+77qy+3r0Ua5xTFuie6JOLr4ZZgl5g+W5/uRTS2M1V8mVAFPuA== 16 | dependencies: 17 | "@grpc/proto-loader" "^0.7.0" 18 | "@types/node" ">=12.12.47" 19 | 20 | "@grpc/proto-loader@^0.6.9": 21 | version "0.6.9" 22 | resolved "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.9.tgz" 23 | integrity sha512-UlcCS8VbsU9d3XTXGiEVFonN7hXk+oMXZtoHHG2oSA1/GcDP1q6OUgs20PzHDGizzyi8ufGSUDlk3O2NyY7leg== 24 | dependencies: 25 | "@types/long" "^4.0.1" 26 | lodash.camelcase "^4.3.0" 27 | long "^4.0.0" 28 | protobufjs "^6.10.0" 29 | yargs "^16.2.0" 30 | 31 | "@grpc/proto-loader@^0.7.0": 32 | version "0.7.13" 33 | resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.7.13.tgz#f6a44b2b7c9f7b609f5748c6eac2d420e37670cf" 34 | integrity sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw== 35 | dependencies: 36 | lodash.camelcase "^4.3.0" 37 | long "^5.0.0" 38 | protobufjs "^7.2.5" 39 | yargs "^17.7.2" 40 | 41 | "@ledgerhq/devices@^6.24.1": 42 | version "6.24.1" 43 | resolved "https://registry.npmjs.org/@ledgerhq/devices/-/devices-6.24.1.tgz" 44 | integrity sha512-6SNXWXxojUF6WKXMVIbRs15Mveg+9k0RKJK/PKlwZh929Lnr/NcbONWdwPjWKZAp1g82eEPT4jIkG6qc4QXlcA== 45 | dependencies: 46 | "@ledgerhq/errors" "^6.10.0" 47 | "@ledgerhq/logs" "^6.10.0" 48 | rxjs "6" 49 | semver "^7.3.5" 50 | 51 | "@ledgerhq/errors@^6.10.0": 52 | version "6.10.0" 53 | resolved "https://registry.npmjs.org/@ledgerhq/errors/-/errors-6.10.0.tgz" 54 | integrity sha512-fQFnl2VIXh9Yd41lGjReCeK+Q2hwxQJvLZfqHnKqWapTz68NHOv5QcI0OHuZVNEbv0xhgdLhi5b65kgYeQSUVg== 55 | 56 | "@ledgerhq/hw-transport-http@^6.24.1": 57 | version "6.27.0" 58 | resolved "https://registry.npmjs.org/@ledgerhq/hw-transport-http/-/hw-transport-http-6.27.0.tgz" 59 | integrity sha512-9ZPwo1E7YpLBMbmiRumgBE9CnwlQ7gXmg2K52Y7Lu5U/UVnQ8WoHhYr8GHm9wyC2gu/30XDaJGzgvZYqArdRHA== 60 | dependencies: 61 | "@ledgerhq/errors" "^6.10.0" 62 | "@ledgerhq/hw-transport" "^6.24.1" 63 | "@ledgerhq/logs" "^6.10.0" 64 | axios "^0.26.1" 65 | ws "8.5.0" 66 | 67 | "@ledgerhq/hw-transport@^6.24.1": 68 | version "6.24.1" 69 | resolved "https://registry.npmjs.org/@ledgerhq/hw-transport/-/hw-transport-6.24.1.tgz" 70 | integrity sha512-cOhxkQJrN7DvPFLLXAS2nqAZ7NIDaFqnbgu9ugTccgbJm2/z7ClRZX/uQoI4FscswZ47MuJQdXqz4nK48phteQ== 71 | dependencies: 72 | "@ledgerhq/devices" "^6.24.1" 73 | "@ledgerhq/errors" "^6.10.0" 74 | events "^3.3.0" 75 | 76 | "@ledgerhq/logs@^6.10.0": 77 | version "6.10.0" 78 | resolved "https://registry.npmjs.org/@ledgerhq/logs/-/logs-6.10.0.tgz" 79 | integrity sha512-lLseUPEhSFUXYTKj6q7s2O3s2vW2ebgA11vMAlKodXGf5AFw4zUoEbTz9CoFOC9jS6xY4Qr8BmRnxP/odT4Uuw== 80 | 81 | "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": 82 | version "1.1.2" 83 | resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" 84 | integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ== 85 | 86 | "@protobufjs/base64@^1.1.2": 87 | version "1.1.2" 88 | resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" 89 | integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== 90 | 91 | "@protobufjs/codegen@^2.0.4": 92 | version "2.0.4" 93 | resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" 94 | integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== 95 | 96 | "@protobufjs/eventemitter@^1.1.0": 97 | version "1.1.0" 98 | resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" 99 | integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q== 100 | 101 | "@protobufjs/fetch@^1.1.0": 102 | version "1.1.0" 103 | resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" 104 | integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ== 105 | dependencies: 106 | "@protobufjs/aspromise" "^1.1.1" 107 | "@protobufjs/inquire" "^1.1.0" 108 | 109 | "@protobufjs/float@^1.0.2": 110 | version "1.0.2" 111 | resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" 112 | integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ== 113 | 114 | "@protobufjs/inquire@^1.1.0": 115 | version "1.1.0" 116 | resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" 117 | integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q== 118 | 119 | "@protobufjs/path@^1.1.2": 120 | version "1.1.2" 121 | resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" 122 | integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA== 123 | 124 | "@protobufjs/pool@^1.1.0": 125 | version "1.1.0" 126 | resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" 127 | integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw== 128 | 129 | "@protobufjs/utf8@^1.1.0": 130 | version "1.1.0" 131 | resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" 132 | integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== 133 | 134 | "@types/long@^4.0.1": 135 | version "4.0.2" 136 | resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a" 137 | integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA== 138 | 139 | "@types/node@>=12.12.47", "@types/node@>=13.7.0": 140 | version "17.0.38" 141 | resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.38.tgz#f8bb07c371ccb1903f3752872c89f44006132947" 142 | integrity sha512-5jY9RhV7c0Z4Jy09G+NIDTsCZ5G0L5n+Z+p+Y7t5VJHM30bgwzSjVtlcBxqAj+6L/swIlvtOSzr8rBk/aNyV2g== 143 | 144 | "@ungap/promise-all-settled@1.1.2": 145 | version "1.1.2" 146 | resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" 147 | integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== 148 | 149 | "@zondax/zemu@^0.30.0": 150 | version "0.30.0" 151 | resolved "https://registry.yarnpkg.com/@zondax/zemu/-/zemu-0.30.0.tgz#56265c64742008395e7af558957e59fd517b7cdd" 152 | integrity sha512-Xl7nY6oE3y3BU5xa2Ar4NGelNvOiqvke3TSmJx1GDrVkJXPXSbXfkP4R+WupbpBxsk/XgCZtkKUtx76FERU0/Q== 153 | dependencies: 154 | "@grpc/grpc-js" "^1.5.5" 155 | "@grpc/proto-loader" "^0.6.9" 156 | "@ledgerhq/hw-transport" "^6.24.1" 157 | "@ledgerhq/hw-transport-http" "^6.24.1" 158 | axios "^0.27.2" 159 | axios-retry "^3.2.0" 160 | dockerode "^3.3.1" 161 | elfy "^1.0.0" 162 | fs-extra "^10.0.0" 163 | get-port "^5.1.1" 164 | path "^0.12.7" 165 | pngjs "^6.0.0" 166 | randomstring "^1.2.1" 167 | sleep "^6.3.0" 168 | 169 | ansi-colors@4.1.1: 170 | version "4.1.1" 171 | resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz" 172 | integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== 173 | 174 | ansi-regex@^5.0.1: 175 | version "5.0.1" 176 | resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" 177 | integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== 178 | 179 | ansi-styles@^4.0.0, ansi-styles@^4.1.0: 180 | version "4.3.0" 181 | resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" 182 | integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== 183 | dependencies: 184 | color-convert "^2.0.1" 185 | 186 | anymatch@~3.1.2: 187 | version "3.1.2" 188 | resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" 189 | integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== 190 | dependencies: 191 | normalize-path "^3.0.0" 192 | picomatch "^2.0.4" 193 | 194 | argparse@^2.0.1: 195 | version "2.0.1" 196 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" 197 | integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== 198 | 199 | array-uniq@1.0.2: 200 | version "1.0.2" 201 | resolved "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.2.tgz" 202 | integrity sha1-X8w3OSB3VyPP1k1lxkvvU7+eum0= 203 | 204 | asn1@^0.2.4: 205 | version "0.2.6" 206 | resolved "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz" 207 | integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== 208 | dependencies: 209 | safer-buffer "~2.1.0" 210 | 211 | asynckit@^0.4.0: 212 | version "0.4.0" 213 | resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" 214 | integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== 215 | 216 | axios-retry@^3.2.0: 217 | version "3.2.4" 218 | resolved "https://registry.npmjs.org/axios-retry/-/axios-retry-3.2.4.tgz" 219 | integrity sha512-Co3UXiv4npi6lM963mfnuH90/YFLKWWDmoBYfxkHT5xtkSSWNqK9zdG3fw5/CP/dsoKB5aMMJCsgab+tp1OxLQ== 220 | dependencies: 221 | "@babel/runtime" "^7.15.4" 222 | is-retry-allowed "^2.2.0" 223 | 224 | axios@^0.26.1: 225 | version "0.26.1" 226 | resolved "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz" 227 | integrity sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA== 228 | dependencies: 229 | follow-redirects "^1.14.8" 230 | 231 | axios@^0.27.2: 232 | version "0.27.2" 233 | resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" 234 | integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== 235 | dependencies: 236 | follow-redirects "^1.14.9" 237 | form-data "^4.0.0" 238 | 239 | balanced-match@^1.0.0: 240 | version "1.0.0" 241 | resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz" 242 | integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= 243 | 244 | base64-js@^1.0.2: 245 | version "1.3.1" 246 | resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz" 247 | integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== 248 | 249 | bcrypt-pbkdf@^1.0.2: 250 | version "1.0.2" 251 | resolved "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz" 252 | integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= 253 | dependencies: 254 | tweetnacl "^0.14.3" 255 | 256 | binary-extensions@^2.0.0: 257 | version "2.0.0" 258 | resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz" 259 | integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow== 260 | 261 | bl@^4.0.1: 262 | version "4.0.3" 263 | resolved "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz" 264 | integrity sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg== 265 | dependencies: 266 | buffer "^5.5.0" 267 | inherits "^2.0.4" 268 | readable-stream "^3.4.0" 269 | 270 | brace-expansion@^1.1.7: 271 | version "1.1.11" 272 | resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" 273 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 274 | dependencies: 275 | balanced-match "^1.0.0" 276 | concat-map "0.0.1" 277 | 278 | brace-expansion@^2.0.1: 279 | version "2.0.1" 280 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" 281 | integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== 282 | dependencies: 283 | balanced-match "^1.0.0" 284 | 285 | braces@~3.0.2: 286 | version "3.0.3" 287 | resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" 288 | integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== 289 | dependencies: 290 | fill-range "^7.1.1" 291 | 292 | browser-stdout@1.3.1: 293 | version "1.3.1" 294 | resolved "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz" 295 | integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== 296 | 297 | buffer@^5.5.0: 298 | version "5.6.0" 299 | resolved "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz" 300 | integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw== 301 | dependencies: 302 | base64-js "^1.0.2" 303 | ieee754 "^1.1.4" 304 | 305 | buildcheck@0.0.3: 306 | version "0.0.3" 307 | resolved "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.3.tgz" 308 | integrity sha512-pziaA+p/wdVImfcbsZLNF32EiWyujlQLwolMqUQE8xpKNOH7KmZQaY8sXN7DGOEzPAElo9QTaeNRfGnf3iOJbA== 309 | 310 | camelcase@^6.0.0: 311 | version "6.3.0" 312 | resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" 313 | integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== 314 | 315 | chalk@^4.1.0: 316 | version "4.1.2" 317 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" 318 | integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== 319 | dependencies: 320 | ansi-styles "^4.1.0" 321 | supports-color "^7.1.0" 322 | 323 | chokidar@3.5.3: 324 | version "3.5.3" 325 | resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" 326 | integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== 327 | dependencies: 328 | anymatch "~3.1.2" 329 | braces "~3.0.2" 330 | glob-parent "~5.1.2" 331 | is-binary-path "~2.1.0" 332 | is-glob "~4.0.1" 333 | normalize-path "~3.0.0" 334 | readdirp "~3.6.0" 335 | optionalDependencies: 336 | fsevents "~2.3.2" 337 | 338 | chownr@^1.1.1: 339 | version "1.1.4" 340 | resolved "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz" 341 | integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== 342 | 343 | cliui@^7.0.2: 344 | version "7.0.4" 345 | resolved "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz" 346 | integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== 347 | dependencies: 348 | string-width "^4.2.0" 349 | strip-ansi "^6.0.0" 350 | wrap-ansi "^7.0.0" 351 | 352 | cliui@^8.0.1: 353 | version "8.0.1" 354 | resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" 355 | integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== 356 | dependencies: 357 | string-width "^4.2.0" 358 | strip-ansi "^6.0.1" 359 | wrap-ansi "^7.0.0" 360 | 361 | color-convert@^2.0.1: 362 | version "2.0.1" 363 | resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" 364 | integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== 365 | dependencies: 366 | color-name "~1.1.4" 367 | 368 | color-name@~1.1.4: 369 | version "1.1.4" 370 | resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" 371 | integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== 372 | 373 | combined-stream@^1.0.8: 374 | version "1.0.8" 375 | resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" 376 | integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== 377 | dependencies: 378 | delayed-stream "~1.0.0" 379 | 380 | concat-map@0.0.1: 381 | version "0.0.1" 382 | resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" 383 | integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= 384 | 385 | cpu-features@0.0.3: 386 | version "0.0.3" 387 | resolved "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.3.tgz" 388 | integrity sha512-p6C/uud4F4bDyCz9+BNU22KdV1AGxPK6L9rQG9x3x4SSzdMPyBPErP7Rxn8avT2ex1M2g5Rpjz5Us/ri/766Qg== 389 | dependencies: 390 | buildcheck "0.0.3" 391 | nan "^2.15.0" 392 | 393 | debug@4.3.4, debug@^4.1.1: 394 | version "4.3.4" 395 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" 396 | integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== 397 | dependencies: 398 | ms "2.1.2" 399 | 400 | decamelize@^4.0.0: 401 | version "4.0.0" 402 | resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" 403 | integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== 404 | 405 | delayed-stream@~1.0.0: 406 | version "1.0.0" 407 | resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" 408 | integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== 409 | 410 | diff@5.0.0: 411 | version "5.0.0" 412 | resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" 413 | integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== 414 | 415 | docker-modem@^3.0.0: 416 | version "3.0.3" 417 | resolved "https://registry.npmjs.org/docker-modem/-/docker-modem-3.0.3.tgz" 418 | integrity sha512-Tgkn2a+yiNP9FoZgMa/D9Wk+D2Db///0KOyKSYZRJa8w4+DzKyzQMkczKSdR/adQ0x46BOpeNkoyEOKjPhCzjw== 419 | dependencies: 420 | debug "^4.1.1" 421 | readable-stream "^3.5.0" 422 | split-ca "^1.0.1" 423 | ssh2 "^1.4.0" 424 | 425 | dockerode@^3.3.1: 426 | version "3.3.1" 427 | resolved "https://registry.npmjs.org/dockerode/-/dockerode-3.3.1.tgz" 428 | integrity sha512-AS2mr8Lp122aa5n6d99HkuTNdRV1wkkhHwBdcnY6V0+28D3DSYwhxAk85/mM9XwD3RMliTxyr63iuvn5ZblFYQ== 429 | dependencies: 430 | docker-modem "^3.0.0" 431 | tar-fs "~2.0.1" 432 | 433 | elfy@^1.0.0: 434 | version "1.0.0" 435 | resolved "https://registry.npmjs.org/elfy/-/elfy-1.0.0.tgz" 436 | integrity sha512-4Kp3AA94jC085IJox+qnvrZ3PudqTi4gQNvIoTZfJJ9IqkRuCoqP60vCVYlIg00c5aYusi5Wjh2bf0cHYt+6gQ== 437 | dependencies: 438 | endian-reader "^0.3.0" 439 | 440 | emoji-regex@^8.0.0: 441 | version "8.0.0" 442 | resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" 443 | integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== 444 | 445 | end-of-stream@^1.1.0, end-of-stream@^1.4.1: 446 | version "1.4.4" 447 | resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz" 448 | integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== 449 | dependencies: 450 | once "^1.4.0" 451 | 452 | endian-reader@^0.3.0: 453 | version "0.3.0" 454 | resolved "https://registry.npmjs.org/endian-reader/-/endian-reader-0.3.0.tgz" 455 | integrity sha1-hOykNrgK7Q0GOcRykTOLky7+UKA= 456 | 457 | escalade@^3.1.1: 458 | version "3.1.1" 459 | resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" 460 | integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== 461 | 462 | escape-string-regexp@4.0.0: 463 | version "4.0.0" 464 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" 465 | integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== 466 | 467 | events@^3.3.0: 468 | version "3.3.0" 469 | resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz" 470 | integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== 471 | 472 | fill-range@^7.1.1: 473 | version "7.1.1" 474 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" 475 | integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== 476 | dependencies: 477 | to-regex-range "^5.0.1" 478 | 479 | find-up@5.0.0: 480 | version "5.0.0" 481 | resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" 482 | integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== 483 | dependencies: 484 | locate-path "^6.0.0" 485 | path-exists "^4.0.0" 486 | 487 | flat@^5.0.2: 488 | version "5.0.2" 489 | resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" 490 | integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== 491 | 492 | follow-redirects@^1.14.8, follow-redirects@^1.14.9: 493 | version "1.15.6" 494 | resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" 495 | integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== 496 | 497 | form-data@^4.0.0: 498 | version "4.0.0" 499 | resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" 500 | integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== 501 | dependencies: 502 | asynckit "^0.4.0" 503 | combined-stream "^1.0.8" 504 | mime-types "^2.1.12" 505 | 506 | fs-constants@^1.0.0: 507 | version "1.0.0" 508 | resolved "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz" 509 | integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== 510 | 511 | fs-extra@^10.0.0: 512 | version "10.0.1" 513 | resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz" 514 | integrity sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag== 515 | dependencies: 516 | graceful-fs "^4.2.0" 517 | jsonfile "^6.0.1" 518 | universalify "^2.0.0" 519 | 520 | fs.realpath@^1.0.0: 521 | version "1.0.0" 522 | resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" 523 | integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= 524 | 525 | fsevents@~2.3.2: 526 | version "2.3.2" 527 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" 528 | integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== 529 | 530 | get-caller-file@^2.0.5: 531 | version "2.0.5" 532 | resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" 533 | integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== 534 | 535 | get-port@^5.1.1: 536 | version "5.1.1" 537 | resolved "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz" 538 | integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== 539 | 540 | glob-parent@~5.1.2: 541 | version "5.1.2" 542 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" 543 | integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== 544 | dependencies: 545 | is-glob "^4.0.1" 546 | 547 | glob@7.2.0: 548 | version "7.2.0" 549 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" 550 | integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== 551 | dependencies: 552 | fs.realpath "^1.0.0" 553 | inflight "^1.0.4" 554 | inherits "2" 555 | minimatch "^3.0.4" 556 | once "^1.3.0" 557 | path-is-absolute "^1.0.0" 558 | 559 | graceful-fs@^4.1.6, graceful-fs@^4.2.0: 560 | version "4.2.9" 561 | resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz" 562 | integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== 563 | 564 | has-flag@^4.0.0: 565 | version "4.0.0" 566 | resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" 567 | integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== 568 | 569 | he@1.2.0: 570 | version "1.2.0" 571 | resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz" 572 | integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== 573 | 574 | ieee754@^1.1.4: 575 | version "1.1.13" 576 | resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz" 577 | integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== 578 | 579 | inflight@^1.0.4: 580 | version "1.0.6" 581 | resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" 582 | integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= 583 | dependencies: 584 | once "^1.3.0" 585 | wrappy "1" 586 | 587 | inherits@2, inherits@^2.0.3, inherits@^2.0.4: 588 | version "2.0.4" 589 | resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" 590 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 591 | 592 | inherits@2.0.3: 593 | version "2.0.3" 594 | resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" 595 | integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= 596 | 597 | is-binary-path@~2.1.0: 598 | version "2.1.0" 599 | resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" 600 | integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== 601 | dependencies: 602 | binary-extensions "^2.0.0" 603 | 604 | is-extglob@^2.1.1: 605 | version "2.1.1" 606 | resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" 607 | integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= 608 | 609 | is-fullwidth-code-point@^3.0.0: 610 | version "3.0.0" 611 | resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" 612 | integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== 613 | 614 | is-glob@^4.0.1, is-glob@~4.0.1: 615 | version "4.0.1" 616 | resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz" 617 | integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== 618 | dependencies: 619 | is-extglob "^2.1.1" 620 | 621 | is-number@^7.0.0: 622 | version "7.0.0" 623 | resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" 624 | integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== 625 | 626 | is-plain-obj@^2.1.0: 627 | version "2.1.0" 628 | resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" 629 | integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== 630 | 631 | is-retry-allowed@^2.2.0: 632 | version "2.2.0" 633 | resolved "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz" 634 | integrity sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg== 635 | 636 | is-unicode-supported@^0.1.0: 637 | version "0.1.0" 638 | resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" 639 | integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== 640 | 641 | js-yaml@4.1.0: 642 | version "4.1.0" 643 | resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" 644 | integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== 645 | dependencies: 646 | argparse "^2.0.1" 647 | 648 | jsonfile@^6.0.1: 649 | version "6.1.0" 650 | resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz" 651 | integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== 652 | dependencies: 653 | universalify "^2.0.0" 654 | optionalDependencies: 655 | graceful-fs "^4.1.6" 656 | 657 | locate-path@^6.0.0: 658 | version "6.0.0" 659 | resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" 660 | integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== 661 | dependencies: 662 | p-locate "^5.0.0" 663 | 664 | lodash.camelcase@^4.3.0: 665 | version "4.3.0" 666 | resolved "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz" 667 | integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= 668 | 669 | log-symbols@4.1.0: 670 | version "4.1.0" 671 | resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" 672 | integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== 673 | dependencies: 674 | chalk "^4.1.0" 675 | is-unicode-supported "^0.1.0" 676 | 677 | long@^4.0.0: 678 | version "4.0.0" 679 | resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" 680 | integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== 681 | 682 | long@^5.0.0: 683 | version "5.2.3" 684 | resolved "https://registry.yarnpkg.com/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1" 685 | integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q== 686 | 687 | mime-db@1.52.0: 688 | version "1.52.0" 689 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" 690 | integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== 691 | 692 | mime-types@^2.1.12: 693 | version "2.1.35" 694 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" 695 | integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== 696 | dependencies: 697 | mime-db "1.52.0" 698 | 699 | minimatch@5.0.1: 700 | version "5.0.1" 701 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" 702 | integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== 703 | dependencies: 704 | brace-expansion "^2.0.1" 705 | 706 | minimatch@^3.0.4: 707 | version "3.1.2" 708 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" 709 | integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== 710 | dependencies: 711 | brace-expansion "^1.1.7" 712 | 713 | mkdirp-classic@^0.5.2: 714 | version "0.5.3" 715 | resolved "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz" 716 | integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== 717 | 718 | mocha@^10.0.0: 719 | version "10.0.0" 720 | resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.0.0.tgz#205447d8993ec755335c4b13deba3d3a13c4def9" 721 | integrity sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA== 722 | dependencies: 723 | "@ungap/promise-all-settled" "1.1.2" 724 | ansi-colors "4.1.1" 725 | browser-stdout "1.3.1" 726 | chokidar "3.5.3" 727 | debug "4.3.4" 728 | diff "5.0.0" 729 | escape-string-regexp "4.0.0" 730 | find-up "5.0.0" 731 | glob "7.2.0" 732 | he "1.2.0" 733 | js-yaml "4.1.0" 734 | log-symbols "4.1.0" 735 | minimatch "5.0.1" 736 | ms "2.1.3" 737 | nanoid "3.3.3" 738 | serialize-javascript "6.0.0" 739 | strip-json-comments "3.1.1" 740 | supports-color "8.1.1" 741 | workerpool "6.2.1" 742 | yargs "16.2.0" 743 | yargs-parser "20.2.4" 744 | yargs-unparser "2.0.0" 745 | 746 | ms@2.1.2: 747 | version "2.1.2" 748 | resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" 749 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 750 | 751 | ms@2.1.3: 752 | version "2.1.3" 753 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" 754 | integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== 755 | 756 | nan@^2.14.1, nan@^2.15.0: 757 | version "2.15.0" 758 | resolved "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz" 759 | integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ== 760 | 761 | nanoid@3.3.3: 762 | version "3.3.3" 763 | resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" 764 | integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== 765 | 766 | normalize-path@^3.0.0, normalize-path@~3.0.0: 767 | version "3.0.0" 768 | resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" 769 | integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== 770 | 771 | once@^1.3.0, once@^1.3.1, once@^1.4.0: 772 | version "1.4.0" 773 | resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" 774 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= 775 | dependencies: 776 | wrappy "1" 777 | 778 | p-limit@^3.0.2: 779 | version "3.1.0" 780 | resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" 781 | integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== 782 | dependencies: 783 | yocto-queue "^0.1.0" 784 | 785 | p-locate@^5.0.0: 786 | version "5.0.0" 787 | resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" 788 | integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== 789 | dependencies: 790 | p-limit "^3.0.2" 791 | 792 | path-exists@^4.0.0: 793 | version "4.0.0" 794 | resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" 795 | integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== 796 | 797 | path-is-absolute@^1.0.0: 798 | version "1.0.1" 799 | resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" 800 | integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= 801 | 802 | path@^0.12.7: 803 | version "0.12.7" 804 | resolved "https://registry.npmjs.org/path/-/path-0.12.7.tgz" 805 | integrity sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8= 806 | dependencies: 807 | process "^0.11.1" 808 | util "^0.10.3" 809 | 810 | picomatch@^2.0.4: 811 | version "2.2.2" 812 | resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz" 813 | integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== 814 | 815 | picomatch@^2.2.1: 816 | version "2.3.1" 817 | resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" 818 | integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== 819 | 820 | pngjs@^6.0.0: 821 | version "6.0.0" 822 | resolved "https://registry.npmjs.org/pngjs/-/pngjs-6.0.0.tgz" 823 | integrity sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg== 824 | 825 | process@^0.11.1: 826 | version "0.11.10" 827 | resolved "https://registry.npmjs.org/process/-/process-0.11.10.tgz" 828 | integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= 829 | 830 | protobufjs@^6.10.0: 831 | version "6.11.4" 832 | resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.4.tgz#29a412c38bf70d89e537b6d02d904a6f448173aa" 833 | integrity sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw== 834 | dependencies: 835 | "@protobufjs/aspromise" "^1.1.2" 836 | "@protobufjs/base64" "^1.1.2" 837 | "@protobufjs/codegen" "^2.0.4" 838 | "@protobufjs/eventemitter" "^1.1.0" 839 | "@protobufjs/fetch" "^1.1.0" 840 | "@protobufjs/float" "^1.0.2" 841 | "@protobufjs/inquire" "^1.1.0" 842 | "@protobufjs/path" "^1.1.2" 843 | "@protobufjs/pool" "^1.1.0" 844 | "@protobufjs/utf8" "^1.1.0" 845 | "@types/long" "^4.0.1" 846 | "@types/node" ">=13.7.0" 847 | long "^4.0.0" 848 | 849 | protobufjs@^7.2.5: 850 | version "7.3.0" 851 | resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.3.0.tgz#a32ec0422c039798c41a0700306a6e305b9cb32c" 852 | integrity sha512-YWD03n3shzV9ImZRX3ccbjqLxj7NokGN0V/ESiBV5xWqrommYHYiihuIyavq03pWSGqlyvYUFmfoMKd+1rPA/g== 853 | dependencies: 854 | "@protobufjs/aspromise" "^1.1.2" 855 | "@protobufjs/base64" "^1.1.2" 856 | "@protobufjs/codegen" "^2.0.4" 857 | "@protobufjs/eventemitter" "^1.1.0" 858 | "@protobufjs/fetch" "^1.1.0" 859 | "@protobufjs/float" "^1.0.2" 860 | "@protobufjs/inquire" "^1.1.0" 861 | "@protobufjs/path" "^1.1.2" 862 | "@protobufjs/pool" "^1.1.0" 863 | "@protobufjs/utf8" "^1.1.0" 864 | "@types/node" ">=13.7.0" 865 | long "^5.0.0" 866 | 867 | pump@^3.0.0: 868 | version "3.0.0" 869 | resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz" 870 | integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== 871 | dependencies: 872 | end-of-stream "^1.1.0" 873 | once "^1.3.1" 874 | 875 | randombytes@2.0.3: 876 | version "2.0.3" 877 | resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.0.3.tgz" 878 | integrity sha1-Z0yZdgkBw8QRJ3GjHlIdw0nMCew= 879 | 880 | randombytes@^2.1.0: 881 | version "2.1.0" 882 | resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" 883 | integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== 884 | dependencies: 885 | safe-buffer "^5.1.0" 886 | 887 | randomstring@^1.2.1: 888 | version "1.2.2" 889 | resolved "https://registry.npmjs.org/randomstring/-/randomstring-1.2.2.tgz" 890 | integrity sha512-9FByiB8guWZLbE+akdQiWE3I1I6w7Vn5El4o4y7o5bWQ6DWPcEOp+aLG7Jezc8BVRKKpgJd2ppRX0jnKu1YCfg== 891 | dependencies: 892 | array-uniq "1.0.2" 893 | randombytes "2.0.3" 894 | 895 | readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.5.0: 896 | version "3.6.0" 897 | resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz" 898 | integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== 899 | dependencies: 900 | inherits "^2.0.3" 901 | string_decoder "^1.1.1" 902 | util-deprecate "^1.0.1" 903 | 904 | readdirp@~3.6.0: 905 | version "3.6.0" 906 | resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" 907 | integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== 908 | dependencies: 909 | picomatch "^2.2.1" 910 | 911 | regenerator-runtime@^0.13.4: 912 | version "0.13.9" 913 | resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz" 914 | integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== 915 | 916 | require-directory@^2.1.1: 917 | version "2.1.1" 918 | resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" 919 | integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= 920 | 921 | rxjs@6: 922 | version "6.6.7" 923 | resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz" 924 | integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== 925 | dependencies: 926 | tslib "^1.9.0" 927 | 928 | safe-buffer@^5.1.0, safe-buffer@~5.2.0: 929 | version "5.2.1" 930 | resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" 931 | integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== 932 | 933 | safer-buffer@~2.1.0: 934 | version "2.1.2" 935 | resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" 936 | integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== 937 | 938 | semver@^7.3.5: 939 | version "7.6.1" 940 | resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.1.tgz#60bfe090bf907a25aa8119a72b9f90ef7ca281b2" 941 | integrity sha512-f/vbBsu+fOiYt+lmwZV0rVwJScl46HppnOA1ZvIuBWKOTlllpyJ3bfVax76/OrhCH38dyxoDIA8K7uB963IYgA== 942 | 943 | serialize-javascript@6.0.0: 944 | version "6.0.0" 945 | resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" 946 | integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== 947 | dependencies: 948 | randombytes "^2.1.0" 949 | 950 | sleep@^6.3.0: 951 | version "6.3.0" 952 | resolved "https://registry.npmjs.org/sleep/-/sleep-6.3.0.tgz" 953 | integrity sha512-+WgYl951qdUlb1iS97UvQ01pkauoBK9ML9I/CMPg41v0Ze4EyMlTgFTDDo32iYj98IYqxIjDMRd+L71lawFfpQ== 954 | dependencies: 955 | nan "^2.14.1" 956 | 957 | split-ca@^1.0.1: 958 | version "1.0.1" 959 | resolved "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz" 960 | integrity sha1-bIOv82kvphJW4M0ZfgXp3hV2kaY= 961 | 962 | ssh2@^1.4.0: 963 | version "1.8.0" 964 | resolved "https://registry.npmjs.org/ssh2/-/ssh2-1.8.0.tgz" 965 | integrity sha512-NVIRkIwJvWl+mcRozp+EBzHMVCcbDKBea64ToPdZEk43yAVGwmfqYZRPFRnnvGjsKC34wYCmiupTcKgCVNVNNg== 966 | dependencies: 967 | asn1 "^0.2.4" 968 | bcrypt-pbkdf "^1.0.2" 969 | optionalDependencies: 970 | cpu-features "0.0.3" 971 | nan "^2.15.0" 972 | 973 | string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: 974 | version "4.2.3" 975 | resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" 976 | integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== 977 | dependencies: 978 | emoji-regex "^8.0.0" 979 | is-fullwidth-code-point "^3.0.0" 980 | strip-ansi "^6.0.1" 981 | 982 | string_decoder@^1.1.1: 983 | version "1.3.0" 984 | resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" 985 | integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== 986 | dependencies: 987 | safe-buffer "~5.2.0" 988 | 989 | strip-ansi@^6.0.0, strip-ansi@^6.0.1: 990 | version "6.0.1" 991 | resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" 992 | integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== 993 | dependencies: 994 | ansi-regex "^5.0.1" 995 | 996 | strip-json-comments@3.1.1: 997 | version "3.1.1" 998 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" 999 | integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== 1000 | 1001 | supports-color@8.1.1: 1002 | version "8.1.1" 1003 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" 1004 | integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== 1005 | dependencies: 1006 | has-flag "^4.0.0" 1007 | 1008 | supports-color@^7.1.0: 1009 | version "7.2.0" 1010 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" 1011 | integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== 1012 | dependencies: 1013 | has-flag "^4.0.0" 1014 | 1015 | tar-fs@~2.0.1: 1016 | version "2.0.1" 1017 | resolved "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz" 1018 | integrity sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA== 1019 | dependencies: 1020 | chownr "^1.1.1" 1021 | mkdirp-classic "^0.5.2" 1022 | pump "^3.0.0" 1023 | tar-stream "^2.0.0" 1024 | 1025 | tar-stream@^2.0.0: 1026 | version "2.1.2" 1027 | resolved "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.2.tgz" 1028 | integrity sha512-UaF6FoJ32WqALZGOIAApXx+OdxhekNMChu6axLJR85zMMjXKWFGjbIRe+J6P4UnRGg9rAwWvbTT0oI7hD/Un7Q== 1029 | dependencies: 1030 | bl "^4.0.1" 1031 | end-of-stream "^1.4.1" 1032 | fs-constants "^1.0.0" 1033 | inherits "^2.0.3" 1034 | readable-stream "^3.1.1" 1035 | 1036 | to-regex-range@^5.0.1: 1037 | version "5.0.1" 1038 | resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" 1039 | integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== 1040 | dependencies: 1041 | is-number "^7.0.0" 1042 | 1043 | tslib@^1.9.0: 1044 | version "1.14.1" 1045 | resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" 1046 | integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== 1047 | 1048 | tweetnacl@^0.14.3: 1049 | version "0.14.5" 1050 | resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" 1051 | integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= 1052 | 1053 | universalify@^2.0.0: 1054 | version "2.0.0" 1055 | resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz" 1056 | integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== 1057 | 1058 | util-deprecate@^1.0.1: 1059 | version "1.0.2" 1060 | resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" 1061 | integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= 1062 | 1063 | util@^0.10.3: 1064 | version "0.10.4" 1065 | resolved "https://registry.npmjs.org/util/-/util-0.10.4.tgz" 1066 | integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A== 1067 | dependencies: 1068 | inherits "2.0.3" 1069 | 1070 | workerpool@6.2.1: 1071 | version "6.2.1" 1072 | resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" 1073 | integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== 1074 | 1075 | wrap-ansi@^7.0.0: 1076 | version "7.0.0" 1077 | resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" 1078 | integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== 1079 | dependencies: 1080 | ansi-styles "^4.0.0" 1081 | string-width "^4.1.0" 1082 | strip-ansi "^6.0.0" 1083 | 1084 | wrappy@1: 1085 | version "1.0.2" 1086 | resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" 1087 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= 1088 | 1089 | ws@8.5.0: 1090 | version "8.5.0" 1091 | resolved "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz" 1092 | integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg== 1093 | 1094 | y18n@^5.0.5: 1095 | version "5.0.8" 1096 | resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" 1097 | integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== 1098 | 1099 | yargs-parser@20.2.4: 1100 | version "20.2.4" 1101 | resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" 1102 | integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== 1103 | 1104 | yargs-parser@^20.2.2: 1105 | version "20.2.9" 1106 | resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz" 1107 | integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== 1108 | 1109 | yargs-parser@^21.1.1: 1110 | version "21.1.1" 1111 | resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" 1112 | integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== 1113 | 1114 | yargs-unparser@2.0.0: 1115 | version "2.0.0" 1116 | resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" 1117 | integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== 1118 | dependencies: 1119 | camelcase "^6.0.0" 1120 | decamelize "^4.0.0" 1121 | flat "^5.0.2" 1122 | is-plain-obj "^2.1.0" 1123 | 1124 | yargs@16.2.0, yargs@^16.2.0: 1125 | version "16.2.0" 1126 | resolved "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz" 1127 | integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== 1128 | dependencies: 1129 | cliui "^7.0.2" 1130 | escalade "^3.1.1" 1131 | get-caller-file "^2.0.5" 1132 | require-directory "^2.1.1" 1133 | string-width "^4.2.0" 1134 | y18n "^5.0.5" 1135 | yargs-parser "^20.2.2" 1136 | 1137 | yargs@^17.7.2: 1138 | version "17.7.2" 1139 | resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" 1140 | integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== 1141 | dependencies: 1142 | cliui "^8.0.1" 1143 | escalade "^3.1.1" 1144 | get-caller-file "^2.0.5" 1145 | require-directory "^2.1.1" 1146 | string-width "^4.2.3" 1147 | y18n "^5.0.5" 1148 | yargs-parser "^21.1.1" 1149 | 1150 | yocto-queue@^0.1.0: 1151 | version "0.1.0" 1152 | resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" 1153 | integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== 1154 | --------------------------------------------------------------------------------