Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TACo Encryption Allow Logic Interfaces #89

Merged
merged 19 commits into from
Aug 18, 2023
Merged

Conversation

KPrasch
Copy link
Member

@KPrasch KPrasch commented Jun 21, 2023

Implements the contracts from the specification.

Co Authored By @theref @cygnusv @jMyles @derekpierre

Based over #86

@KPrasch KPrasch requested review from cygnusv and theref June 21, 2023 14:45
@KPrasch KPrasch changed the title [WIP] CBD Auth/Deauth [WIP] CBD Access Control Interfaces Jun 21, 2023
Copy link
Member

@cygnusv cygnusv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good! Some preliminar comments.

bytes memory evidence,
bytes memory ciphertextHash
) public view override returns(bool) {
address enricoAddress = address(uint160(bytes20(evidence)));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An address can't be the evidence, otherwise anyone can put any address there. The evidence was defined as a signature, so what we want here is to do something like:
address enricoAddress = ecrecover(ciphertextHash, v, r, s)
where v, r, s are somehow extracted from the signature evidence, and ciphertextHash should be somehow casted to bytes32 (what ecrecover expects). See https://docs.soliditylang.org/en/latest/cheatsheet.html#mathematical-and-cryptographic-functions

Copy link
Member Author

@KPrasch KPrasch Jun 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@theref and I considered recovering the address on-chain via ECDSA, however in the case of the AllowList for example, the only way an address can be "authorized" is by Ritual.authority setting it in a permissioned way. In other words, only the ritual's authority can add new enricos. Are we missing something?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's say the authority authorizes 0x1234. I don't own that address, but I just write 0x1234 as evidence when creating the conditions and access control policy. Ursulas would accept this evidence and that's not what we want. We need to provide evidence that I own that address.

Copy link
Member Author

@KPrasch KPrasch Jun 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If enricos are submitting a signature of the ciphertext and policy, doesn't that mitigate the issue you indicate (as enrico cannot simply "write 0x1234 as evidence")?

Ursula can recover the address from the signature provided in AccessControlPolicy.authorization and check that it's in the allow list.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If enricos are submitting a signature of the ciphertext and policy, doesn't that mitigate the issue you indicate (as enrico cannot simply "write 0x1234 as evidence")?

Yes, that's what I suggested initially.

Ursula can recover the address from the signature provided in AccessControlPolicy.authorization and check that it's in the allow list.

Right, what I'm saying is that what you called AccessControlPolicy.authorization is what must be submitted as evidence by Ursula. That's what I originally meant by the evidence is attached to the ciphertext.

Copy link
Member Author

@KPrasch KPrasch Jun 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, not sure I'm following - why "must" that be the case?

Is there a problem with trusting ursula to perform a valid signature recovery from AccessControlPolicy.authorization?

Is that different than trusting ursula to perform a valid condition evaluation?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another way of framing it: What is the advantage of doing an authorization signature recovery onchian vs. in Ursula's runtime?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main advantage I see is that this allows for modular and pluggable evidence evaluation mechanisms (via smart contracts), without having to change Ursula's code

import "./Coordinator.sol";


contract AllowList is AccessControlDefaultAdminRules, IAccessController {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you don't plan to use role management functions, AccessControl may be enough.

contracts/contracts/coordination/AllowList.sol Outdated Show resolved Hide resolved
contracts/contracts/coordination/Coordinator.sol Outdated Show resolved Hide resolved
contracts/contracts/coordination/IAccessControler.sol Outdated Show resolved Hide resolved
KPrasch added a commit to KPrasch/nucypher-contracts that referenced this pull request Jun 21, 2023
@cygnusv cygnusv changed the base branch from main to development July 3, 2023 10:48
theref pushed a commit to KPrasch/nucypher-contracts that referenced this pull request Jul 4, 2023
theref pushed a commit to KPrasch/nucypher-contracts that referenced this pull request Jul 8, 2023
@theref theref force-pushed the auth branch 2 times, most recently from 0be3cf3 to e5e8a05 Compare July 8, 2023 17:12
KPrasch added a commit to KPrasch/nucypher-contracts that referenced this pull request Jul 9, 2023
@theref
Copy link
Contributor

theref commented Jul 9, 2023

@KPrasch I think you've overwritten some work here, I'll push to another branch so that we can compare

@KPrasch KPrasch changed the title [WIP] CBD Access Control Interfaces [WIP] TACo Encryption Allow Logic Interfaces Jul 10, 2023
KPrasch added a commit to KPrasch/nucypher-contracts that referenced this pull request Jul 12, 2023
KPrasch added a commit to KPrasch/nucypher-contracts that referenced this pull request Jul 12, 2023
@KPrasch KPrasch marked this pull request as ready for review July 17, 2023 12:38
@KPrasch KPrasch changed the title [WIP] TACo Encryption Allow Logic Interfaces TACo Encryption Allow Logic Interfaces Jul 17, 2023
"Only ritual authority is permitted");
require(coordinator.isRitualFinalized(ritualID),
"Only active rituals can add authorizations");
for (uint i=0; i<addresses.length; i++) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for (uint i=0; i<addresses.length; i++) {
for (uint256 i=0; i < addresses.length; i++) {

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✔️

"Only ritual authority is permitted");
require(coordinator.isRitualFinalized(ritualID),
"Only active rituals can add authorizations");
for (uint i=0; i<addresses.length; i++) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for (uint i=0; i<addresses.length; i++) {
for (uint256 i=0; i < addresses.length; i++) {

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✔️

@@ -184,11 +198,15 @@ contract Coordinator is AccessControlDefaultAdminRules {
return ritual.participant;
}

function initiateRitual(
function _initiateRitual(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we walk back this change i.e. change _initiateRitual -> initiatRitual and remove the function on L254, since the params are now the same?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✔️

@@ -359,13 +391,13 @@ contract Coordinator is AccessControlDefaultAdminRules {
}

function getParticipantFromProvider(
uint256 ritualID,
uint32 ritualID,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we be consistent with our camel-case (other spots as well - L400 and L412)?

Suggested change
uint32 ritualID,
uint32 ritualId,

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✔️

Comment on lines 37 to 39
require(coordinator.getAuthority(ritualID) == msg.sender,
"Only ritual authority is permitted");
require(coordinator.isRitualFinalized(ritualID),
"Only active rituals can add authorizations");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to turn these requires into one of those solidity modifiers like onlyOwner etc., in this case onlyAuthorityForActiveRitual or something like that...

It can be used for authorizing and deauthorizing.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✔️

@@ -93,49 +93,93 @@ def coordinator(project, deployer, stake_info, flat_rate_fee_model, initiator):
return contract


@pytest.fixture()
def global_allow_list(project, deployer, coordinator):
contract = project.GlobalAllowList.deploy(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add tests (maybe not in this file) for the actual GlobalAllowList contract?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, certainly. I intend to push new tests before merging, but still want to get a review in the meantime 👍🏻

@@ -0,0 +1,9 @@
pragma solidity ^0.8.0;

interface IRitualAuthorizer {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Nice!

coordinator = _coordinator;
}

function isAuthorized(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the DKG client supposed to call this method at some point? Fore example, when picking nodes for the DKG cohort?

Copy link
Member

@derekpierre derekpierre Aug 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not when picking nodes for the cohort but rather when decryption is requested from the nodes. Each node that receives a decryption request will check to make sure that this encryptor is allowed to use the corresponding ritual/encrypting key and therefore worthy to be decrypted. This is showcased in WIP PR 3194 - see https://github.com/nucypher/nucypher/pull/3194/files#diff-8df83dacf71b2ff2ca3636cd470cba70eb6b141e3c8843fadfab72514aafa3f6R199 and https://github.com/nucypher/nucypher/pull/3194/files#diff-911a370c18d0d0e3ea62099fc5ff6daa16915da19cf5399f24e404b7d8d6d168R790

Copy link
Member

@cygnusv cygnusv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work! My main comment is regarding how hash within the authorization check is performed and the lack of tests for that.

@@ -9,6 +9,8 @@ import "./IFeeModel.sol";
import "./IReimbursementPool.sol";
import "../lib/BLS12381.sol";
import "../../threshold/IAccessControlApplication.sol";
import "../../../../nucypher/tests/acceptance/contracts/.cache/openzeppelin/v4.8.1/access/IAccessControl.sol";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How many headaches you gave us in Barcelona...

// TODO: restrict to ritualID < rituals.length?
return getRitualState(rituals[ritualId]);
}

function isRitualFinalized(uint32 ritualId) external view returns (bool){
return getRitualState(rituals[ritualId]) == RitualState.FINALIZED;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a comment for this PR, but something to consider I just realized: we may need to discern if a covenant is currently active or not, depending whether it's within its arranged duration or not. The fact that the ritual is FINALIZED is a prerequisite for that to happen, of course. I can address that in #107

import "./IRitualAuthorizer.sol";
import "./Coordinator.sol";

contract GlobalAllowList is AccessControlDefaultAdminRules, IRitualAuthorizer {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After re-reading this PR, I think a more descriptive name for the interface is IEncryptionAuthorizer, and subsequently the contract could be GlobalEncryptionAllowList (although not as necessary if the interface is renamed).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✔️


function setCoordinator(Coordinator _coordinator) public {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "Only admin can set coordinator");
coordinator = _coordinator;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the use case for setting the Coordinator after deployment, if it was already set on construction time?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the event the coordinator is redeployed to support a security patch or new feature.

bytes memory evidence,
bytes memory digest
) public view override returns(bool) {
address recovered_address = keccak256(digest)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If digest is already a digest, there should be no need to call keccak256. Related to this is the lack of tests for this method. Maybe we can just put a pin on this for the moment and revisit in a separate PR, but something needs to be done here.

@KPrasch
Copy link
Member Author

KPrasch commented Aug 18, 2023

Thanks for all of the reviews and suggestions on this PR. I introduced some new tests and covered every RFC. Let me know if there's anything else before merging. If you've already reviewed, perhaps just skim the new commits. 🤠

Copy link
Member

@derekpierre derekpierre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎸

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants