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

How to study a genuine invite is without getting exclusive access to it? #241

Closed
DavidBruant opened this issue Oct 22, 2019 · 11 comments
Closed
Labels
ERTP package: ERTP

Comments

@DavidBruant
Copy link
Contributor

This issue follows a discussion elsewhere

Somebody hands me an alleged invite to a seat to some contract. I want 2 things before making the decision to participate:

  • be sure the object is a genuine invite
  • study the contract source code and terms

I want both before, because i don't want to waste time studying an invite that wouldn't be genuine, but i also don't want to redeem it before having studied it

@Chris-Hibbert suggested that contractHost~.getInviteAssay()~.claimAll(invite) works for the purpose of making sure the invite is genuine, but i feel claimAll is doing too much for what's needed here

would it be possible and make sense to add a dedicated method like assay.validateAssetHolder(paymentOrPurse) that would return true only if paymentOrPurse is of the correct assay?

It's not fully clear to me what other usage there is for contractHost.getInviteAssay. If there isn't any, would it make sense to replace it with validateInvite(invite :Payment) -> (boolean);?

@michaelfig
Copy link
Member

Untested, but would this work?

Promise.all([contractHost~.getInviteAssay(), invite~.getAssay()]).then(([a1, a2]) => a1 === a2)

I think the only reason it failed for you is that you didn't compare the unwrapped assay presences, only the promises (which will always be different).

@katelynsills
Copy link
Contributor

I think we've intentionally rejected having a separate inviteAssay.validate method, because in an asynchronous environment, you always want to use claim or claimAll, and validate on its own is an anti-pattern. Here's why:

Let's suppose that Alice gives Bob an invite ERTP payment. Bob does inviteAssay.validate(invite) and gets true, meaning that the inviteAssay did make this invite payment at some point. Let's say he does invite.getBalance() and gets an assetDesc that is what he expects. "Great!" He thinks. So he goes ahead and tries to send the payment to a contract. Independently, Alice, who still has a reference to the invite, used the payment in a contract just before Bob, and because she's done that, now the new balance of the payment that Bob has access to is 0. So, by using validate + getBalance rather than claimAll, Bob thought he was safe from potential misdeeds from Alice, but he actually wasn't. claim or claimAll is necessary, because it creates a new payment that only bob has a reference to, rather than keeping the payment that both Bob and Alice have a reference to. I'm hard-pressed to find an example where you don't actually want to use claim or claimAll in the place that you would think to use validate, but let me know if you find any.

As for @michaelfig 's example, using a response of getAssay is insecure because the invite object could not be a real ERTP payment at all. This object purporting to be an ERTP payment could have a fake getAssay function that returns a real assay. We have to call a method on the assay that we trust to be able to do any confirmation, so that's why inviteAssay.claim and inviteAssay.claimAll are necessary.

@katelynsills
Copy link
Contributor

Closing this, but feel free to reopen for any reason.

@DavidBruant
Copy link
Contributor Author

(...) in an asynchronous environment (...)

So, by using validate + getBalance rather than claimAll, Bob thought he was safe from potential misdeeds from Alice, but he actually wasn't. claim or claimAll is necessary, because it creates a new payment that only bob has a reference to

These are the parts that i was missing in my reasoning. Using claimAll makes a lot of sense, especially the part about Bob having the only reference to the new payment which guarantees some security properties i had not anticipated

I'm convinced that a dedicated validate is an anti-pattern
Thanks for the explanation @katelynsills !

@DavidBruant
Copy link
Contributor Author

I think the only reason it failed for you is that you didn't compare the unwrapped assay presences, only the promises (which will always be different).

Thanks @michaelfig for the info, i had missed the part of unwrapped promises entirely!

@DavidBruant
Copy link
Contributor Author

DavidBruant commented Nov 7, 2019

Closing this, but feel free to reopen for any reason.

@katelynsills I don't have sufficient permissions on the repo to reopen, but that's my intention

I agree that validate + getBalance has a security flaw, but i still see a scenario something like validate is useful on its own (but i'm not sure whether this scenario is worthwhile)

Pearl sets up a contract and sends an invite to Garnet. Garnet claimsAll to be sure she has a genuine invite before studying it. Upon studying the invite (source + terms), she decides she does not want to participate. It can happen in different ways:

  • Garnet sends back the invite she holds exclusive access to to Pearl
  • Garnet does not send back the invite payment she holds exclusive access to to Pearl

The former case is easy. Pearl claimsAll back the invite and sends it to someone else
The latter case is more complex. Pearl has a contract that is stuck because someone is holding an exclusive access to an invite and she has no way to recover it. Her only option is to reinstall the contract again. If people had participated to the previous contract, they all need to get refunded, get re-invited again, replay the parts of the contract they had already played previously

This all happens because Garnet needed to claimAll (or redeem) before being able to study an invite she knew was genuine

I believe there may be circumstances where you want the invite exclusivity before studying the invite and there may be other circumstances where you want to study the invite before getting exclusive access
And ERTP is convenient only for the former circumstances

Something like host~.getInviteContractSourceCodeAndTerms(invite) (or any better name) which returns {source, terms} would be help the latter (and might be also more convenient for the former; i feel it's weird to get one part from the invite object and the other from a method on host)

I also see a (minor?) privacy issue with claimAll which is that Pearl knows Garnet claimed the payment (and when) by polling the invite payment she has access to to know when it's empty
host~.getInviteContractSourceCodeAndTerms(invite) would not have this privacy issue

@DavidBruant DavidBruant changed the title How to verify an invite is genuine without redeeming it? How to study a genuine invite is without getting exclusive access to it? Nov 7, 2019
@Chris-Hibbert
Copy link
Contributor

I think the invite's extent includes the contract's terms, which you can validate before claiming.

invitePayment.getBalance().extent

@DavidBruant
Copy link
Contributor Author

invitePayment.getBalance().extent

(i assume you mean invitePayment.getBalance().terms)

Yes, but it happens at a moment when you don't know invitePayment is a genuine invite payment of the correct assay, so maybe you're wasting your time reviewing a malicious object

@Chris-Hibbert
Copy link
Contributor

If it doesn't match the terms you wanted, you should drop it and walk away. If it matches the terms you were interested in, you can call claimAll() in the way you proposed above. The existing protocol gives the ability to compare the terms before taking exclusive access.

@DavidBruant
Copy link
Contributor Author

Yes, i understand, but you have no way of knowing the invite is genuine before studying its source + terms

One way to forge a fake invite is if you already have access to a genuine invite to the contract, you can do the following:

// assumed genuineInvite object

const fakeInvite = {
    getBalance(){
        return genuineInvite.getBalance()
    }
}

If i share fakeInvite with you, you can do host.getInstallationSourceCode(fakeInvite.getBalance().installation) and fakeInvite.getBalance().terms successfully. Then you spend time studying the source code and terms, but you will not be able to do either claimAll(fakeInvite) nor host~.redeem(fakeInvite) successfully, so the time you spent making sure you're interested in the terms is wasted

With host~.getInviteContractSourceCodeAndTerms(allegedInvite), if it returns, you would know that you're studying a genuine invite. host~.getInviteContractSourceCodeAndTerms(fakeInvite) would fail because host does not know wht to do of fakeInvite unlike in both host.getInstallationSourceCode(fakeInvite.getBalance().installation) and fakeInvite.getBalance().terms

@warner
Copy link
Member

warner commented Dec 4, 2019

in the old repo. this was ERTP issue 179

@warner warner transferred this issue from Agoric/ERTP Dec 4, 2019
@warner warner added the ERTP package: ERTP label Dec 4, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ERTP package: ERTP
Projects
None yet
Development

No branches or pull requests

5 participants