From 8b354505e760bb0271e9cb529f765efe895a70ec Mon Sep 17 00:00:00 2001 From: yeastplume Date: Wed, 26 Jun 2019 15:25:30 +0100 Subject: [PATCH 01/15] add wallet lifecycle --- grinrfc-0000-full-wallet-lifecycle.md | 173 ++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 grinrfc-0000-full-wallet-lifecycle.md diff --git a/grinrfc-0000-full-wallet-lifecycle.md b/grinrfc-0000-full-wallet-lifecycle.md new file mode 100644 index 0000000..d8c5bf4 --- /dev/null +++ b/grinrfc-0000-full-wallet-lifecycle.md @@ -0,0 +1,173 @@ +# GRIN-RFC-0000 Full Wallet Lifecycle Support in Wallet API + +``` +- Number: GRIN-RFC-0000 +- Title: Full Wallet Lifecycle Support in Wallet API +- Status: Draft +- Authors: yeastplume (yeastplume@protonmail.com) +- Created : June 26th, 2019 +``` + +# Summary +[summary]: #summary + +Increase the scope of the Grin Wallet's Owner API to support all wallet lifecycle functions. Wallet creation, seed management, (TBD) + +# Motivation +[motivation]: #motivation + +Grin Wallet's APIs currently provides functions for transacting and querying the contents of the wallet. However, several pieces of functionality +around wallet creation and seed/password management are not included within the API. This means that any consumers of the API will expect their users +to initialize the wallet manually before the APIs can be used. + +The Wallet APIs are intended to be the foundation upon which community-created wallets should be built, and the job of a wallet creator is made far +more difficult by the absence of wallet creation and seed management functions within the API. Ideally, it should be the case that a wallet can +be instantiated and managed solely via the Owner API. + +# Community-level explanation +[community-level-explanation]: #community-level-explanation + +Explain the proposal as if it were already included in the Grin ecosystem and you were teaching it to another Grin community member. That generally means: + +- Introducing new named concepts. +- Explaining the feature largely in terms of examples. +- Explaining how Grin community members should *think* about the feature, and how it should impact the way they use Grin. It should explain the impact as concretely as possible. +- If applicable, provide sample error messages, deprecation warnings, or migration guidance. +- If applicable, describe the differences between teaching this to existing Grin community members and new Grin community members. + +For implementation-oriented RFCs (e.g. for wallet), this section should focus on how wallet contributors should think about the change, and give examples of its concrete impact. For policy RFCs, this section should provide an example-driven introduction to the policy, and explain its impact in concrete terms. + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +## Wallet Backend Structure and Multiple Wallets + +The Wallet API should be able to support multiple named Wallets. Currently, the default wallet data directory assumes a single wallet instance, and +is structured as follows: + +``` +~/.grin/main/ + grin-wallet.log + grin-wallet.toml + wallet_data/ + wallet.seed + db/ + saved_txs/ +``` + +`wallet_data` contains the seed and database for a single instance of the wallet, with the path to the wallet's data directory configured in +`grin_wallet.toml`. + +The structure of the wallet's data files will be changed to allow multiple wallet instances, with each named wallet contained in a directory +named according to name of the wallet provided by the end user. Current single-instance wallet data will be placed into a directory called +`default`, and the API and all wallet software will assume the default directory is to be used if no name is provided. + +The wallet data directory structure will become (for example): + +``` +~/.grin/main/ + grin-wallet.log + grin-wallet.toml + wallet_data/ + default/ + wallet.seed + db/ + saved_txs/ + my_wallet_1/ + wallet.seed + ... + my_wallet_2/ + wallet.seed + ... +``` + +If a data directory is not provided in a wallet API call, it will be assumed to be operating on the `default` wallet + +### Data migration + +The target version of the wallet will contain a function to migrate existing wallets from the current data structure to the new, +essentially moving `wallet_data/wallet.seed`, `wallet_data/db/` and `wallet_data/saved_txs/` into `wallet_data/default/wallet.seed` etc. +(Should this be performed within the API for consistency?) + +## Wallet Initialization + +Currently, wallet data does not exist until the user runs `grin-wallet init`. The `init` command creates `grin-wallet.toml`, +in the `~/.grin/main` directory (or `~/.grin/floonet`, or the current directory via the `-h` flag), prompts the user for a password, +creates a seed file, stores the resulting data files in the directory specified in `grin-wallet.toml` (`~/.grin/main/wallet_data` by default) +and initialises the lmdb database. + +OwnerAPI::create_config(Option) -> Outputs a `grin-wallet.toml` file into the given location, defaulting to `~/.grin/main/wallet_data` +OwnerAPI::create_wallet(Option, password) -> Creates and initializes a new wallet in the directory specified by `name`, `default` if None +OwnerAPI::get_mnemonic(name) -> Returns mnemonic from given wallet + +It should be possible to run `grin-wallet owner_api` or invoke the API directly from a linked binary without having instantiated a wallet. + + +(How is grin-wallet.toml generated?) + +## Additional API Functions + +Owner::SetWalletDirectory -> Set the top-level system wallet directory (`~/.grin/main/wallet_data` by default) +Owner::ListWallets -> list wallet directories + +# Drawbacks +[drawbacks]: #drawbacks + +Why should we *not* do this? + +# Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + +- Why is this design the best in the space of possible designs? +- What other designs have been considered and what is the rationale for not choosing them? +- What is the impact of not doing this? + +# Prior art +[prior-art]: #prior-art + +Discuss prior art, both the good and the bad, in relation to this proposal. +A few examples of what this can include are: + +- For core, node, wallet and infrastructure proposals: Does this feature exist in other projects and what experience have their community had? +- For community, ecosystem and moderation proposals: Is this done by some other community and what were their experiences with it? +- For other teams: What lessons can we learn from what other communities have done here? +- Papers: Are there any published papers or great posts that discuss this? If you have some relevant papers to refer to, this can serve as a more detailed theoretical background. + +This section is intended to encourage you as an author to think about the lessons from other languages, provide readers of your RFC with a fuller picture. +If there is no prior art, that is fine - your ideas are interesting to us whether they are brand new or if it is an adaptation from other projects. + +Note that while precedent set by other projects is some motivation, it does not on its own motivate an RFC. +Please also take into consideration that Grin sometimes intentionally diverges from common project features. + +# Unresolved questions +[unresolved-questions]: #unresolved-questions + +- What parts of the design do you expect to resolve through the RFC process before this gets merged? +- What parts of the design do you expect to resolve through the implementation of this feature before stabilization? +- What related issues do you consider out of scope for this RFC that could be addressed in the future independently of the solution that comes out of this RFC? + +# Future possibilities +[future-possibilities]: #future-possibilities + +Think about what the natural extension and evolution of your proposal would +be and how it would affect the project and ecosystem as a whole in a holistic +way. Try to use this section as a tool to more fully consider all possible +interactions with the project and language in your proposal. +Also consider how the this all fits into the roadmap for the project +and of the relevant sub-team. + +This is also a good place to "dump ideas", if they are out of scope for the +RFC you are writing but otherwise related. + +If you have tried and cannot think of any future possibilities, +you may simply state that you cannot think of anything. + +Note that having something written down in the future-possibilities section +is not a reason to accept the current or a future RFC; such notes should be +in the section on motivation or rationale in this or subsequent RFCs. +The section merely provides additional information. + +# References +[references]: #references + +This is a sections for references such as links to other documents or reference implementations From a29db9af809e5e578d9adbd5bc66ada0aebb1da6 Mon Sep 17 00:00:00 2001 From: Yeastplume Date: Tue, 2 Jul 2019 15:07:56 +0100 Subject: [PATCH 02/15] add wallet runtime instantiation component --- .../0000-full-wallet-lifecycle.md | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) rename grinrfc-0000-full-wallet-lifecycle.md => text/0000-full-wallet-lifecycle.md (94%) diff --git a/grinrfc-0000-full-wallet-lifecycle.md b/text/0000-full-wallet-lifecycle.md similarity index 94% rename from grinrfc-0000-full-wallet-lifecycle.md rename to text/0000-full-wallet-lifecycle.md index d8c5bf4..4067610 100644 --- a/grinrfc-0000-full-wallet-lifecycle.md +++ b/text/0000-full-wallet-lifecycle.md @@ -1,12 +1,10 @@ -# GRIN-RFC-0000 Full Wallet Lifecycle Support in Wallet API +- Title: full-wallet-lifecycle +- Authors: [Michael Cordner](mailto:yeastplume@protonmail.com) +- Start date : June 26th, 2019 +- RFC PR: Edit if merged: [mimblewimble/grin-rfcs#0000](https://github.com/mimblewimble/grin-rfcs/pull/0000) +- Tracking issue: [Edit if merged with link to tracking github issue] -``` -- Number: GRIN-RFC-0000 -- Title: Full Wallet Lifecycle Support in Wallet API -- Status: Draft -- Authors: yeastplume (yeastplume@protonmail.com) -- Created : June 26th, 2019 -``` +--- # Summary [summary]: #summary @@ -81,13 +79,13 @@ The wallet data directory structure will become (for example): ... ``` -If a data directory is not provided in a wallet API call, it will be assumed to be operating on the `default` wallet +If a data directory is not provided in a wallet API call, it will be assumed to be operating on the `default` wallet. ### Data migration The target version of the wallet will contain a function to migrate existing wallets from the current data structure to the new, essentially moving `wallet_data/wallet.seed`, `wallet_data/db/` and `wallet_data/saved_txs/` into `wallet_data/default/wallet.seed` etc. -(Should this be performed within the API for consistency?) +(Should this be performed within an API call for consistency?) ## Wallet Initialization From 7a7b2aae944edcbd37b7f117724ca92d9d151e76 Mon Sep 17 00:00:00 2001 From: Yeastplume Date: Wed, 3 Jul 2019 10:15:06 +0100 Subject: [PATCH 03/15] update api definitions --- text/0000-full-wallet-lifecycle.md | 42 +++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/text/0000-full-wallet-lifecycle.md b/text/0000-full-wallet-lifecycle.md index 4067610..0821ac0 100644 --- a/text/0000-full-wallet-lifecycle.md +++ b/text/0000-full-wallet-lifecycle.md @@ -22,6 +22,12 @@ The Wallet APIs are intended to be the foundation upon which community-created w more difficult by the absence of wallet creation and seed management functions within the API. Ideally, it should be the case that a wallet can be instantiated and managed solely via the Owner API. +In order to achieve this, several other pieces of functionality will need to in place. These are outlined in detail below, but as a summary: + +* Support for multiple wallets within a single data directory +* Change the model for the command-line wallet from single-use commands to an internal prompt, keeping the wallet instance resident between commands. + + # Community-level explanation [community-level-explanation]: #community-level-explanation @@ -81,6 +87,9 @@ The wallet data directory structure will become (for example): If a data directory is not provided in a wallet API call, it will be assumed to be operating on the `default` wallet. +There should only be a single `grin-wallet.toml` file in the wallet's data directory. Its configuration settings are global to all wallet +instances. + ### Data migration The target version of the wallet will contain a function to migrate existing wallets from the current data structure to the new, @@ -94,19 +103,32 @@ in the `~/.grin/main` directory (or `~/.grin/floonet`, or the current directory creates a seed file, stores the resulting data files in the directory specified in `grin-wallet.toml` (`~/.grin/main/wallet_data` by default) and initialises the lmdb database. -OwnerAPI::create_config(Option) -> Outputs a `grin-wallet.toml` file into the given location, defaulting to `~/.grin/main/wallet_data` -OwnerAPI::create_wallet(Option, password) -> Creates and initializes a new wallet in the directory specified by `name`, `default` if None -OwnerAPI::get_mnemonic(name) -> Returns mnemonic from given wallet - It should be possible to run `grin-wallet owner_api` or invoke the API directly from a linked binary without having instantiated a wallet. - (How is grin-wallet.toml generated?) -## Additional API Functions +## Wallet Runtime Instantiation + +Currently, wallet instantiation works differently depending on whether the wallet is invoked via the APIs or via the command line. -Owner::SetWalletDirectory -> Set the top-level system wallet directory (`~/.grin/main/wallet_data` by default) -Owner::ListWallets -> list wallet directories +1. In the command line case, each wallet command is a separate invocation. Command line invocation will ask for a password, decrypt the master seed and initialize the wallet with the descrypted seed. It will then perform the desired function and return, zeroing memory and exiting the process. + +1. In the case of a listening API (owner or foreign), the password is given once and the wallet seed is decrypted. The wallet instance is then kept in memory by the handling thread, and re-used for each Foreign or Owner API call. + +In order to retain consistency and provide a framework through which the wallet can be run and lifecycle API functions can be called without wallet data actually being present, we propose that the operational model of the command-line wallet be changed to point 2 above. When the command-line wallet is first invoked, it will essentially present nothing other than a prompt. The user can then enter commands to create a wallet, instantiate a particular wallet, change the active wallet, etc. via the wallet's command prompt. + +Note that (wallet713)[https://github.com/vault713/wallet713] by vault713 already works exactly as this feature is described. Pending approval by vault 713, we propose merging the relevant code from wallet713 directly into the Grin command line wallet. + +Note this will mean significant changes from an end-user perspective on the relevant wallet release, as the existing command line commands will all be replaced with internal equivalents. There will also be additional end-user lifecycle commands that call new wallet lifecycle APIs. + +## New API Functions + +* OwnerAPI::set_wallet_directory -> Set the top-level system wallet directory (`~/.grin/main/wallet_data` by default,) from which named wallets are read +* OwnerAPI::create_config(Option) -> Outputs a `grin-wallet.toml` file into the given location, defaulting to `~/.grin/main/wallet_data` +* OwnerAPI::list_wallets -> list created wallets (subdirectories from the system wallet directory). +* OwnerAPI::create_wallet(Option, password) -> Creates and initializes a new wallet in the directory specified by `name`, `default` if None +* OwnerAPI::open_wallet(name, password) -> Opens the specified wallet and sets it as the 'active' wallet. All further API commands will be performed against this wallet. +* OwnerAPI::get_mnemonic() -> Returns mnemonic from the active, (open) wallet # Drawbacks [drawbacks]: #drawbacks @@ -168,4 +190,6 @@ The section merely provides additional information. # References [references]: #references -This is a sections for references such as links to other documents or reference implementations +**wallet713** +- https://github.com/vault713/wallet713 + From ffda4e7fcf35f50e7fa8a70fd629bf68280a4a59 Mon Sep 17 00:00:00 2001 From: Yeastplume Date: Wed, 3 Jul 2019 15:11:14 +0100 Subject: [PATCH 04/15] further shaping of exact API --- text/0000-full-wallet-lifecycle.md | 69 +++++++++++++++++++++++------- 1 file changed, 54 insertions(+), 15 deletions(-) diff --git a/text/0000-full-wallet-lifecycle.md b/text/0000-full-wallet-lifecycle.md index 0821ac0..bbaea10 100644 --- a/text/0000-full-wallet-lifecycle.md +++ b/text/0000-full-wallet-lifecycle.md @@ -15,8 +15,8 @@ Increase the scope of the Grin Wallet's Owner API to support all wallet lifecycl [motivation]: #motivation Grin Wallet's APIs currently provides functions for transacting and querying the contents of the wallet. However, several pieces of functionality -around wallet creation and seed/password management are not included within the API. This means that any consumers of the API will expect their users -to initialize the wallet manually before the APIs can be used. +around wallet creation and seed/password management are not included within the API. This means that any consumers of the API will either expect their users +to initialize the wallet manually before the APIs can be used, or provide custom management for wallet lifecycle functions. The Wallet APIs are intended to be the foundation upon which community-created wallets should be built, and the job of a wallet creator is made far more difficult by the absence of wallet creation and seed management functions within the API. Ideally, it should be the case that a wallet can @@ -27,7 +27,6 @@ In order to achieve this, several other pieces of functionality will need to in * Support for multiple wallets within a single data directory * Change the model for the command-line wallet from single-use commands to an internal prompt, keeping the wallet instance resident between commands. - # Community-level explanation [community-level-explanation]: #community-level-explanation @@ -94,7 +93,7 @@ instances. The target version of the wallet will contain a function to migrate existing wallets from the current data structure to the new, essentially moving `wallet_data/wallet.seed`, `wallet_data/db/` and `wallet_data/saved_txs/` into `wallet_data/default/wallet.seed` etc. -(Should this be performed within an API call for consistency?) +(Should this be performed within an API call for consistency, or should existing files just be left alone?) ## Wallet Initialization @@ -105,8 +104,6 @@ and initialises the lmdb database. It should be possible to run `grin-wallet owner_api` or invoke the API directly from a linked binary without having instantiated a wallet. -(How is grin-wallet.toml generated?) - ## Wallet Runtime Instantiation Currently, wallet instantiation works differently depending on whether the wallet is invoked via the APIs or via the command line. @@ -123,12 +120,54 @@ Note this will mean significant changes from an end-user perspective on the rele ## New API Functions -* OwnerAPI::set_wallet_directory -> Set the top-level system wallet directory (`~/.grin/main/wallet_data` by default,) from which named wallets are read -* OwnerAPI::create_config(Option) -> Outputs a `grin-wallet.toml` file into the given location, defaulting to `~/.grin/main/wallet_data` -* OwnerAPI::list_wallets -> list created wallets (subdirectories from the system wallet directory). -* OwnerAPI::create_wallet(Option, password) -> Creates and initializes a new wallet in the directory specified by `name`, `default` if None -* OwnerAPI::open_wallet(name, password) -> Opens the specified wallet and sets it as the 'active' wallet. All further API commands will be performed against this wallet. -* OwnerAPI::get_mnemonic() -> Returns mnemonic from the active, (open) wallet +* `OwnerAPI::set_wallet_directory(dir: String) -> Result<(), libwallet::Error>` + - On API startup, it's assumed the top-level wallet data directory is `~/.grin/main/wallet_data` (or floonet equivalent) + - Set the top-level system wallet directory from which named wallets are read. Further calls to lifecycle functions will use this wallet directory +* `OwnerAPI::create_config(data_dir: Option, config_overrides: Option) -> Result<(), libwallet::Error>` + - Outputs a `grin-wallet.toml` file into current top-level system wallet directory + - Optionally takes wallet configuration structure to override defaults in the grin-wallet.toml file +* `OwnerAPI::list_wallets() -> Result, libwallet::Error>` + - list created wallets (i.e. wallet subdirectory names from the top-level system wallet directory). +* `OwnerAPI::create_wallet(name: Option, mnemonic: String, password: String) -> Result<(), libwallet::Error>` + - Creates and initializes a new wallet in the subdirectory specified by `name`, `default` if None + - Initializes seed from given mnemonic if given, random seed otherwise + - Should error appropriately if the wallet subdir already exists +* `OwnerAPI::open_wallet(name: Option, password: String) -> Result<(), libwallet::Error>` + - Opens the specified wallet and sets it as the 'active' wallet. All further API commands will be performed against this wallet. +* `OwnerAPI::get_mnemonic() -> Result` + - Returns the mnemonic from the active, (open) wallet +* `OwnerAPI::change_password(old: String, new: String) -> Result<(), libwallet::Error>` + - Changes the password for the open wallet. This will essentially: + - Close the wallet instance + - Confirm the existing seed can be opened with the given password + - Regenerate the `wallet.seed` file with the new password + - Re-open the wallet instance + - (Should this just operate on closed wallets instead?) + +### Use cases + +* First time use + - run `grin-wallet` (or launch OwnerAPI for web wallet) + - top-level data directory is set to `~/.grin/main/wallet_data` (but nothing is yet written) + - Optionally config data directory + - `create_config` called to create `grin-wallet.toml` + - `create_wallet` called with new wallet name and seed to create a new wallet and seed + - new wallet subdirectory is created and initialized + +* Recover from seed + - As above, except call `create_wallet` with mnemonic seed instead + +### Implementation notes + +Although this document doesn't attempt to outline implementation, a few notes to consider for the implementor: + +* Currently, the code that deals with wallet initialization and seed management sits outside the wallet APIs, in the `impls` crate, (denoting they're implementation specific). The implementation should attempt to refactor traits from these hard implementations into a new interface, similar to the existing WalletBackend and NodeClient interfaces (WalletLifecycleManager, for instance). The implementation within `impls` will then become an implementation of that trait, and can be substituted by wallet authors with their own implementations. +* The implementation period of this RFC may be a good time to remove the BIP32 specific code out from Grin core into the wallet or into a separate rust crate (probably more desirable). +* New API functions should be implemented as additions, with the new features optional to ensure complete backwards compatibility +* The implementation should likely be split up into separate PRs (possibly across multiple releases), in rough order: + * Support for multiple wallets, and upgrade mechanism + * Trait refactoring and new API function implementation + * (wallet713-based) CLI Rework, including calls to new API functions # Drawbacks [drawbacks]: #drawbacks @@ -162,9 +201,9 @@ Please also take into consideration that Grin sometimes intentionally diverges f # Unresolved questions [unresolved-questions]: #unresolved-questions -- What parts of the design do you expect to resolve through the RFC process before this gets merged? -- What parts of the design do you expect to resolve through the implementation of this feature before stabilization? -- What related issues do you consider out of scope for this RFC that could be addressed in the future independently of the solution that comes out of this RFC? +* Security implications of sending passwords and/or master seed mnemonics through the JSON-RPC API, and how to deal with this as securely as possible. +* Security implications of leaving master seed 'open' in memory (this is aleady a concern for most wallets, but there isn't a clear way to deal with this). +* Should upgrade mechanism to support multiple wallets just leave the default directory in place, to minimise the impact of disruption? # Future possibilities [future-possibilities]: #future-possibilities From 0398709eccfb0fecdb05b9773c5cb93dbfc5da19 Mon Sep 17 00:00:00 2001 From: Yeastplume Date: Thu, 4 Jul 2019 12:23:28 +0100 Subject: [PATCH 05/15] split into 3 separate RFCs --- text/0000-full-wallet-lifecycle.md | 153 +++++------------------------ 1 file changed, 22 insertions(+), 131 deletions(-) diff --git a/text/0000-full-wallet-lifecycle.md b/text/0000-full-wallet-lifecycle.md index bbaea10..087cf79 100644 --- a/text/0000-full-wallet-lifecycle.md +++ b/text/0000-full-wallet-lifecycle.md @@ -9,7 +9,7 @@ # Summary [summary]: #summary -Increase the scope of the Grin Wallet's Owner API to support all wallet lifecycle functions. Wallet creation, seed management, (TBD) +Increase the scope of the Grin Wallet's Owner API to support full wallet lifecycle functions. # Motivation [motivation]: #motivation @@ -22,79 +22,14 @@ The Wallet APIs are intended to be the foundation upon which community-created w more difficult by the absence of wallet creation and seed management functions within the API. Ideally, it should be the case that a wallet can be instantiated and managed solely via the Owner API. -In order to achieve this, several other pieces of functionality will need to in place. These are outlined in detail below, but as a summary: - -* Support for multiple wallets within a single data directory -* Change the model for the command-line wallet from single-use commands to an internal prompt, keeping the wallet instance resident between commands. - # Community-level explanation [community-level-explanation]: #community-level-explanation -Explain the proposal as if it were already included in the Grin ecosystem and you were teaching it to another Grin community member. That generally means: - -- Introducing new named concepts. -- Explaining the feature largely in terms of examples. -- Explaining how Grin community members should *think* about the feature, and how it should impact the way they use Grin. It should explain the impact as concretely as possible. -- If applicable, provide sample error messages, deprecation warnings, or migration guidance. -- If applicable, describe the differences between teaching this to existing Grin community members and new Grin community members. - -For implementation-oriented RFCs (e.g. for wallet), this section should focus on how wallet contributors should think about the change, and give examples of its concrete impact. For policy RFCs, this section should provide an example-driven introduction to the policy, and explain its impact in concrete terms. +From an end-user perspective, (i.e. end-users of community wallets that use the wallet API,) this change should be transparent. # Reference-level explanation [reference-level-explanation]: #reference-level-explanation -## Wallet Backend Structure and Multiple Wallets - -The Wallet API should be able to support multiple named Wallets. Currently, the default wallet data directory assumes a single wallet instance, and -is structured as follows: - -``` -~/.grin/main/ - grin-wallet.log - grin-wallet.toml - wallet_data/ - wallet.seed - db/ - saved_txs/ -``` - -`wallet_data` contains the seed and database for a single instance of the wallet, with the path to the wallet's data directory configured in -`grin_wallet.toml`. - -The structure of the wallet's data files will be changed to allow multiple wallet instances, with each named wallet contained in a directory -named according to name of the wallet provided by the end user. Current single-instance wallet data will be placed into a directory called -`default`, and the API and all wallet software will assume the default directory is to be used if no name is provided. - -The wallet data directory structure will become (for example): - -``` -~/.grin/main/ - grin-wallet.log - grin-wallet.toml - wallet_data/ - default/ - wallet.seed - db/ - saved_txs/ - my_wallet_1/ - wallet.seed - ... - my_wallet_2/ - wallet.seed - ... -``` - -If a data directory is not provided in a wallet API call, it will be assumed to be operating on the `default` wallet. - -There should only be a single `grin-wallet.toml` file in the wallet's data directory. Its configuration settings are global to all wallet -instances. - -### Data migration - -The target version of the wallet will contain a function to migrate existing wallets from the current data structure to the new, -essentially moving `wallet_data/wallet.seed`, `wallet_data/db/` and `wallet_data/saved_txs/` into `wallet_data/default/wallet.seed` etc. -(Should this be performed within an API call for consistency, or should existing files just be left alone?) - ## Wallet Initialization Currently, wallet data does not exist until the user runs `grin-wallet init`. The `init` command creates `grin-wallet.toml`, @@ -104,20 +39,6 @@ and initialises the lmdb database. It should be possible to run `grin-wallet owner_api` or invoke the API directly from a linked binary without having instantiated a wallet. -## Wallet Runtime Instantiation - -Currently, wallet instantiation works differently depending on whether the wallet is invoked via the APIs or via the command line. - -1. In the command line case, each wallet command is a separate invocation. Command line invocation will ask for a password, decrypt the master seed and initialize the wallet with the descrypted seed. It will then perform the desired function and return, zeroing memory and exiting the process. - -1. In the case of a listening API (owner or foreign), the password is given once and the wallet seed is decrypted. The wallet instance is then kept in memory by the handling thread, and re-used for each Foreign or Owner API call. - -In order to retain consistency and provide a framework through which the wallet can be run and lifecycle API functions can be called without wallet data actually being present, we propose that the operational model of the command-line wallet be changed to point 2 above. When the command-line wallet is first invoked, it will essentially present nothing other than a prompt. The user can then enter commands to create a wallet, instantiate a particular wallet, change the active wallet, etc. via the wallet's command prompt. - -Note that (wallet713)[https://github.com/vault713/wallet713] by vault713 already works exactly as this feature is described. Pending approval by vault 713, we propose merging the relevant code from wallet713 directly into the Grin command line wallet. - -Note this will mean significant changes from an end-user perspective on the relevant wallet release, as the existing command line commands will all be replaced with internal equivalents. There will also be additional end-user lifecycle commands that call new wallet lifecycle APIs. - ## New API Functions * `OwnerAPI::set_wallet_directory(dir: String) -> Result<(), libwallet::Error>` @@ -126,14 +47,14 @@ Note this will mean significant changes from an end-user perspective on the rele * `OwnerAPI::create_config(data_dir: Option, config_overrides: Option) -> Result<(), libwallet::Error>` - Outputs a `grin-wallet.toml` file into current top-level system wallet directory - Optionally takes wallet configuration structure to override defaults in the grin-wallet.toml file -* `OwnerAPI::list_wallets() -> Result, libwallet::Error>` - - list created wallets (i.e. wallet subdirectory names from the top-level system wallet directory). +* `OwnerAPI::open_wallet(name: Option, password: String) -> Result<(), libwallet::Error>` + - Opens the wallet and sets it as the 'active' wallet. All further API commands will be performed against this wallet. + - The 'name' argument is included for future use, anticipating the inclusion of multiple wallets and seeds within a single top-level wallet directory. * `OwnerAPI::create_wallet(name: Option, mnemonic: String, password: String) -> Result<(), libwallet::Error>` - - Creates and initializes a new wallet in the subdirectory specified by `name`, `default` if None + - Creates and initializes a new wallet - Initializes seed from given mnemonic if given, random seed otherwise - - Should error appropriately if the wallet subdir already exists -* `OwnerAPI::open_wallet(name: Option, password: String) -> Result<(), libwallet::Error>` - - Opens the specified wallet and sets it as the 'active' wallet. All further API commands will be performed against this wallet. + - Should error appropriately if the wallet already exists + - The 'name' parameter is included for future use as in `open_wallet` above. * `OwnerAPI::get_mnemonic() -> Result` - Returns the mnemonic from the active, (open) wallet * `OwnerAPI::change_password(old: String, new: String) -> Result<(), libwallet::Error>` @@ -143,16 +64,17 @@ Note this will mean significant changes from an end-user perspective on the rele - Regenerate the `wallet.seed` file with the new password - Re-open the wallet instance - (Should this just operate on closed wallets instead?) +* `OwnerAPI::delete_wallet(name: Option, password: String) -> Result<(), libwallet::Error>` + - Dangerous function that removes all wallet data + - name argument reserved for future use ### Use cases -* First time use - - run `grin-wallet` (or launch OwnerAPI for web wallet) +* First time use (API Case) + - run `grin-wallet owner_api` - top-level data directory is set to `~/.grin/main/wallet_data` (but nothing is yet written) - - Optionally config data directory - `create_config` called to create `grin-wallet.toml` - - `create_wallet` called with new wallet name and seed to create a new wallet and seed - - new wallet subdirectory is created and initialized + - `create_wallet` called (with name == None) and seed to create a new wallet and seed, wallet data is created and initialized * Recover from seed - As above, except call `create_wallet` with mnemonic seed instead @@ -164,39 +86,21 @@ Although this document doesn't attempt to outline implementation, a few notes to * Currently, the code that deals with wallet initialization and seed management sits outside the wallet APIs, in the `impls` crate, (denoting they're implementation specific). The implementation should attempt to refactor traits from these hard implementations into a new interface, similar to the existing WalletBackend and NodeClient interfaces (WalletLifecycleManager, for instance). The implementation within `impls` will then become an implementation of that trait, and can be substituted by wallet authors with their own implementations. * The implementation period of this RFC may be a good time to remove the BIP32 specific code out from Grin core into the wallet or into a separate rust crate (probably more desirable). * New API functions should be implemented as additions, with the new features optional to ensure complete backwards compatibility -* The implementation should likely be split up into separate PRs (possibly across multiple releases), in rough order: - * Support for multiple wallets, and upgrade mechanism - * Trait refactoring and new API function implementation - * (wallet713-based) CLI Rework, including calls to new API functions # Drawbacks [drawbacks]: #drawbacks -Why should we *not* do this? +* Sending sensitive information such as passwords and mnemonics via the OwnerAPI is an obvious security concern. # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives -- Why is this design the best in the space of possible designs? -- What other designs have been considered and what is the rationale for not choosing them? -- What is the impact of not doing this? +TBD # Prior art [prior-art]: #prior-art -Discuss prior art, both the good and the bad, in relation to this proposal. -A few examples of what this can include are: - -- For core, node, wallet and infrastructure proposals: Does this feature exist in other projects and what experience have their community had? -- For community, ecosystem and moderation proposals: Is this done by some other community and what were their experiences with it? -- For other teams: What lessons can we learn from what other communities have done here? -- Papers: Are there any published papers or great posts that discuss this? If you have some relevant papers to refer to, this can serve as a more detailed theoretical background. - -This section is intended to encourage you as an author to think about the lessons from other languages, provide readers of your RFC with a fuller picture. -If there is no prior art, that is fine - your ideas are interesting to us whether they are brand new or if it is an adaptation from other projects. - -Note that while precedent set by other projects is some motivation, it does not on its own motivate an RFC. -Please also take into consideration that Grin sometimes intentionally diverges from common project features. +TBD # Unresolved questions [unresolved-questions]: #unresolved-questions @@ -208,27 +112,14 @@ Please also take into consideration that Grin sometimes intentionally diverges f # Future possibilities [future-possibilities]: #future-possibilities -Think about what the natural extension and evolution of your proposal would -be and how it would affect the project and ecosystem as a whole in a holistic -way. Try to use this section as a tool to more fully consider all possible -interactions with the project and language in your proposal. -Also consider how the this all fits into the roadmap for the project -and of the relevant sub-team. - -This is also a good place to "dump ideas", if they are out of scope for the -RFC you are writing but otherwise related. +The changes in this RFC lead the way for: -If you have tried and cannot think of any future possibilities, -you may simply state that you cannot think of anything. - -Note that having something written down in the future-possibilities section -is not a reason to accept the current or a future RFC; such notes should be -in the section on motivation or rationale in this or subsequent RFCs. -The section merely provides additional information. +* Support for multiple wallets in a single top-level data directory +* An alternate method of command-line invocation whereby the wallet presents its own prompt instead of using single-use commands. # References [references]: #references -**wallet713** -- https://github.com/vault713/wallet713 +None + From bf11ab74bbd81541d384883a128a22eff52b715a Mon Sep 17 00:00:00 2001 From: Yeastplume Date: Thu, 4 Jul 2019 12:34:47 +0100 Subject: [PATCH 06/15] add note on CLI wallet --- text/0000-full-wallet-lifecycle.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/text/0000-full-wallet-lifecycle.md b/text/0000-full-wallet-lifecycle.md index 087cf79..45b71c8 100644 --- a/text/0000-full-wallet-lifecycle.md +++ b/text/0000-full-wallet-lifecycle.md @@ -79,6 +79,10 @@ It should be possible to run `grin-wallet owner_api` or invoke the API directly * Recover from seed - As above, except call `create_wallet` with mnemonic seed instead +### API only + +Note that this RFC does not propose making user-facing changes to the existing CLI wallet to invoke these functions. It's expected that the existing cli functionality will be modified to invoke the new API functions. + ### Implementation notes Although this document doesn't attempt to outline implementation, a few notes to consider for the implementor: From 1ee8c3a9a2e13f135d62ff12557e3b0999cedada Mon Sep 17 00:00:00 2001 From: Yeastplume Date: Mon, 22 Jul 2019 15:17:54 +0100 Subject: [PATCH 07/15] add initial pass at security scheme --- assets/0000/0000-api-call.puml | 34 +++++++++++++++++++++ assets/0000/0000-api-call.svg | 47 ++++++++++++++++++++++++++++++ assets/0000/0000-dh-init.puml | 16 ++++++++++ assets/0000/0000-dh-init.svg | 29 ++++++++++++++++++ assets/0000/0000-open-wallet.puml | 21 +++++++++++++ assets/0000/0000-open-wallet.svg | 34 +++++++++++++++++++++ assets/0000/0000-unsafe-mode.puml | 28 ++++++++++++++++++ assets/0000/0000-unsafe-mode.svg | 41 ++++++++++++++++++++++++++ text/0000-full-wallet-lifecycle.md | 43 +++++++++++++++++++++++++++ 9 files changed, 293 insertions(+) create mode 100644 assets/0000/0000-api-call.puml create mode 100644 assets/0000/0000-api-call.svg create mode 100644 assets/0000/0000-dh-init.puml create mode 100644 assets/0000/0000-dh-init.svg create mode 100644 assets/0000/0000-open-wallet.puml create mode 100644 assets/0000/0000-open-wallet.svg create mode 100644 assets/0000/0000-unsafe-mode.puml create mode 100644 assets/0000/0000-unsafe-mode.svg diff --git a/assets/0000/0000-api-call.puml b/assets/0000/0000-api-call.puml new file mode 100644 index 0000000..0338d2a --- /dev/null +++ b/assets/0000/0000-api-call.puml @@ -0,0 +1,34 @@ +@startuml +actor "Client" as client +participant "JSON-RPC API (if used)" as jsonrpc +participant "Internal Rust API" as rustapi +participant "Wallet Backend" as backend + +== Previous call == + +rustapi -> rustapi: Generate/store new nonce -> (**k**) +rustapi -> rustapi: Encrypt any sensitive call result data (**secret_data**, **s** + **k_prev**) -> **sdEnc** +rustapi -> rustapi: Encrypt (**k**, **s** + **k_prev**) -> (**kEnc**) +note left: 'open_wallet' returns **t|k**, all other api calls return **k** +rustapi -> jsonrpc: Return (**kEnc**, Result, **sdEnc**) +jsonrpc -> client: Return (**kEnc**, Result, **sdEnc**) +client -> client: decrypt(**kEnc**, **s** + **k_prev**) -> **k** +client -> client: decrypt(**sdEnc**, **s** + **k_prev**) -> **secret_data** +client -> client: Next call encrypts t as enc(**s** + **k**, **t**) + +== API Call == +client -> jsonrpc: Encrypt token (**s** + **k**, **t**) -> **tEnc** +client -> jsonrpc: store **k** as **k_prev** +client -> jsonrpc: Encrypt sensitive data (**s** + **k**, **secret_data**) -> **sdEnc** +client -> jsonrpc: api_call(**tEnc**, **sdEnc**) +jsonrpc -> rustapi: api_call(**tEnc**, **sdEnc**) +rustapi -> rustapi: dec(**tEnc**, **s** + **k**) -> **T** +rustapi -> rustapi: dec(**sdEnc**, **s** + **k**) -> **secret_data** +rustapi -> rustapi: store **k** as **k_prev** +rustapi -> backend: decode_seed(**T**) +backend -> backend: calculate (**T** ^ **SeedT**) -> **Seed** +rustapi -> backend: Further API calls +backend -> backend: Drop **Seed** +rustapi -> rustapi: Generate/store new nonce -> (**k**) +rustapi -> client: (Proceed as per 'previous call' section above) +@enduml \ No newline at end of file diff --git a/assets/0000/0000-api-call.svg b/assets/0000/0000-api-call.svg new file mode 100644 index 0000000..039aacd --- /dev/null +++ b/assets/0000/0000-api-call.svg @@ -0,0 +1,47 @@ +ClientClientJSON-RPC API (if used)JSON-RPC API (if used)Internal Rust APIInternal Rust APIWallet BackendWallet BackendPrevious callGenerate/store new nonce -> (k)Encrypt any sensitive call result data (secret_data,s+k_prev) ->sdEncEncrypt (k,s+k_prev) -> (kEnc)'open_wallet' returnst|k, all other api calls returnkReturn (kEnc, Result,sdEnc)Return (kEnc, Result,sdEnc)decrypt(kEnc,s+k_prev) ->kdecrypt(sdEnc,s+k_prev) ->secret_dataNext call encrypts t as enc(s+k,t)API CallEncrypt token (s+k,t) ->tEncstorekask_prevEncrypt sensitive data (s+k,secret_data) ->sdEncapi_call(tEnc,sdEnc)api_call(tEnc,sdEnc)dec(tEnc,s+k) ->Tdec(sdEnc,s+k) ->secret_datastorekask_prevdecode_seed(T)calculate (T^SeedT) ->SeedFurther API callsDropSeedGenerate/store new nonce -> (k)(Proceed as per 'previous call' section above) \ No newline at end of file diff --git a/assets/0000/0000-dh-init.puml b/assets/0000/0000-dh-init.puml new file mode 100644 index 0000000..72911ca --- /dev/null +++ b/assets/0000/0000-dh-init.puml @@ -0,0 +1,16 @@ +@startuml +actor "Client" as client +participant "JSON-RPC API (if used)" as jsonrpc +participant "Internal Rust API" as rustapi +participant "Wallet Backend" as backend + +== Initialization (ECDH/secp256k1)== +client -> client: Calc Pub ECDH Key (**cP**) +client -> jsonrpc: init_api_secure (**cP**) +jsonrpc -> rustapi: init_api_secure (**cP**) +rustapi -> rustapi: Calc Pub ECDH Key (**sP**) +rustapi -> rustapi: Calc shared secret (**sS**) +rustapi -> jsonrpc: **sP** +jsonrpc -> client: **sP** +client -> client: Calc shared secret (**s**) +@enduml \ No newline at end of file diff --git a/assets/0000/0000-dh-init.svg b/assets/0000/0000-dh-init.svg new file mode 100644 index 0000000..8fa3fac --- /dev/null +++ b/assets/0000/0000-dh-init.svg @@ -0,0 +1,29 @@ +ClientClientJSON-RPC API (if used)JSON-RPC API (if used)Internal Rust APIInternal Rust APIWallet BackendWallet BackendInitialization (ECDH/secp256k1)Calc Pub ECDH Key (cP)init_api_secure (cP)init_api_secure (cP)Calc Pub ECDH Key (sP)Calc shared secret (sS)sPsPCalc shared secret (s) \ No newline at end of file diff --git a/assets/0000/0000-open-wallet.puml b/assets/0000/0000-open-wallet.puml new file mode 100644 index 0000000..b7ccedc --- /dev/null +++ b/assets/0000/0000-open-wallet.puml @@ -0,0 +1,21 @@ +@startuml +actor "Client" as client +participant "JSON-RPC API (if used)" as jsonrpc +participant "Internal Rust API" as rustapi +participant "Wallet Backend" as backend +== Open Wallet == +client -> client: Encrypt password (**pw**) with DH shared secret **s** -> **pwEnc** +client -> jsonrpc: Open Wallet (**pwEnc**) +jsonrpc -> rustapi: Open Wallet (**pwEnc**) +rustapi -> rustapi: Decrypt (**pwEnc**, **s**) -> (**pw**) +rustapi -> rustapi: Generate Token (**t**) +rustapi -> backend: Open Wallet(**t**, **pw**) +backend -> backend: Decrypt seed(**pw**) -> **Seed** +backend -> backend: Store Seed^Token (**seedT**) +rustapi -> rustapi: Generate random nonce -> (**k**) +rustapi -> rustapi: Encrypt (**T**|**k**, **s**) -> (**tkEnc**) +rustapi -> jsonrpc: Return (**tkEnc**) +jsonrpc -> client: Return (**tkEnc**) +client -> client: Decode(**s**, **tkEnc**) -> **t|k** +client -> client: Next call encrypts t as enc(**s** + **k**, **t**) +@enduml \ No newline at end of file diff --git a/assets/0000/0000-open-wallet.svg b/assets/0000/0000-open-wallet.svg new file mode 100644 index 0000000..d355abc --- /dev/null +++ b/assets/0000/0000-open-wallet.svg @@ -0,0 +1,34 @@ +ClientClientJSON-RPC API (if used)JSON-RPC API (if used)Internal Rust APIInternal Rust APIWallet BackendWallet BackendOpen WalletEncrypt password (pw) with DH shared secrets->pwEncOpen Wallet (pwEnc)Open Wallet (pwEnc)Decrypt (pwEnc,s) -> (pw)Generate Token (t)Open Wallet(t,pw)Decrypt seed(pw) ->SeedStore Seed^Token (seedT)Generate random nonce -> (k)Encrypt (T|k,s) -> (tkEnc)Return (tkEnc)Return (tkEnc)Decode(s,tkEnc) ->t|kNext call encrypts t as enc(s+k,t) \ No newline at end of file diff --git a/assets/0000/0000-unsafe-mode.puml b/assets/0000/0000-unsafe-mode.puml new file mode 100644 index 0000000..0d8a8e2 --- /dev/null +++ b/assets/0000/0000-unsafe-mode.puml @@ -0,0 +1,28 @@ +@startuml +actor "Client" as client +participant "JSON-RPC API (if used)" as jsonrpc +participant "Internal Rust API" as rustapi +participant "Wallet Backend" as backend +== Open Wallet == +client -> client: Plain-text password (**pw**) +client -> jsonrpc: Open Wallet(**pw**) +jsonrpc -> rustapi: Open Wallet(**pw**) +rustapi -> rustapi: Generate Token (**t**) +rustapi -> backend: Open Wallet(**t**, **pw**) +backend -> backend: Decrypt seed(**pw**) -> **Seed** +backend -> backend: Store **Seed**^**Token** (**seedT**) +rustapi -> jsonrpc: Return (**t**) +jsonrpc -> client: Return (**t**) +client -> client: Include **t** in next call + +== API Call == +client -> jsonrpc: api_call(**t**, **data**) +note right: Sensitive data such as recovery seeds are in plaintext +jsonrpc -> rustapi: api_call(**t**, **data**) +rustapi -> backend: decode_seed(**T**) +backend -> backend: calculate (**T** ^ **SeedT**) -> **Seed** +rustapi <-> backend: Further API calls +backend -> backend: Drop **Seed** +rustapi -> jsonrpc: Return Data +jsonrpc -> client: Return Data +@enduml \ No newline at end of file diff --git a/assets/0000/0000-unsafe-mode.svg b/assets/0000/0000-unsafe-mode.svg new file mode 100644 index 0000000..24fa944 --- /dev/null +++ b/assets/0000/0000-unsafe-mode.svg @@ -0,0 +1,41 @@ +ClientClientJSON-RPC API (if used)JSON-RPC API (if used)Internal Rust APIInternal Rust APIWallet BackendWallet BackendOpen WalletPlain-text password (pw)Open Wallet(pw)Open Wallet(pw)Generate Token (t)Open Wallet(t,pw)Decrypt seed(pw) ->SeedStoreSeed^Token(seedT)Return (t)Return (t)Includetin next callAPI Callapi_call(t,data)Sensitive data such as recovery seeds are in plaintextapi_call(t,data)decode_seed(T)calculate (T^SeedT) ->SeedFurther API callsDropSeedReturn DataReturn Data \ No newline at end of file diff --git a/text/0000-full-wallet-lifecycle.md b/text/0000-full-wallet-lifecycle.md index 45b71c8..16539dd 100644 --- a/text/0000-full-wallet-lifecycle.md +++ b/text/0000-full-wallet-lifecycle.md @@ -39,8 +39,51 @@ and initialises the lmdb database. It should be possible to run `grin-wallet owner_api` or invoke the API directly from a linked binary without having instantiated a wallet. +## Security Model + +Given that the Wallet's Owner API needs to deal with sensitive data such as passwords and seed phrases, the API will be enhanced with a new ECDH and Token-based security model, the primary goals of which are to: + +* Ensure sensitive data such as passwords or seed phrases are always end-to-end encrypted between the client and Owner API server, regardless of what higher-level protocols are used during the exchange. +* Minimize the potential for damage that can be done by a third party listening on the exchange between a wallet client and its corresponding server. +* Ensure that sensitive data such as passwords or seed phrases are not resident in server-side memory any longer than they absolutely need to be. + +Note that this mode of operation is primarly intended for use over the JSON-RPC API, which supports many different architectural possiblities. Clients that link libraries directly and keep all sensitive data in the same process would see less benefit from this scheme, and an alternative model which doesn't encrypt any sensitive data is provided. Further, authors of existing wallets will need time to consider and/or implement the added complexity needed on the client-side to support ECDH and encryption. It's therefore proposed that the Owner API initially provide the new "SecureAPI" mode as an optional feature, with wallet authors strongly encouraged to make use of it. Support for the "InsecureAPI" model can be maintained indefinitely for directly-linked wallets, and for the JSON-RPC API until a cut-off release at some point in the future. + +### SecureAPI Mode + +SecureAPI Mode consists of an ECDH key agreement followed by the establishment of an API Token that's used to XOR encrypt the wallet seed on the server side. The negotiated ECDH shared key is used to encrypt the initial password used during the first call to the `open_wallet` API function, which generates and returns a token to the client to be supplied during all subsequent API calls. The token is encrypted using a scheme whereby a new random nonce is generated during each API call, which both client and server will add to the original shared key for use in the next API call. This ensures that each instance of an encrypted Token passed in by the client is single-use, and a snooping attacker can not reuse an encrypted token. + +Q: Exact encryption parameters TBD + +### Security Mode Selection + +To initialize SecureAPI Mode, clients will generate an EC keypair using the secp256k1 curve, and provide the public key to the Owner API server via a new `init_api_secure` method. Both client and server will calculate the shared key, and store this key for the remainder of the session. The sequence of operations is outlined below: + +![init_api_secure](../assets/0000/0000-dh-init.svg) + +Any wallets opened by the API will be closed on a call to `init_api_secure`. Once `init_api_secure` is called the API will assume that the next call to `open_wallet` will contain an encrypted password, and that that all subsequent API calls will be accompanied with a valid encrypted token derived during the call to the `open_wallet` function. This assumption will remain until the server process exits or a call to a corresponding `close_api_secure` function is called. + +The shared secret can be refreshed by the client at any time with another call to `init_api_secure`. Closing a wallet via the `close_wallet` function does not regenerate the shared secret. + +### Opening a Wallet + +Opening a wallet in SecureAPI mode consists of encrypting the password with the original shared secret `s` and providing it to the `open_wallet` api function. The decrypted password is used on the server-side to unlock the wallet master seed, which is stored XORed against a randomly-generated token T. T is then returned to the client for use in further API calls alongside another randomly generated "next nonce" k, to be used in encryption of T during the next call: + +![open_wallet](../assets/0000/0000-open-wallet.svg) + +### Calling API functions in SecureAPI mode + +Each call to the API function encrypts the token T with the DH-derived shared secret key added to a random nonce generated by the server during the previous API call. The token is XORed against the stored XORed seed to recover the original seed for each call, and the seed value is dropped and zeroed from memory when each API call returns. + +![api_call](../assets/0000/0000-api-call.svg) + + + + ## New API Functions +(Needs updating) + * `OwnerAPI::set_wallet_directory(dir: String) -> Result<(), libwallet::Error>` - On API startup, it's assumed the top-level wallet data directory is `~/.grin/main/wallet_data` (or floonet equivalent) - Set the top-level system wallet directory from which named wallets are read. Further calls to lifecycle functions will use this wallet directory From e061f9cf62dcf5f7547ccad533fc8df7e08e6698 Mon Sep 17 00:00:00 2001 From: Yeastplume Date: Mon, 22 Jul 2019 15:19:54 +0100 Subject: [PATCH 08/15] add insecure status --- text/0000-full-wallet-lifecycle.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/text/0000-full-wallet-lifecycle.md b/text/0000-full-wallet-lifecycle.md index 16539dd..38336ea 100644 --- a/text/0000-full-wallet-lifecycle.md +++ b/text/0000-full-wallet-lifecycle.md @@ -55,7 +55,7 @@ SecureAPI Mode consists of an ECDH key agreement followed by the establishment o Q: Exact encryption parameters TBD -### Security Mode Selection +#### Security Mode Selection To initialize SecureAPI Mode, clients will generate an EC keypair using the secp256k1 curve, and provide the public key to the Owner API server via a new `init_api_secure` method. Both client and server will calculate the shared key, and store this key for the remainder of the session. The sequence of operations is outlined below: @@ -65,19 +65,25 @@ Any wallets opened by the API will be closed on a call to `init_api_secure`. Onc The shared secret can be refreshed by the client at any time with another call to `init_api_secure`. Closing a wallet via the `close_wallet` function does not regenerate the shared secret. -### Opening a Wallet +#### Opening a Wallet in SecureAPI Mode Opening a wallet in SecureAPI mode consists of encrypting the password with the original shared secret `s` and providing it to the `open_wallet` api function. The decrypted password is used on the server-side to unlock the wallet master seed, which is stored XORed against a randomly-generated token T. T is then returned to the client for use in further API calls alongside another randomly generated "next nonce" k, to be used in encryption of T during the next call: ![open_wallet](../assets/0000/0000-open-wallet.svg) -### Calling API functions in SecureAPI mode +#### Calling API functions in SecureAPI Mode Each call to the API function encrypts the token T with the DH-derived shared secret key added to a random nonce generated by the server during the previous API call. The token is XORed against the stored XORed seed to recover the original seed for each call, and the seed value is dropped and zeroed from memory when each API call returns. +Sensitive data, particularly new passwords and/or seed phrases that may be transmitted during API calls are also encrypted with **s**+**k** when used as either input parameters or return values. + ![api_call](../assets/0000/0000-api-call.svg) +### InsecureAPI Mode + +Calling the API in insecure mode is a simpler case of the above, where passwords, seed phrases and nonces are all sent in the clear. For consistency with the above exchange, an API Token is still returned during the initial call to the `open_wallet` function, and this token is used to store the XORed seed value internally. However this mode provides no protection against snooping attackers, and should **never** be used in a cross-machine configuration. +![unsafe-mode](../assets/0000/0000-unsafe-mode.svg) ## New API Functions From 497cc3cf67dc41b233ab209a58b62c401017e5e1 Mon Sep 17 00:00:00 2001 From: Yeastplume Date: Tue, 23 Jul 2019 08:25:45 +0100 Subject: [PATCH 09/15] Update API functions --- text/0000-full-wallet-lifecycle.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/text/0000-full-wallet-lifecycle.md b/text/0000-full-wallet-lifecycle.md index 38336ea..5cfcbb7 100644 --- a/text/0000-full-wallet-lifecycle.md +++ b/text/0000-full-wallet-lifecycle.md @@ -93,27 +93,29 @@ Calling the API in insecure mode is a simpler case of the above, where passwords * `OwnerAPI::set_wallet_directory(dir: String) -> Result<(), libwallet::Error>` - On API startup, it's assumed the top-level wallet data directory is `~/.grin/main/wallet_data` (or floonet equivalent) - Set the top-level system wallet directory from which named wallets are read. Further calls to lifecycle functions will use this wallet directory -* `OwnerAPI::create_config(data_dir: Option, config_overrides: Option) -> Result<(), libwallet::Error>` +* `OwnerAPI::create_config(chain_type: &global::ChainTypes, config_overrides: Option) -> Result<(), libwallet::Error>` - Outputs a `grin-wallet.toml` file into current top-level system wallet directory - Optionally takes wallet configuration structure to override defaults in the grin-wallet.toml file -* `OwnerAPI::open_wallet(name: Option, password: String) -> Result<(), libwallet::Error>` - - Opens the wallet and sets it as the 'active' wallet. All further API commands will be performed against this wallet. - - The 'name' argument is included for future use, anticipating the inclusion of multiple wallets and seeds within a single top-level wallet directory. -* `OwnerAPI::create_wallet(name: Option, mnemonic: String, password: String) -> Result<(), libwallet::Error>` +* `OwnerAPI::create_wallet(name: Option, mnemonic: Option, mnemonic_length: usize, password: ZeroingString) -> Result<(), libwallet::Error>` - Creates and initializes a new wallet - Initializes seed from given mnemonic if given, random seed otherwise - Should error appropriately if the wallet already exists - The 'name' parameter is included for future use as in `open_wallet` above. -* `OwnerAPI::get_mnemonic() -> Result` +* `OwnerAPI::open_wallet(name: Option, password: String) -> Result<(), libwallet::Error>` + - Opens the wallet and sets it as the 'active' wallet. All further API commands will be performed against this wallet. + - The 'name' argument is included for future use, anticipating the inclusion of multiple wallets and seeds within a single top-level wallet directory. +* `OwnerAPI::close_wallet(&mut self) -> Result<(), libwallet::Error>` + - Closes the currently open wallet +* `OwnerAPI::get_mnemonic() -> Result` - Returns the mnemonic from the active, (open) wallet -* `OwnerAPI::change_password(old: String, new: String) -> Result<(), libwallet::Error>` +* `OwnerAPI::change_password(old: ZeroingString, new: ZeroingString) -> Result<(), libwallet::Error>` - Changes the password for the open wallet. This will essentially: - Close the wallet instance - Confirm the existing seed can be opened with the given password - Regenerate the `wallet.seed` file with the new password - Re-open the wallet instance - (Should this just operate on closed wallets instead?) -* `OwnerAPI::delete_wallet(name: Option, password: String) -> Result<(), libwallet::Error>` +* `OwnerAPI::delete_wallet(name: Option, password: ZeroingString) -> Result<(), libwallet::Error>` - Dangerous function that removes all wallet data - name argument reserved for future use @@ -143,7 +145,7 @@ Although this document doesn't attempt to outline implementation, a few notes to # Drawbacks [drawbacks]: #drawbacks -* Sending sensitive information such as passwords and mnemonics via the OwnerAPI is an obvious security concern. +* Security-critical information such as passwords and mnemonics are covered via the encryption in the above scheme, but sending slate information via the OwnerAPI has privacy concerns. # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives @@ -158,9 +160,7 @@ TBD # Unresolved questions [unresolved-questions]: #unresolved-questions -* Security implications of sending passwords and/or master seed mnemonics through the JSON-RPC API, and how to deal with this as securely as possible. -* Security implications of leaving master seed 'open' in memory (this is aleady a concern for most wallets, but there isn't a clear way to deal with this). -* Should upgrade mechanism to support multiple wallets just leave the default directory in place, to minimise the impact of disruption? +* Should all fields be encrypted in addition to password and seed fields (or perhaps just the slate) # Future possibilities [future-possibilities]: #future-possibilities From 018e4e581cd6fa08ad2d83909636539c97d09ec2 Mon Sep 17 00:00:00 2001 From: Yeastplume Date: Wed, 24 Jul 2019 11:59:37 +0100 Subject: [PATCH 10/15] Update security model to encrypt entire JSON-RPC request/response --- assets/0000/0000-api-call-direct.puml | 16 +++++++++ assets/0000/0000-api-call-direct.svg | 29 +++++++++++++++++ assets/0000/0000-api-call.puml | 38 ++++++++-------------- assets/0000/0000-api-call.svg | 40 +++++++++-------------- assets/0000/0000-dh-init.puml | 12 +++---- assets/0000/0000-dh-init.svg | 14 ++++---- assets/0000/0000-open-wallet-direct.puml | 19 +++++++++++ assets/0000/0000-open-wallet-direct.svg | 32 ++++++++++++++++++ assets/0000/0000-open-wallet.puml | 35 +++++++++++--------- assets/0000/0000-open-wallet.svg | 37 ++++++++++++--------- assets/0000/0000-unsafe-mode.puml | 28 ---------------- assets/0000/0000-unsafe-mode.svg | 41 ------------------------ text/0000-full-wallet-lifecycle.md | 25 ++++++++------- 13 files changed, 193 insertions(+), 173 deletions(-) create mode 100644 assets/0000/0000-api-call-direct.puml create mode 100644 assets/0000/0000-api-call-direct.svg create mode 100644 assets/0000/0000-open-wallet-direct.puml create mode 100644 assets/0000/0000-open-wallet-direct.svg delete mode 100644 assets/0000/0000-unsafe-mode.puml delete mode 100644 assets/0000/0000-unsafe-mode.svg diff --git a/assets/0000/0000-api-call-direct.puml b/assets/0000/0000-api-call-direct.puml new file mode 100644 index 0000000..dc81618 --- /dev/null +++ b/assets/0000/0000-api-call-direct.puml @@ -0,0 +1,16 @@ +@startuml +actor "Client" as client +participant "Internal Rust API" as rustapi +participant "Wallet Backend" as backend + +title Wallet API Call (Directly Linked) + +== API Call == +client -> client: Stored token **t** +client -> rustapi: call_api_function(**t**, **other_params**) +rustapi -> backend: retrieve_seed(**t**) +backend -> backend: calc(**t** ^ **seedT**) -> **seed** +rustapi -> backend: Internal Wallet Calls +backend -> backend: Drop **seed** +rustapi -> client: Return (**return_data**) +@enduml \ No newline at end of file diff --git a/assets/0000/0000-api-call-direct.svg b/assets/0000/0000-api-call-direct.svg new file mode 100644 index 0000000..334cb37 --- /dev/null +++ b/assets/0000/0000-api-call-direct.svg @@ -0,0 +1,29 @@ +Wallet API Call (Directly Linked)ClientClientInternal Rust APIInternal Rust APIWallet BackendWallet BackendAPI CallStored tokentcall_api_function(t,other_params)retrieve_seed(t)calc(t^seedT) ->seedInternal Wallet CallsDropseedReturn (return_data) \ No newline at end of file diff --git a/assets/0000/0000-api-call.puml b/assets/0000/0000-api-call.puml index 0338d2a..d74ad32 100644 --- a/assets/0000/0000-api-call.puml +++ b/assets/0000/0000-api-call.puml @@ -4,31 +4,21 @@ participant "JSON-RPC API (if used)" as jsonrpc participant "Internal Rust API" as rustapi participant "Wallet Backend" as backend -== Previous call == +title Wallet API Call via JSON-RPC -rustapi -> rustapi: Generate/store new nonce -> (**k**) -rustapi -> rustapi: Encrypt any sensitive call result data (**secret_data**, **s** + **k_prev**) -> **sdEnc** -rustapi -> rustapi: Encrypt (**k**, **s** + **k_prev**) -> (**kEnc**) -note left: 'open_wallet' returns **t|k**, all other api calls return **k** -rustapi -> jsonrpc: Return (**kEnc**, Result, **sdEnc**) -jsonrpc -> client: Return (**kEnc**, Result, **sdEnc**) -client -> client: decrypt(**kEnc**, **s** + **k_prev**) -> **k** -client -> client: decrypt(**sdEnc**, **s** + **k_prev**) -> **secret_data** -client -> client: Next call encrypts t as enc(**s** + **k**, **t**) == API Call == -client -> jsonrpc: Encrypt token (**s** + **k**, **t**) -> **tEnc** -client -> jsonrpc: store **k** as **k_prev** -client -> jsonrpc: Encrypt sensitive data (**s** + **k**, **secret_data**) -> **sdEnc** -client -> jsonrpc: api_call(**tEnc**, **sdEnc**) -jsonrpc -> rustapi: api_call(**tEnc**, **sdEnc**) -rustapi -> rustapi: dec(**tEnc**, **s** + **k**) -> **T** -rustapi -> rustapi: dec(**sdEnc**, **s** + **k**) -> **secret_data** -rustapi -> rustapi: store **k** as **k_prev** -rustapi -> backend: decode_seed(**T**) -backend -> backend: calculate (**T** ^ **SeedT**) -> **Seed** -rustapi -> backend: Further API calls -backend -> backend: Drop **Seed** -rustapi -> rustapi: Generate/store new nonce -> (**k**) -rustapi -> client: (Proceed as per 'previous call' section above) +client -> client: Supply stored token **t** in **req** as **req.t** +client -> client: enc(**req**, **s**) -> **reqEnc** +client -> jsonrpc: call_api_function(**reqEnc**) +jsonrpc -> jsonrpc: dec(**reqEnc**, **s**) -> **req** +jsonrpc -> rustapi: call_api_function(**req**) +rustapi -> backend: retrieve_seed(**req.t** as **t**) +backend -> backend: calc(**t** ^ **seedT**) -> **seed** +rustapi -> backend: Internal Wallet Calls +backend -> backend: Drop **seed** +rustapi -> jsonrpc: Return (**resp**) +jsonrpc -> jsonrpc: enc(**resp**, **s**) -> (**respEnc**) +jsonrpc -> client: Return (**respEnc**) +client -> client: dec(**s**, **respEnc**) -> **resp** @enduml \ No newline at end of file diff --git a/assets/0000/0000-api-call.svg b/assets/0000/0000-api-call.svg index 039aacd..6ac3690 100644 --- a/assets/0000/0000-api-call.svg +++ b/assets/0000/0000-api-call.svg @@ -1,37 +1,27 @@ -ClientClientJSON-RPC API (if used)JSON-RPC API (if used)Internal Rust APIInternal Rust APIWallet BackendWallet BackendPrevious callGenerate/store new nonce -> (k)Encrypt any sensitive call result data (secret_data,s+k_prev) ->sdEncEncrypt (k,s+k_prev) -> (kEnc)'open_wallet' returnst|k, all other api calls returnkReturn (kEnc, Result,sdEnc)Return (kEnc, Result,sdEnc)decrypt(kEnc,s+k_prev) ->kdecrypt(sdEnc,s+k_prev) ->secret_dataNext call encrypts t as enc(s+k,t)API CallEncrypt token (s+k,t) ->tEncstorekask_prevEncrypt sensitive data (s+k,secret_data) ->sdEncapi_call(tEnc,sdEnc)api_call(tEnc,sdEnc)dec(tEnc,s+k) ->Tdec(sdEnc,s+k) ->secret_datastorekask_prevdecode_seed(T)calculate (T^SeedT) ->SeedFurther API callsDropSeedGenerate/store new nonce -> (k)(Proceed as per 'previous call' section above) \ No newline at end of file diff --git a/assets/0000/0000-open-wallet.puml b/assets/0000/0000-open-wallet.puml index b7ccedc..741b641 100644 --- a/assets/0000/0000-open-wallet.puml +++ b/assets/0000/0000-open-wallet.puml @@ -1,21 +1,26 @@ @startuml actor "Client" as client -participant "JSON-RPC API (if used)" as jsonrpc +participant "JSON-RPC API" as jsonrpc participant "Internal Rust API" as rustapi participant "Wallet Backend" as backend + +title Opening a Wallet in Secure Mode via JSON-RPC API + == Open Wallet == -client -> client: Encrypt password (**pw**) with DH shared secret **s** -> **pwEnc** -client -> jsonrpc: Open Wallet (**pwEnc**) -jsonrpc -> rustapi: Open Wallet (**pwEnc**) -rustapi -> rustapi: Decrypt (**pwEnc**, **s**) -> (**pw**) -rustapi -> rustapi: Generate Token (**t**) -rustapi -> backend: Open Wallet(**t**, **pw**) -backend -> backend: Decrypt seed(**pw**) -> **Seed** -backend -> backend: Store Seed^Token (**seedT**) -rustapi -> rustapi: Generate random nonce -> (**k**) -rustapi -> rustapi: Encrypt (**T**|**k**, **s**) -> (**tkEnc**) -rustapi -> jsonrpc: Return (**tkEnc**) -jsonrpc -> client: Return (**tkEnc**) -client -> client: Decode(**s**, **tkEnc**) -> **t|k** -client -> client: Next call encrypts t as enc(**s** + **k**, **t**) +client -> client: Supply password field **pw** in **req** as **req.pw** +client -> client: enc(**req**, **s**) -> **reqEnc** +client -> jsonrpc: open_wallet(**reqEnc**) +jsonrpc -> jsonrpc: dec(**reqEnc**, **s**) -> **req** +jsonrpc -> rustapi: open_wallet(**req**) +rustapi -> rustapi: Generate Random Token (**t**) +rustapi -> backend: Open Wallet(**t**, **req.pw**) +backend -> backend: Decrypt seed(**req.pw**) -> **seed** +backend -> backend: Store **seed**^**t** -> (**seedT**) +rustapi -> rustapi: Include **t** in **resp** as **resp.t** +rustapi -> jsonrpc: Return (**resp**) +jsonrpc -> jsonrpc: enc(**resp**, **s**) -> (**respEnc**) +jsonrpc -> client: Return (**respEnc**) +client -> client: dec(**s**, **respEnc**) -> **resp** +client -> client: Store **resp.t** as **t** +client -> client: All further API requests contain **t** @enduml \ No newline at end of file diff --git a/assets/0000/0000-open-wallet.svg b/assets/0000/0000-open-wallet.svg index d355abc..52a39af 100644 --- a/assets/0000/0000-open-wallet.svg +++ b/assets/0000/0000-open-wallet.svg @@ -1,24 +1,29 @@ -ClientClientJSON-RPC API (if used)JSON-RPC API (if used)Internal Rust APIInternal Rust APIWallet BackendWallet BackendOpen WalletEncrypt password (pw) with DH shared secrets->pwEncOpen Wallet (pwEnc)Open Wallet (pwEnc)Decrypt (pwEnc,s) -> (pw)Generate Token (t)Open Wallet(t,pw)Decrypt seed(pw) ->SeedStore Seed^Token (seedT)Generate random nonce -> (k)Encrypt (T|k,s) -> (tkEnc)Return (tkEnc)Return (tkEnc)Decode(s,tkEnc) ->t|kNext call encrypts t as enc(s+k,t) \ No newline at end of file diff --git a/text/0000-full-wallet-lifecycle.md b/text/0000-full-wallet-lifecycle.md index 5cfcbb7..d2242c5 100644 --- a/text/0000-full-wallet-lifecycle.md +++ b/text/0000-full-wallet-lifecycle.md @@ -51,9 +51,9 @@ Note that this mode of operation is primarly intended for use over the JSON-RPC ### SecureAPI Mode -SecureAPI Mode consists of an ECDH key agreement followed by the establishment of an API Token that's used to XOR encrypt the wallet seed on the server side. The negotiated ECDH shared key is used to encrypt the initial password used during the first call to the `open_wallet` API function, which generates and returns a token to the client to be supplied during all subsequent API calls. The token is encrypted using a scheme whereby a new random nonce is generated during each API call, which both client and server will add to the original shared key for use in the next API call. This ensures that each instance of an encrypted Token passed in by the client is single-use, and a snooping attacker can not reuse an encrypted token. +SecureAPI Mode consists of an ECDH key agreement followed by the establishment of an API Token that's used to XOR encrypt the wallet seed on the server side. The negotiated ECDH shared key is used to encrypt all requests and responses between the client and the JSON-RPC layer, while the token must be included in all API requests to allow the wallet backend to decrypt the seed. 'Open' wallets store their in-memory seeds XORed against the token, which is temporarily XORed against the supplied token during each request to reproduce the master seed. -Q: Exact encryption parameters TBD +Q: Exact encryption algorithm TBD #### Security Mode Selection @@ -61,29 +61,32 @@ To initialize SecureAPI Mode, clients will generate an EC keypair using the secp ![init_api_secure](../assets/0000/0000-dh-init.svg) -Any wallets opened by the API will be closed on a call to `init_api_secure`. Once `init_api_secure` is called the API will assume that the next call to `open_wallet` will contain an encrypted password, and that that all subsequent API calls will be accompanied with a valid encrypted token derived during the call to the `open_wallet` function. This assumption will remain until the server process exits or a call to a corresponding `close_api_secure` function is called. +Once `init_api_secure` is called the API will assume that subsequent API requests will be encrypted with the shared secret, and that that all subsequent API calls (other than `open_wallet`) will be accompanied with a valid encrypted token derived during the call to the `open_wallet` function. This assumption will remain until the server process exits or a call to a corresponding `close_api_secure` function is called. -The shared secret can be refreshed by the client at any time with another call to `init_api_secure`. Closing a wallet via the `close_wallet` function does not regenerate the shared secret. +The shared secret can be refreshed by the client at any time with another call to `init_api_secure`. Closing a wallet via the `close_wallet` function does not regenerate the shared secret but does invalidate the token and drops the XORed seed from memory. + +##### Legacy support + +If `init_api_secure` is not called, the mode of operation for the JSON-RPC API will be assumed to work as currently, i.e. requests and responses are unencrypted, the wallet stores its full seed in-memory between requests and the providing of a Token with each request is not requred. However, the new lifecycle functions described in this RFC, which deal with highly sensitive data such as passwords and master keys, will not be available via the JSON-RPC API unless `init_api_secure` has been called. This setup should allow existing wallets to continue working as-is until a cutoff release for legacy mode is determined. #### Opening a Wallet in SecureAPI Mode -Opening a wallet in SecureAPI mode consists of encrypting the password with the original shared secret `s` and providing it to the `open_wallet` api function. The decrypted password is used on the server-side to unlock the wallet master seed, which is stored XORed against a randomly-generated token T. T is then returned to the client for use in further API calls alongside another randomly generated "next nonce" k, to be used in encryption of T during the next call: +Opening a wallet in SecureAPI mode consists of encrypting a request to `open_wallet` (which contains the wallet password) with the shared secret `s`. The request is decrypted in the JSON-RPC layer, and the password is used in the wallet backend to unlock the wallet master seed. The master seed is stored XORed against a randomly-generated token T, which is returned to the client in an encrypted response for inclusion in all further API calls. T is valid for the lifetime of the process, or until a corresponding call to `close_wallet`. ![open_wallet](../assets/0000/0000-open-wallet.svg) #### Calling API functions in SecureAPI Mode -Each call to the API function encrypts the token T with the DH-derived shared secret key added to a random nonce generated by the server during the previous API call. The token is XORed against the stored XORed seed to recover the original seed for each call, and the seed value is dropped and zeroed from memory when each API call returns. - -Sensitive data, particularly new passwords and/or seed phrases that may be transmitted during API calls are also encrypted with **s**+**k** when used as either input parameters or return values. +Calls to each API function proceed as per a call to `open_wallet`, however each encrypted request must contain the token provided by the `open_wallet` call. The token is XORed against the stored XORed seed to recover the original seed by the backend for the duration of each call, and the seed value is dropped and zeroed from memory when each call returns. ![api_call](../assets/0000/0000-api-call.svg) -### InsecureAPI Mode +### Directly Linked Wallets -Calling the API in insecure mode is a simpler case of the above, where passwords, seed phrases and nonces are all sent in the clear. For consistency with the above exchange, an API Token is still returned during the initial call to the `open_wallet` function, and this token is used to store the XORed seed value internally. However this mode provides no protection against snooping attackers, and should **never** be used in a cross-machine configuration. +Wallets that link the wallet API directly will not be required to encrypt parameters, as there would be little benefit to doing so within a single process. However, for consistency they will be expected to store and supply a token to each API call. The modified workflow for a linked wallet is outlined below: -![unsafe-mode](../assets/0000/0000-unsafe-mode.svg) +![open-wallet-direct](../assets/0000/0000-open-wallet-direct.svg) +![api-call-direct](../assets/0000/0000-api-call-direct.svg) ## New API Functions From 5fb50a33c76df9da09321a85c344f0e029367525 Mon Sep 17 00:00:00 2001 From: Yeastplume Date: Wed, 24 Jul 2019 12:01:57 +0100 Subject: [PATCH 11/15] text on direct-linked mode not having a legacy mode --- text/0000-full-wallet-lifecycle.md | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/text/0000-full-wallet-lifecycle.md b/text/0000-full-wallet-lifecycle.md index d2242c5..db93f88 100644 --- a/text/0000-full-wallet-lifecycle.md +++ b/text/0000-full-wallet-lifecycle.md @@ -88,10 +88,9 @@ Wallets that link the wallet API directly will not be required to encrypt parame ![open-wallet-direct](../assets/0000/0000-open-wallet-direct.svg) ![api-call-direct](../assets/0000/0000-api-call-direct.svg) +'Legacy' support will not be provided for directly-linked wallets on release of the features described in this RFC. Is is expected wallet authors will need to update their code to store and supply the token with each request. -## New API Functions - -(Needs updating) +## New Lifecycle API Functions * `OwnerAPI::set_wallet_directory(dir: String) -> Result<(), libwallet::Error>` - On API startup, it's assumed the top-level wallet data directory is `~/.grin/main/wallet_data` (or floonet equivalent) @@ -104,12 +103,12 @@ Wallets that link the wallet API directly will not be required to encrypt parame - Initializes seed from given mnemonic if given, random seed otherwise - Should error appropriately if the wallet already exists - The 'name' parameter is included for future use as in `open_wallet` above. -* `OwnerAPI::open_wallet(name: Option, password: String) -> Result<(), libwallet::Error>` +* `OwnerAPI::open_wallet(name: Option, password: String) -> Result` - Opens the wallet and sets it as the 'active' wallet. All further API commands will be performed against this wallet. - The 'name' argument is included for future use, anticipating the inclusion of multiple wallets and seeds within a single top-level wallet directory. * `OwnerAPI::close_wallet(&mut self) -> Result<(), libwallet::Error>` - Closes the currently open wallet -* `OwnerAPI::get_mnemonic() -> Result` +* `OwnerAPI::get_mnemonic(t:Token) -> Result` - Returns the mnemonic from the active, (open) wallet * `OwnerAPI::change_password(old: ZeroingString, new: ZeroingString) -> Result<(), libwallet::Error>` - Changes the password for the open wallet. This will essentially: @@ -150,20 +149,10 @@ Although this document doesn't attempt to outline implementation, a few notes to * Security-critical information such as passwords and mnemonics are covered via the encryption in the above scheme, but sending slate information via the OwnerAPI has privacy concerns. -# Rationale and alternatives -[rationale-and-alternatives]: #rationale-and-alternatives - -TBD - -# Prior art -[prior-art]: #prior-art - -TBD - # Unresolved questions [unresolved-questions]: #unresolved-questions -* Should all fields be encrypted in addition to password and seed fields (or perhaps just the slate) +* Is there a potential for replay attacks? # Future possibilities [future-possibilities]: #future-possibilities From 7296df21af96bd214d3f0a250b634b11f5b446cf Mon Sep 17 00:00:00 2001 From: Yeastplume Date: Mon, 29 Jul 2019 13:19:01 +0100 Subject: [PATCH 12/15] update encryption standards, open question about Foreign API --- text/0000-full-wallet-lifecycle.md | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/text/0000-full-wallet-lifecycle.md b/text/0000-full-wallet-lifecycle.md index db93f88..93e04e0 100644 --- a/text/0000-full-wallet-lifecycle.md +++ b/text/0000-full-wallet-lifecycle.md @@ -53,7 +53,7 @@ Note that this mode of operation is primarly intended for use over the JSON-RPC SecureAPI Mode consists of an ECDH key agreement followed by the establishment of an API Token that's used to XOR encrypt the wallet seed on the server side. The negotiated ECDH shared key is used to encrypt all requests and responses between the client and the JSON-RPC layer, while the token must be included in all API requests to allow the wallet backend to decrypt the seed. 'Open' wallets store their in-memory seeds XORed against the token, which is temporarily XORed against the supplied token during each request to reproduce the master seed. -Q: Exact encryption algorithm TBD +ECDH will use secp256k1 for key agreement, while encryption of JSON-RPC requests and responses will be performed via the AES256 Encryption standard. #### Security Mode Selection @@ -92,6 +92,11 @@ Wallets that link the wallet API directly will not be required to encrypt parame ## New Lifecycle API Functions +The functions as shown here are for illustrative purposes, and their signatures will change during implementation. + +* `OwnerAPI::init_api_secure(pubkey: Secp256k1Point) -> Result + - Initializes secure API mode, returning the server-side public key to be used for key agreement + - All further calls to the JSON-RPC API must be encrypted with the shared secret * `OwnerAPI::set_wallet_directory(dir: String) -> Result<(), libwallet::Error>` - On API startup, it's assumed the top-level wallet data directory is `~/.grin/main/wallet_data` (or floonet equivalent) - Set the top-level system wallet directory from which named wallets are read. Further calls to lifecycle functions will use this wallet directory @@ -104,10 +109,11 @@ Wallets that link the wallet API directly will not be required to encrypt parame - Should error appropriately if the wallet already exists - The 'name' parameter is included for future use as in `open_wallet` above. * `OwnerAPI::open_wallet(name: Option, password: String) -> Result` - - Opens the wallet and sets it as the 'active' wallet. All further API commands will be performed against this wallet. + - Opens a wallet and sets it as the 'active' wallet. All further API commands will be performed against this wallet. + - 'Opens' the wallet seed in memory, stored XORd against a new token. The token is to be returned to the client for use in all further API calls. - The 'name' argument is included for future use, anticipating the inclusion of multiple wallets and seeds within a single top-level wallet directory. * `OwnerAPI::close_wallet(&mut self) -> Result<(), libwallet::Error>` - - Closes the currently open wallet + - Closes the currently open wallet (i.e. drops the XORed seed from memory) * `OwnerAPI::get_mnemonic(t:Token) -> Result` - Returns the mnemonic from the active, (open) wallet * `OwnerAPI::change_password(old: ZeroingString, new: ZeroingString) -> Result<(), libwallet::Error>` @@ -121,17 +127,6 @@ Wallets that link the wallet API directly will not be required to encrypt parame - Dangerous function that removes all wallet data - name argument reserved for future use -### Use cases - -* First time use (API Case) - - run `grin-wallet owner_api` - - top-level data directory is set to `~/.grin/main/wallet_data` (but nothing is yet written) - - `create_config` called to create `grin-wallet.toml` - - `create_wallet` called (with name == None) and seed to create a new wallet and seed, wallet data is created and initialized - -* Recover from seed - - As above, except call `create_wallet` with mnemonic seed instead - ### API only Note that this RFC does not propose making user-facing changes to the existing CLI wallet to invoke these functions. It's expected that the existing cli functionality will be modified to invoke the new API functions. @@ -152,7 +147,7 @@ Although this document doesn't attempt to outline implementation, a few notes to # Unresolved questions [unresolved-questions]: #unresolved-questions -* Is there a potential for replay attacks? +* Due to how this is likely to be implemented, the Foreign API will also have to provide a token for all wallet access. The Foreign API will need to store this token in-process, therefore negating much of the benefit of the scheme. Is there a cleverer way to deal with this? # Future possibilities [future-possibilities]: #future-possibilities From 62852116bcb8dcff329f9399b4378e0e00da88ff Mon Sep 17 00:00:00 2001 From: Yeastplume Date: Wed, 21 Aug 2019 09:29:20 +0100 Subject: [PATCH 13/15] update with detail from implementation, typo fixes, assign RFC number for merge --- ...e-diagram.puml => 0004-state-diagram.puml} | 0 ...ate-diagram.svg => 0004-state-diagram.svg} | 0 .../{0000 => 0004}/0000-api-call-direct.puml | 0 .../{0000 => 0004}/0000-api-call-direct.svg | 0 assets/{0000 => 0004}/0000-api-call.puml | 0 assets/{0000 => 0004}/0000-api-call.svg | 0 assets/{0000 => 0004}/0000-dh-init.puml | 0 assets/{0000 => 0004}/0000-dh-init.svg | 0 .../0000-open-wallet-direct.puml | 0 .../0000-open-wallet-direct.svg | 0 assets/{0000 => 0004}/0000-open-wallet.puml | 0 assets/{0000 => 0004}/0000-open-wallet.svg | 0 ...cycle.md => 0004-full-wallet-lifecycle.md} | 58 ++++++++++++------- 13 files changed, 38 insertions(+), 20 deletions(-) rename assets/{0001-state-diagram.puml => 0004-state-diagram.puml} (100%) rename assets/{0001-state-diagram.svg => 0004-state-diagram.svg} (100%) rename assets/{0000 => 0004}/0000-api-call-direct.puml (100%) rename assets/{0000 => 0004}/0000-api-call-direct.svg (100%) rename assets/{0000 => 0004}/0000-api-call.puml (100%) rename assets/{0000 => 0004}/0000-api-call.svg (100%) rename assets/{0000 => 0004}/0000-dh-init.puml (100%) rename assets/{0000 => 0004}/0000-dh-init.svg (100%) rename assets/{0000 => 0004}/0000-open-wallet-direct.puml (100%) rename assets/{0000 => 0004}/0000-open-wallet-direct.svg (100%) rename assets/{0000 => 0004}/0000-open-wallet.puml (100%) rename assets/{0000 => 0004}/0000-open-wallet.svg (100%) rename text/{0000-full-wallet-lifecycle.md => 0004-full-wallet-lifecycle.md} (65%) diff --git a/assets/0001-state-diagram.puml b/assets/0004-state-diagram.puml similarity index 100% rename from assets/0001-state-diagram.puml rename to assets/0004-state-diagram.puml diff --git a/assets/0001-state-diagram.svg b/assets/0004-state-diagram.svg similarity index 100% rename from assets/0001-state-diagram.svg rename to assets/0004-state-diagram.svg diff --git a/assets/0000/0000-api-call-direct.puml b/assets/0004/0000-api-call-direct.puml similarity index 100% rename from assets/0000/0000-api-call-direct.puml rename to assets/0004/0000-api-call-direct.puml diff --git a/assets/0000/0000-api-call-direct.svg b/assets/0004/0000-api-call-direct.svg similarity index 100% rename from assets/0000/0000-api-call-direct.svg rename to assets/0004/0000-api-call-direct.svg diff --git a/assets/0000/0000-api-call.puml b/assets/0004/0000-api-call.puml similarity index 100% rename from assets/0000/0000-api-call.puml rename to assets/0004/0000-api-call.puml diff --git a/assets/0000/0000-api-call.svg b/assets/0004/0000-api-call.svg similarity index 100% rename from assets/0000/0000-api-call.svg rename to assets/0004/0000-api-call.svg diff --git a/assets/0000/0000-dh-init.puml b/assets/0004/0000-dh-init.puml similarity index 100% rename from assets/0000/0000-dh-init.puml rename to assets/0004/0000-dh-init.puml diff --git a/assets/0000/0000-dh-init.svg b/assets/0004/0000-dh-init.svg similarity index 100% rename from assets/0000/0000-dh-init.svg rename to assets/0004/0000-dh-init.svg diff --git a/assets/0000/0000-open-wallet-direct.puml b/assets/0004/0000-open-wallet-direct.puml similarity index 100% rename from assets/0000/0000-open-wallet-direct.puml rename to assets/0004/0000-open-wallet-direct.puml diff --git a/assets/0000/0000-open-wallet-direct.svg b/assets/0004/0000-open-wallet-direct.svg similarity index 100% rename from assets/0000/0000-open-wallet-direct.svg rename to assets/0004/0000-open-wallet-direct.svg diff --git a/assets/0000/0000-open-wallet.puml b/assets/0004/0000-open-wallet.puml similarity index 100% rename from assets/0000/0000-open-wallet.puml rename to assets/0004/0000-open-wallet.puml diff --git a/assets/0000/0000-open-wallet.svg b/assets/0004/0000-open-wallet.svg similarity index 100% rename from assets/0000/0000-open-wallet.svg rename to assets/0004/0000-open-wallet.svg diff --git a/text/0000-full-wallet-lifecycle.md b/text/0004-full-wallet-lifecycle.md similarity index 65% rename from text/0000-full-wallet-lifecycle.md rename to text/0004-full-wallet-lifecycle.md index 93e04e0..a239064 100644 --- a/text/0000-full-wallet-lifecycle.md +++ b/text/0004-full-wallet-lifecycle.md @@ -1,7 +1,7 @@ - Title: full-wallet-lifecycle - Authors: [Michael Cordner](mailto:yeastplume@protonmail.com) - Start date : June 26th, 2019 -- RFC PR: Edit if merged: [mimblewimble/grin-rfcs#0000](https://github.com/mimblewimble/grin-rfcs/pull/0000) +- RFC PR: [mimblewimble/grin-rfcs#0004](https://github.com/mimblewimble/grin-rfcs/pull/18) - Tracking issue: [Edit if merged with link to tracking github issue] --- @@ -43,58 +43,76 @@ It should be possible to run `grin-wallet owner_api` or invoke the API directly Given that the Wallet's Owner API needs to deal with sensitive data such as passwords and seed phrases, the API will be enhanced with a new ECDH and Token-based security model, the primary goals of which are to: -* Ensure sensitive data such as passwords or seed phrases are always end-to-end encrypted between the client and Owner API server, regardless of what higher-level protocols are used during the exchange. +* Ensure sensitive data such as passwords or seed phrases are always end-to-end encrypted between the client and the Owner API server, regardless of what higher-level protocols are used during the exchange. * Minimize the potential for damage that can be done by a third party listening on the exchange between a wallet client and its corresponding server. * Ensure that sensitive data such as passwords or seed phrases are not resident in server-side memory any longer than they absolutely need to be. -Note that this mode of operation is primarly intended for use over the JSON-RPC API, which supports many different architectural possiblities. Clients that link libraries directly and keep all sensitive data in the same process would see less benefit from this scheme, and an alternative model which doesn't encrypt any sensitive data is provided. Further, authors of existing wallets will need time to consider and/or implement the added complexity needed on the client-side to support ECDH and encryption. It's therefore proposed that the Owner API initially provide the new "SecureAPI" mode as an optional feature, with wallet authors strongly encouraged to make use of it. Support for the "InsecureAPI" model can be maintained indefinitely for directly-linked wallets, and for the JSON-RPC API until a cut-off release at some point in the future. +Note that this mode of operation is primarily intended for use over the JSON-RPC API, which supports many different architectural possiblities. Clients that link libraries directly and keep all sensitive data in the same process would see less benefit from this scheme, and an alternative model which doesn't encrypt any sensitive data is provided. Further, authors of existing wallets will need time to consider and/or implement the added complexity needed on the client-side to support ECDH and encryption. It's therefore proposed that the Owner API initially provide the new "SecureAPI" mode as an optional feature, with wallet authors strongly encouraged to make use of it. Support for the "InsecureAPI" model can be maintained indefinitely for directly-linked wallets, and for the JSON-RPC API until a cut-off release at some point in the future. + +Note that the "SecureAPI" mode and all lifecycle functions will be implemented in a V3 API, with the V2 API maintained for a time for backwards compatibility. The V3 API requires all JSON-RPC communication to be encrypted, with the exception of the `init_secure_api` function. ### SecureAPI Mode SecureAPI Mode consists of an ECDH key agreement followed by the establishment of an API Token that's used to XOR encrypt the wallet seed on the server side. The negotiated ECDH shared key is used to encrypt all requests and responses between the client and the JSON-RPC layer, while the token must be included in all API requests to allow the wallet backend to decrypt the seed. 'Open' wallets store their in-memory seeds XORed against the token, which is temporarily XORed against the supplied token during each request to reproduce the master seed. -ECDH will use secp256k1 for key agreement, while encryption of JSON-RPC requests and responses will be performed via the AES256 Encryption standard. +ECDH will use secp256k1 for key agreement. + +Encryption of JSON-RPC requests and responses will be performed using AEAD in GCM mode with 128-bit tags, 96 bit nonces, a 16 byte suffix length and an empty vector for the additional data. A 12 byte nonce will be applied in the encryption and included in each request/response to use o n the decrypting side. + +Encrypted requests and responses will be exchanged in valid JSON-RPC calls with the method "encrypted_request_v3" (with 'v3' here denoting the version of the API). They will have the following form: + +``` +{ + "jsonrpc": "2.0", + "method: "encrypted_request_v3", + "id": "1", + "params": { + "nonce": "ef32...", + "body_enc": "e0bcd..." + } +} +``` -#### Security Mode Selection +#### Security Mode Initialization -To initialize SecureAPI Mode, clients will generate an EC keypair using the secp256k1 curve, and provide the public key to the Owner API server via a new `init_api_secure` method. Both client and server will calculate the shared key, and store this key for the remainder of the session. The sequence of operations is outlined below: +To initialize the Secure API, clients will generate an EC keypair using the secp256k1 curve, and provide the public key to the Owner API server via a new `init_secure_api` method. Both client and server will calculate the shared key, and store this key for the remainder of the session. The sequence of operations is outlined below: -![init_api_secure](../assets/0000/0000-dh-init.svg) +![init_secure_api](../assets/0004/0004-dh-init.svg) -Once `init_api_secure` is called the API will assume that subsequent API requests will be encrypted with the shared secret, and that that all subsequent API calls (other than `open_wallet`) will be accompanied with a valid encrypted token derived during the call to the `open_wallet` function. This assumption will remain until the server process exits or a call to a corresponding `close_api_secure` function is called. +The Secure API assumes that all requests (other than the actual call to `init_secure_api` itself) will be encrypted with the shared secret and presented in the above JSON-RPC format. All API calls other than `open_wallet` will be accompanied with a valid encrypted token derived during the call to the `open_wallet` function. This assumption will remain until the server process exits or a call to a corresponding `close_api_secure` function is called. -The shared secret can be refreshed by the client at any time with another call to `init_api_secure`. Closing a wallet via the `close_wallet` function does not regenerate the shared secret but does invalidate the token and drops the XORed seed from memory. +The shared secret can be refreshed by the client at any time with another call to `init_secure_api` (either encrypted or unencrypted). Closing a wallet via the `close_wallet` function does not regenerate the shared secret but does invalidate the token and drops the XORed seed from memory. ##### Legacy support -If `init_api_secure` is not called, the mode of operation for the JSON-RPC API will be assumed to work as currently, i.e. requests and responses are unencrypted, the wallet stores its full seed in-memory between requests and the providing of a Token with each request is not requred. However, the new lifecycle functions described in this RFC, which deal with highly sensitive data such as passwords and master keys, will not be available via the JSON-RPC API unless `init_api_secure` has been called. This setup should allow existing wallets to continue working as-is until a cutoff release for legacy mode is determined. +The V2 API will remain active for a time the mode of operation for its JSON-RPC API will be assumed to work as currently, i.e. requests and responses are unencrypted, the wallet stores its full seed in-memory between requests and the providing of a token with each request is not requred. However, the new lifecycle functions described in this RFC, which deal with highly sensitive data such as passwords and master keys, will not be available in the V2 API. This setup should allow existing wallets to continue working as-is until a cutoff release for legacy mode is determined. #### Opening a Wallet in SecureAPI Mode -Opening a wallet in SecureAPI mode consists of encrypting a request to `open_wallet` (which contains the wallet password) with the shared secret `s`. The request is decrypted in the JSON-RPC layer, and the password is used in the wallet backend to unlock the wallet master seed. The master seed is stored XORed against a randomly-generated token T, which is returned to the client in an encrypted response for inclusion in all further API calls. T is valid for the lifetime of the process, or until a corresponding call to `close_wallet`. +Opening a wallet in SecureAPI mode consists of encrypting a request to `open_wallet` (which contains the wallet password) with the shared secret `s`. The request is decrypted in the JSON-RPC layer and the password is used in the wallet backend to unlock the wallet master seed. The master seed is stored XORed against a randomly-generated token T, which is returned to the client in an encrypted response for inclusion in all further API calls. T is valid for the lifetime of the process, or until a corresponding call to `close_wallet`. -![open_wallet](../assets/0000/0000-open-wallet.svg) +![open_wallet](../assets/0004/0004-open-wallet.svg) #### Calling API functions in SecureAPI Mode Calls to each API function proceed as per a call to `open_wallet`, however each encrypted request must contain the token provided by the `open_wallet` call. The token is XORed against the stored XORed seed to recover the original seed by the backend for the duration of each call, and the seed value is dropped and zeroed from memory when each call returns. -![api_call](../assets/0000/0000-api-call.svg) +![api_call](../assets/0004/0004-api-call.svg) ### Directly Linked Wallets -Wallets that link the wallet API directly will not be required to encrypt parameters, as there would be little benefit to doing so within a single process. However, for consistency they will be expected to store and supply a token to each API call. The modified workflow for a linked wallet is outlined below: +Wallets that link the wallet API directly will not be required to encrypt parameters, as there would be little benefit to doing so within a single process. However, for consistency, they will be expected to store and supply a token to each API call. The modified workflow for a linked wallet is outlined below: -![open-wallet-direct](../assets/0000/0000-open-wallet-direct.svg) -![api-call-direct](../assets/0000/0000-api-call-direct.svg) +![open-wallet-direct](../assets/0004/0004-open-wallet-direct.svg) +![api-call-direct](../assets/0004/0004-api-call-direct.svg) -'Legacy' support will not be provided for directly-linked wallets on release of the features described in this RFC. Is is expected wallet authors will need to update their code to store and supply the token with each request. +'Legacy' support will not be provided for directly-linked wallets on release of the features described in this RFC. It is expected that wallet authors will need to update their code to store and supply the token with each request. ## New Lifecycle API Functions The functions as shown here are for illustrative purposes, and their signatures will change during implementation. -* `OwnerAPI::init_api_secure(pubkey: Secp256k1Point) -> Result +* `OwnerAPI::init_secure_api(pubkey: Secp256k1Point) -> Result` - Initializes secure API mode, returning the server-side public key to be used for key agreement - All further calls to the JSON-RPC API must be encrypted with the shared secret * `OwnerAPI::set_wallet_directory(dir: String) -> Result<(), libwallet::Error>` @@ -110,7 +128,7 @@ The functions as shown here are for illustrative purposes, and their signatures - The 'name' parameter is included for future use as in `open_wallet` above. * `OwnerAPI::open_wallet(name: Option, password: String) -> Result` - Opens a wallet and sets it as the 'active' wallet. All further API commands will be performed against this wallet. - - 'Opens' the wallet seed in memory, stored XORd against a new token. The token is to be returned to the client for use in all further API calls. + - 'Opens' the wallet seed in memory, stored XORed against a new token. The token is to be returned to the client for use in all further API calls. - The 'name' argument is included for future use, anticipating the inclusion of multiple wallets and seeds within a single top-level wallet directory. * `OwnerAPI::close_wallet(&mut self) -> Result<(), libwallet::Error>` - Closes the currently open wallet (i.e. drops the XORed seed from memory) @@ -135,7 +153,7 @@ Note that this RFC does not propose making user-facing changes to the existing C Although this document doesn't attempt to outline implementation, a few notes to consider for the implementor: -* Currently, the code that deals with wallet initialization and seed management sits outside the wallet APIs, in the `impls` crate, (denoting they're implementation specific). The implementation should attempt to refactor traits from these hard implementations into a new interface, similar to the existing WalletBackend and NodeClient interfaces (WalletLifecycleManager, for instance). The implementation within `impls` will then become an implementation of that trait, and can be substituted by wallet authors with their own implementations. +* Currently, the code that deals with wallet initialization and seed management sits outside the wallet APIs, in the `impls` crate, (denoting they're implementation specific). The implementation should attempt to refactor traits from these hard implementations into a new interface, similar to the existing WalletBackend and NodeClient interfaces (WalletLifecycleManager, for instance). The implementation within `impls` will then become an implementation of that trait and can be substituted by wallet authors with their own implementations. * The implementation period of this RFC may be a good time to remove the BIP32 specific code out from Grin core into the wallet or into a separate rust crate (probably more desirable). * New API functions should be implemented as additions, with the new features optional to ensure complete backwards compatibility From 7e3811df5c528d69239814cbd877abb19129b6df Mon Sep 17 00:00:00 2001 From: Yeastplume Date: Wed, 21 Aug 2019 09:32:22 +0100 Subject: [PATCH 14/15] rename assets --- assets/{0004-state-diagram.puml => 0001-state-diagram.puml} | 0 assets/{0004-state-diagram.svg => 0001-state-diagram.svg} | 0 .../0004/{0000-api-call-direct.puml => 0004-api-call-direct.puml} | 0 .../0004/{0000-api-call-direct.svg => 0004-api-call-direct.svg} | 0 assets/0004/{0000-api-call.puml => 0004-api-call.puml} | 0 assets/0004/{0000-api-call.svg => 0004-api-call.svg} | 0 assets/0004/{0000-dh-init.puml => 0004-dh-init.puml} | 0 assets/0004/{0000-dh-init.svg => 0004-dh-init.svg} | 0 ...{0000-open-wallet-direct.puml => 0004-open-wallet-direct.puml} | 0 .../{0000-open-wallet-direct.svg => 0004-open-wallet-direct.svg} | 0 assets/0004/{0000-open-wallet.puml => 0004-open-wallet.puml} | 0 assets/0004/{0000-open-wallet.svg => 0004-open-wallet.svg} | 0 12 files changed, 0 insertions(+), 0 deletions(-) rename assets/{0004-state-diagram.puml => 0001-state-diagram.puml} (100%) rename assets/{0004-state-diagram.svg => 0001-state-diagram.svg} (100%) rename assets/0004/{0000-api-call-direct.puml => 0004-api-call-direct.puml} (100%) rename assets/0004/{0000-api-call-direct.svg => 0004-api-call-direct.svg} (100%) rename assets/0004/{0000-api-call.puml => 0004-api-call.puml} (100%) rename assets/0004/{0000-api-call.svg => 0004-api-call.svg} (100%) rename assets/0004/{0000-dh-init.puml => 0004-dh-init.puml} (100%) rename assets/0004/{0000-dh-init.svg => 0004-dh-init.svg} (100%) rename assets/0004/{0000-open-wallet-direct.puml => 0004-open-wallet-direct.puml} (100%) rename assets/0004/{0000-open-wallet-direct.svg => 0004-open-wallet-direct.svg} (100%) rename assets/0004/{0000-open-wallet.puml => 0004-open-wallet.puml} (100%) rename assets/0004/{0000-open-wallet.svg => 0004-open-wallet.svg} (100%) diff --git a/assets/0004-state-diagram.puml b/assets/0001-state-diagram.puml similarity index 100% rename from assets/0004-state-diagram.puml rename to assets/0001-state-diagram.puml diff --git a/assets/0004-state-diagram.svg b/assets/0001-state-diagram.svg similarity index 100% rename from assets/0004-state-diagram.svg rename to assets/0001-state-diagram.svg diff --git a/assets/0004/0000-api-call-direct.puml b/assets/0004/0004-api-call-direct.puml similarity index 100% rename from assets/0004/0000-api-call-direct.puml rename to assets/0004/0004-api-call-direct.puml diff --git a/assets/0004/0000-api-call-direct.svg b/assets/0004/0004-api-call-direct.svg similarity index 100% rename from assets/0004/0000-api-call-direct.svg rename to assets/0004/0004-api-call-direct.svg diff --git a/assets/0004/0000-api-call.puml b/assets/0004/0004-api-call.puml similarity index 100% rename from assets/0004/0000-api-call.puml rename to assets/0004/0004-api-call.puml diff --git a/assets/0004/0000-api-call.svg b/assets/0004/0004-api-call.svg similarity index 100% rename from assets/0004/0000-api-call.svg rename to assets/0004/0004-api-call.svg diff --git a/assets/0004/0000-dh-init.puml b/assets/0004/0004-dh-init.puml similarity index 100% rename from assets/0004/0000-dh-init.puml rename to assets/0004/0004-dh-init.puml diff --git a/assets/0004/0000-dh-init.svg b/assets/0004/0004-dh-init.svg similarity index 100% rename from assets/0004/0000-dh-init.svg rename to assets/0004/0004-dh-init.svg diff --git a/assets/0004/0000-open-wallet-direct.puml b/assets/0004/0004-open-wallet-direct.puml similarity index 100% rename from assets/0004/0000-open-wallet-direct.puml rename to assets/0004/0004-open-wallet-direct.puml diff --git a/assets/0004/0000-open-wallet-direct.svg b/assets/0004/0004-open-wallet-direct.svg similarity index 100% rename from assets/0004/0000-open-wallet-direct.svg rename to assets/0004/0004-open-wallet-direct.svg diff --git a/assets/0004/0000-open-wallet.puml b/assets/0004/0004-open-wallet.puml similarity index 100% rename from assets/0004/0000-open-wallet.puml rename to assets/0004/0004-open-wallet.puml diff --git a/assets/0004/0000-open-wallet.svg b/assets/0004/0004-open-wallet.svg similarity index 100% rename from assets/0004/0000-open-wallet.svg rename to assets/0004/0004-open-wallet.svg From 4dcc0d315c3d3455c43d43c9b8be378a5bad516c Mon Sep 17 00:00:00 2001 From: Yeastplume Date: Wed, 21 Aug 2019 09:36:11 +0100 Subject: [PATCH 15/15] update index --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a16ca3e..27aafc8 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ To begin writing your own RFC or to find out more about the process and the gene |:---|:---| | [0001-rfc-process](text/0001-rfc-process.md) | Introduce RFC process | | [0002-grin-governance](text/0002-grin-governance.md) | Articulate community values, define core and sub-teams | +| [0003-security-process](text/0003-security-process.md) | Define community standards for ethical disclosure behaviour | +| [0004-full-wallet-lifecycle](text/0004-full-wallet-lifecycle.md) | Define API standard for sensitive wallet operations | ## License