184 lines
11 KiB
Markdown
184 lines
11 KiB
Markdown
BSIP: 0040
|
|
Title: Custom active permissions
|
|
Authors:
|
|
Alex Megalokonomos <https://github.com/clockworkgr>
|
|
Fabian Schuh <https://github.com/xeroc>
|
|
Stefan Schießl <https://github.com/sschiessl-bcp>
|
|
Status: Draft
|
|
Type: Protocol
|
|
Created: 2018-07-25
|
|
Discussion: https://github.com/bitshares/bitshares-core/issues/1061
|
|
Worker: <Id of worker proposal>
|
|
|
|
# Abstract
|
|
|
|
Strengthening user security is one of the main factors to elevate BitShares. In light of recent
|
|
hacking and phishing attempts this becomes even more important. The need for a more sophisticated
|
|
account security preceeded the idea for a finer-grained control of account permissions.
|
|
We propose to add an additional authority to the account, called Custom Active (Permission). The
|
|
permission contains a list of operationid-to-authority mappings that each grant access to the respective
|
|
operation as if it were the active permission of the account. Additionally, the arguments of said operation
|
|
can be restricted.
|
|
|
|
# Motivation
|
|
|
|
Any successfull hacking or phishing attempt on any of the web wallets that are powered by the
|
|
BitShares Blockchain is bad publicity. The user needs to be educated in account security, and this BSIP
|
|
aims to ensure all technical possibilities are met while being flexible to allow many use-cases.
|
|
|
|
With this BSIP any user can create keys with specific purpose (everything else is prohibited). We list some possibilities below:
|
|
- Witness Key: Only allows update signing key and publish price feed
|
|
- Trading Key: Only allows limit orders (arguments restricted to desired markets), update margin position and transfers (arguments restricted to certain accounts)
|
|
- Proposal Update Key: Approve proposals (2FA comes to mind)
|
|
- Faucet Key: Allow only to create accounts
|
|
- Withdrawal Key: Allow another account to transfer funds to himself
|
|
- Cold Storage Key: Only allow to move funds to the Hot Wallet
|
|
|
|
The above list of named keys is nothing that is known to the backend as the backend should have an abstract implementation.
|
|
The UI could provide a button "Create Trading Key" that properly configures the respective custom active permission entry.
|
|
|
|
# Rational
|
|
|
|
Custom active permission is a list of `custom active authorities`. A `custom active authority` contains an `operation_id`, an `authority` (just like with active permission) and `asserts` than can be used to restrict arguments and is only valid a certain time period (`valid_from` and `valid_to`). When handling incoming signed transactions, the backend checks for each operation if there is a `custom active authority` for any of its required accounts. Check for every required account of the transaction if all its belonging operations have at least one positively matched `custom active authority` (match means same `operationid`, now is within `valid_to` and `valid_from` and all `asserts` pass), and if so grant the active authority of the corresponding account.
|
|
|
|
# Specification
|
|
|
|
All descriptions in this section are on a pseudo/prosa level and no recommendation how it can best be implemented or serialized. They are meant to facilitate the understanding.
|
|
|
|
### Custom active permission and custom active authority
|
|
|
|
A `custom_active_permission` looks like follows (in JSON/pseudo for clarification):
|
|
```
|
|
custom_active_permission = list of custom_active_authority items
|
|
custom_active_authority = {
|
|
valid_from, // timestamp when this is active, defaults to now
|
|
valid_to, // timestamp when this is invalid, defaults to 1 month
|
|
operationid, // operationid of the target operation,
|
|
authority, // same as for the existing authortities (multisig with weighted accounts or keys),
|
|
asserts // see below
|
|
}
|
|
```
|
|
Note: This assumes `custom_active_permission` is stored within `account_object`. Actual implementation details left to the implementer, as long as every `custom_active_permission` can be assigned to exactly one account.
|
|
|
|
### Asserts
|
|
|
|
The `asserts` is a list of `assert_objects` that are all together evaluated with `and` logic to evaluate a match
|
|
```
|
|
asserts = list of assert_objects
|
|
assert_object = {
|
|
argument, // target argument of the operationid
|
|
function, // functionid to do the assert
|
|
data, // stores data specific to the chosen function
|
|
state // if this assert is statefull
|
|
}
|
|
```
|
|
List of possible asserts are:
|
|
|
|
| function | data | state |
|
|
| ------------- |:-------------:| -----:|
|
|
| `any` | [`list`, `of`, `allowed`, `values`] | stateless |
|
|
| `none` | [`none`, `of`, `these`, `values`] | stateless |
|
|
| `lt, le, gt, ge` | `comparative` | stateless |
|
|
| `length` | [`min`, `max`] | stateless |
|
|
| `limit` | [`max_cumsum`, `interval_in_sec`] | [`current_cumsum`, `interval_began`] |
|
|
| `limit_monthly` | [`max_cumsum`, `interval_in_months`] | [`current_cumsum`, `interval_began`] |
|
|
| `contains_only` | [`list`, `of`, `allowed`, `fields`] | stateless |
|
|
|
|
There is no implicit type conversion when attempting to assert, incompatible type means assert failure.
|
|
|
|
In the following we list possible `asserts`. Mentioning `argument value` refers to the value of the argument of the operation specified `argument` in `assert_object`.
|
|
|
|
#### `any`
|
|
Stateless assert, all argument types. `Argument value` must be equal to one of values in the data list
|
|
|
|
#### `none`
|
|
Stateless assert, all argument types. `Argument value` must NOT be equal to any of the values in the list.
|
|
|
|
#### `lt, le, gt, ge`
|
|
Stateless assert, only `int` type arguments.
|
|
- `lt`: `Argument value` must be less than `comparative`
|
|
- `le`: `Argument value` must be less than or equal to `comparative`
|
|
- `gt`: `Argument value` must be greater than `comparative`
|
|
- `ge`: `Argument value` must be greater than or equal to `comparative`
|
|
|
|
#### `length`
|
|
Stateless assert, only `string` type arguments. Length of string (or encrypted string in case of memo) must be within `min` and `max`, inclusive. One of the bounds can be empty to indicate unlimited.
|
|
|
|
#### `limit`
|
|
Statefull assert, only `int` type arguments. When the authority is created, `interval_began` is set to `valid_from` from its custom active authority and `max_cumsum` to `0`. Incoming operations are first tried to match all stateless asserts,
|
|
and if all passes continue with statefull asserts. If `now > interval_began + interval_in_sec`, then set `max_cumsum = 0` and set `interval_began = now`.
|
|
The assert that needs to pass is now `current_cumsum + incoming value <= max_cumsum`. If all asserts are passed, update `current_cumsum = current_cumsum + incoming value` of all involved statefull asserts.
|
|
|
|
#### `limit_monthly`
|
|
Statefull assert, only `int` type arguments. Analogue to `limit`, but `interval_began` is initially set to `month(valid_from)` and set to `month(now)` on update, additionally the time assert is `month(now) >= interval_began + interval_in_months` (include logic for month overflow when year changes).
|
|
|
|
#### `contains_only`
|
|
Stateless assert, only for dictionary type objects like `options`. The `argument` of the operation is a dictionary type objects and is only allowed to contain the attributes in the data list.
|
|
|
|
### Simple Example
|
|
Assume account A and B and some unrelated key K. Furthermore A has a custom active authority in the following way:
|
|
```
|
|
custom active authority = {
|
|
valid_from: 7.7.2018 00:00
|
|
valid_to: 8.7.2018 00:00
|
|
operationid: transfer,
|
|
authority: {
|
|
threshold: 1
|
|
key_auth: [K, 1]
|
|
account_auth: []
|
|
},
|
|
asserts: [
|
|
{
|
|
argument: to
|
|
function: any,
|
|
data: [B]
|
|
}
|
|
]
|
|
}
|
|
```
|
|
That has the consquence now that a transfer transaction sending funds away from A can be signed with key K as long as the receiver is B.
|
|
|
|
Note: This is just an illustration of a possible serialization, not a specification of the serialized format.
|
|
|
|
### Outline of handling incoming transactions
|
|
|
|
When a signed transaction arrives and before the backend evaluates if all necessary authorities are present through the signatures, do the following:
|
|
- iterate over required accounts and for each account, iterate over all operations within the transactions that require the active authority of this account
|
|
- iterate the `custom_active_authorities` of said account
|
|
- if a `custom_active_authority` is found that matches (same `operationid`, now is within `valid_to` and `valid_from` and passes all `asserts`), remember that and stop iterating the authorities and continue until all operations are checked
|
|
- if the account has a `custom active authority` match for every operation in the transaction that requires it, then grant the `active authority` of said account. If no match is found, treat as if no authority was given
|
|
|
|
Note:
|
|
- A `custom_active_authority` can only grant the `active authority` of the corresponding account, nothing more
|
|
|
|
### Modification to the backend
|
|
* Extend the account object to store custom active permission that includes a list of custom active authorities. Multiple custom active authority entries are possible for one operation
|
|
* If the active authority of the account is updated, all custom active authorities need to be confirmed in the update. Every unconfirmed one is deleted otherwise
|
|
* Extend `account_update` or create a new operation to allow changing the custom active permission
|
|
* Operation-specific authorities (if present) must be evaluated in incoming transactions
|
|
* Additional committee parameters may be needed to limit the extend of usage of this feature
|
|
|
|
Notes: The implementation must not differentiate on which operation the custom active authority is applied, all operations are treated in same fashion
|
|
|
|
# Milestones
|
|
We propose do split the implmentation into two milestones:
|
|
|
|
1. Implementation of basic functionaliy to allow custom active permissions and authorities, including `any`, `none`, `lt, le, gt, ge` and `length` `asserts`
|
|
2. Evaluation of stateful and more sophisticated asserts in terms of performance and if positive, implementation of `limit`, `limit_monthly` and `contains_only` `asserts`
|
|
|
|
This approach allows as well to add other asserts at a later stage (with a new BSIP).
|
|
|
|
# Discussion
|
|
|
|
To be found in the [issue](https://github.com/bitshares/bitshares-core/issues/1061) and [pull request](https://github.com/bitshares/bsips/pull/86).
|
|
|
|
# Summary for Shareholders
|
|
|
|
Bad publicity in terms of security can have very negative effect on the BTS value. This BSIP allows that traders can e.g. use a trading key, witnesses can use their witness key and a faucet can use a faucet key. If then for some reason the key or witness/faucet server becomes compromised, such a key can do little harm to the account holders, minimizing the risk.
|
|
|
|
This BSIP opens up a lot of use-cases as presented in Motivation section. The intention is to not alter any existing logic of the permission system, which reduces the risk of malfunctioning.
|
|
|
|
# Copyright
|
|
|
|
This document is placed in the public domain.
|
|
|