Neo: Scoped Witnesses

Created on 12 Jan 2019  路  34Comments  路  Source: neo-project/neo

Currently, user is able to attach (global) witnesses to an Invocation (or Contract/Claim) Transaction, which are validated on execution time by means of CheckWitness function (mostly based on GetScriptsForVerifying).
These global witnesses are enough for a scenario where a single main script is executed (Contract/Claim Tx). However, on Invocation tx we can have surrogated executions by means of dynamic invoke, so user must have the power to add Contract-Specific Witnesses (in contrast to Global Witnesses), which are witnesses valid only during the execution on a specific contract.

If community likes the idea, I can propose a PR, which should be fully backwards-compatible by simply adding an extra attribute to transaction: Script attribute will continue to correspond to current Global Witness behavior; and a new ContractSpecificScript will only be listed as a verification script when execution is happening on the specified target script. This gives power to user provide a signature which is only valid to its purposes, and nothing more.

ContractSpecificScript will receive two 20-byte parameters: (i) attached scripthash (same as Script) (ii) target scripthash (contract that is allowed to receive this verification script as validation), which can also be provided as a list if multiple targets are informed (to avoid repeated signature validations).

Another possibily is to simply adjust Script attribute to receive this extra (optional) parameter, which is a list of target scripthashes where this is valid (if left empty, it's global).

blocker ledger neo3-preview1 ready-to-implement

Most helpful comment

Don't worry @igormcoelho, I know this is a very complicated and sensitive subject since it is related to the "core security".

@erikzhang do you want to give your opinion about this?

Regarding your question:

Is #287 not a good solution to application security issues?

287 is a good solution for tons of features, but it doesn't solve everything. I think that this was a very good step, every application needs its own meta-data(including permissions), and for that, I think the manifest is perfect.

I will use the camera hardware from mobile devices to try to explain, what I think is the difference:
In mobile, you have to "allow access to the camera", but we don't limit _when_ the camera can be used.
This 'logic flaw' is exploited in Android devices very often, creating many "privacy scandals", like apps recording you without your knowledge.

In Blockchain, the 'flaw' may lead to funds loss.

PS: I might be mistaken. @igormcoelho @shargon could you confirm if my "story" makes sense?

All 34 comments

@canesin @shargon @erikzhang @localhuman

This partially (or totally) resolves: https://github.com/neo-project/neo/issues/446
Adding also: @belane @vncoelho @jsolman

Is #287 not a good solution to application security issues?

Erik, #287 is a very important change, from contract perspective, and I think we should advance in that direction asap. However, this change here is to give security from user perspective, for any contract on network, even the existing ones. I think these are complementary.

I like this. A user can be sure that only the contract they are invoking will be able to transfer their funds and not the contract that is being dynamically invoked by the contract they are calling. Very powerful from the perspective of developing dynamic contracts.

Should we leave this open and close #446 ?
This has the needed functionality for the use cases I think matter.

Lets keep all of them open, for deeper discussions, because there are small and important differences on each approach. Changes will also affect different layers, neovm, neocontract, etc.

Just to emphasize that this is actually useful in other situations, that may allow deeper utilization and greater programmability of the network. Suppose two users want to trade NEP-5 tokens in a OTC style.
A possible invocation script could be:
PUSH transfer data A -> B (token X)
APPCALL contract token X
PUSH transfer data B -> A (token Y)
APPCALL contract token Y

However, A is only interested in signing operation regarding transfer on contract X.. and B is only interested in signing operations involving contract Y. Right now it's impossible to isolate, and the signatures of A and B will be valid for the whole process.
Anyway, it's not a huge advance, since both A and B will need to sign the whole script, and both of them could possibly review the whole process in details before signing. But still, knowing that your signature will only be valid in a much constrained scenario gives more power to create more secure and advanced use-cases, on a smart transaction network.

https://github.com/neo-project/neo-vm/blob/0675aa199fc2c71eac7c3123e73b5a77692417d8/src/neo-vm/OpCode.cs#L379-L382

Can we use the opcode VERIFY to verify the signatures of some conventional data?

@erikzhang we cannot sign any conventional data, because not all witnesses include a public key cryptography (it could be a password-lock script, for example). More elaborate contracts such as multi-sig would also have no way of signing a conventional data.

I believe other features are necessary for the Witness System, perhaps on Neo 3.0, or even on Neo 2.0 to be ported on Neo 3.0 (would be much better in my opinion). Another proposal is to allow the witness to be consumed only a specific number of times (example, I sign this transaction to be used at most one time), because right now the usage is unlimited. Perhaps, the best solution is adding another attribute: ScriptExtended, that allows specifing the contract and number of times to allow the usage of this witness. This gives user more power and control over its own signatures. The negative aspect is that it adds extra complexity to the network... so, in my opinion, it should be added unless there's another simple way to give the same guarantees.

Do you think this is reasonable @shargon? Or perhaps useless, feel free to give insights :)

@jsolman @shargon @ediopia @kokahunter I closed https://github.com/neo-project/neo/issues/446

Any thoughts on this @canesin ? I think it's a very necessary feature, quite easy to do and does not introduce breaking changes.

I like your idea @igormcoelho , and looks possible :)

Maybe we could make a special signature, consumable... only for one use 馃

I think this is very important for Neo 3 since we will rely much more on application for transfers and everything else.
example: I invoke Native Neo sending a given amount of token,but I want my witness to be only valid for this contract (neo native). User can add an extra field with this infornation. User can also choose to allow this witness to be only valid for specific contract group (see Manifest system).
This creates a very solid and safe standard for handling token invocations on Neo 3.

What do you think @shargon @vncoelho @erikzhang ?

I agree, brother, in addition, I think that it will be hard to find other way. ehsuahahah

I like it, but in verification, it works?

For Neo3, we can add an extra field on cosigner, indicating: scripthash of contract (if contract specific); pubkey of group (if group specific); nothing, for "free" signatures on trusted contracts.
Examples: one wants to transfer a native nep5 asset during mint, say Neo, it can use a contract-specific for Neo,if on minting or other operations that are potentially untrusted. If it is for some dapp usage, it can use dapp group signature, since that is trusted and perhaps necessary for operation (like DEX).
For direct native nep5 transfers, "free" witnesses can be used, as only a transfer op is being invoked, and it is completely safe (saves space on tx).

I think that we need it

Can I add high-priority for this issue?
Edit: I added the high priority tag

Changed the name to Scoped Witnesses, since they can be contract/group specific (or general). We need to put scope field on cosigner.
For sender, we should constrain its scope to the Native GAS contract, by default (safety measure). If user wants to reuse its witness for a broader situation, it can add a cosigner to the same input contract scripthash, and add a broader scope (example: general, as empty, or group). User could add multiple scopes by adding multiple cosigners, manually, and when checking CheckWitness, only the allowed scopes are presented to user , e.g., on GetScriptHashesForVerifying(contract=0xabcd), witness is only valid if general, or specific to this contract, or for this contract group.

Another idea emerged when discussing with @shargon , another (necessary) feature is having multiple scripts on a transaction, and attaching witness only to a specific script.
So, it could be like: cosigner hash [type hash]. Type can be CallingScript, ExecutingScript, Group (or None).
[EDIT:] I don't think this is necessary anymore @shargon, we can work fine with a single script on tx. Only ScopeType and Scope fields are enough.

@erikzhang what is your opinion?

I suggest four ScopeType:

  • 0x00 - Global (default on neo2) - no params
  • 0x01 - CalledByEntry (requires CallingScriptHash == EntryScriptHash) - no params - can be default safe choice for native NEO/GAS (neo2 attach mode)
  • 0x02 - CustomScriptHash (requires CurrentScriptHash == custom_hash) - custom hash for contract-specific
  • 0x03 - ExecutingGroupPubKey (requires that executing contract is in provide group parameter) - custom pubkey for group members

Security measure on wallets: if some app requires a witness for a method (see method manifest https://github.com/neo-project/neo/issues/906) and user has tokens/value of that kind, warn user that tokens/value might be consumed on the process.
Strategy to select witness type is: if contract is directly invoked (Native NEO/GAS) and it doesn't require any other contract (see manifest), give root permission 0x01. If contract B may be called afterwards by invoked contract A (see manifest), give specific permission 0x02 to B, or group that includes B (permission 0x03). However, note that if B contains tokens or any value, you are giving right to consume this value too, wallets can easily warn about this too.
Wallets will never give permission 0x00, but some developer may still use it for something it wants.

It's a simple and secure way of exploring both Feature Manifest and Contract Witness.

@igormcoelho this is good. Do you think you can start implementing it? This is a top priority issue, we need this ASAP.
It may not be perfect at the first try, but we need to put this forward.
Thanks!

I will add this to neo 3 milestone since it is mandatory for #902.

I will start to implement this

@igormcoelho

0x01 - CallingScriptHash

Seems unuseful because you will call other contract, your script will call neo-token

@shargon 0x01 is condition CallingScriptHash == EntryScriptHash, meaning that the callingscript must be the entry script.

@lock9 I'm just waiting for more comments... I know it looks slow, but many progress is done this way, comment by comment... becoming reality now ;)

Don't worry @igormcoelho, I know this is a very complicated and sensitive subject since it is related to the "core security".

@erikzhang do you want to give your opinion about this?

Regarding your question:

Is #287 not a good solution to application security issues?

287 is a good solution for tons of features, but it doesn't solve everything. I think that this was a very good step, every application needs its own meta-data(including permissions), and for that, I think the manifest is perfect.

I will use the camera hardware from mobile devices to try to explain, what I think is the difference:
In mobile, you have to "allow access to the camera", but we don't limit _when_ the camera can be used.
This 'logic flaw' is exploited in Android devices very often, creating many "privacy scandals", like apps recording you without your knowledge.

In Blockchain, the 'flaw' may lead to funds loss.

PS: I might be mistaken. @igormcoelho @shargon could you confirm if my "story" makes sense?

I think "Scoped Witnesses" is a good idea. If the solution has been fully discussed, it can be implemented.

Ok, I'll handle it (I really wanted to hear your opinion here Erik, thanks :) ). Since Shargon has already started a PR too, we will see how this goes, adapt it or restart it, in order to finish this as quickly as possible.

Now that it's nearly coming to an end (after much debates), here it goes a simple explanation of the proposed feature:

You are starting an operation, invoking some contracts, and you may decide:

  • Global: "Take my signature, I trust you. You can even give them to others, I trust them too."
  • CalledByEntry: "I only trust my own invocations. I give you the signature, and only you can use it, no one else."
  • CustomScriptHash: "I give my signature only to the contract that I'm indicating here." (it can be composed with multiple attributes like this).
  • CustomGroupPubKey: "I give my signature only to contracts in the group I'm indicating here"

Perfect summary!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Tommo-L picture Tommo-L  路  30Comments

lock9 picture lock9  路  35Comments

coranos picture coranos  路  85Comments

SueNEO picture SueNEO  路  30Comments

lock9 picture lock9  路  62Comments