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

CheckBadBinding when registration #133

Closed
wants to merge 6 commits into from
Closed

CheckBadBinding when registration #133

wants to merge 6 commits into from

Conversation

vqhuy
Copy link
Member

@vqhuy vqhuy commented Nov 23, 2016

tbKey := key
if msg.Error == ReqNameExisted {
tbKey = df.TB.Value
}
Copy link
Member

Choose a reason for hiding this comment

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

I don't agree with this.

First, why only the TB case? What about if you try to register a different key from one that's already made its way into a signed root? That would return a ReqNameExisted and a differing key, and similarly fail to verify the auth path.

And, rightly so. It might be nice if the returned error was ErrBindingsDiffer or something, but this is still a case we want to fail a check on, no?

Copy link
Member Author

@vqhuy vqhuy Nov 27, 2016

Choose a reason for hiding this comment

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

What about if you try to register a different key from one that's already made its way into a signed root?

Ah, right. Sorry for being hasty on this. I did have a test for this but didn't think it thoroughly :(

Copy link
Member

Choose a reason for hiding this comment

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

We are all use TOFU here, so I'm not sure if returning ReqNameExisted along with CheckPassed makes more sense?

Can you elaborate? Because, I think that's what we want. The CheckPassed is telling us that the ReqNameExisted is valid, and that the server proved it to us. The client might still need to act on ReqNameExisted.

Copy link
Member Author

Choose a reason for hiding this comment

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

I think the question is when we want to fail this check? After all the checks are passed or right away in e.g., verifyAuthPath.

Copy link
Member

Choose a reason for hiding this comment

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

Alright, why do you want this to fail later?

Copy link
Member

Choose a reason for hiding this comment

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

Should KeyChange be used here instead?

Ideally, but we don't have that yet.

In this case, how does client distinguish its own registration with other client's registration?

I'm going to assume you actually do mean "client" here (ie. the "user" is trying to reregister for some legitimate reason.) I imagine, in both cases, the client would notify the user that a different binding already exists for that account, and we'd let the user disambiguate the situation. If they understood that they're trying to initiate a key change, they'd do that, or otherwise report that something is amiss.

In other words, I'm not sure the client itself can distinguish which situation it's in. I think this is getting in the realm of UI, and that's something we've hardly tackled to this point.

The case where the registration succeeds but we get an ErrBindingsDiffer is a little more clearcut though, but I'm not sure why any deceitful server would bother doing this.

Also, if it gets ReqNameExisted and ErrBindingsDiffer because of other user's registration, will calling GetKey() and re-verifying with HandleResponse(new_key) make sense?

Can you clarify what you mean by "other user's registration"?

Copy link
Member Author

@vqhuy vqhuy Nov 27, 2016

Choose a reason for hiding this comment

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

Can you clarify what you mean by "other user's registration"?

I meant the user name is already registered by someone else. I was thinking of what the client should do when getting an ErrBindingsDiffer.
UPDATE:

The case where the registration succeeds but we get an ErrBindingsDiffer is a little more clearcut though, but I'm not sure why any deceitful server would bother doing this.

Maybe the server is actually trying to equivocate? I don't know ... Is this one?

Also, on request, if the server does the registration but sneaks in some other key, it would return Success but we'd get ErrBindingsDiffer.

Copy link
Member

Choose a reason for hiding this comment

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

How can someone else register your username? Doesn't that imply they have access to your account? Again, I think this is a case covered above where the user themself needs to be notified.

Copy link
Member Author

Choose a reason for hiding this comment

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

Maybe the service doesn't use a bot though. Maybe I'm just overthinking about this.

Again, I think this is a case covered above where the user themself needs to be notified.

Yep, I agree with this.

Copy link
Member

Choose a reason for hiding this comment

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

Maybe the service doesn't use a bot though.

Regardless of the particular solution (bots being one), I believe the server should use an authenticated way of registering bindings.

return cc.HandleResponse(requestType, res, name, key)
case MonitoringType:
case KeyLookupInEpochType:
func registerAndVerify(d *ConiksDirectory, cc *ConsistencyChecks,
Copy link
Member

@arlolra arlolra Nov 26, 2016

Choose a reason for hiding this comment

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

These functions should really be returning the error from the directory operation we're ignoring. See all the FIXMEs already in this file.

Copy link
Member Author

Choose a reason for hiding this comment

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

Did you mean errors included in Errors map? Are they already being returned in HandleResponse?

Copy link
Member

Choose a reason for hiding this comment

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

I mean below, where you're removing // FIXME: Check that we got an ReqNameExisted. I don't think that's ok.

Copy link
Member Author

Choose a reason for hiding this comment

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

Since this assertion should be checked in directory_test.go, I think it can be removed.

Copy link
Member

@arlolra arlolra Nov 27, 2016

Choose a reason for hiding this comment

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

You're saying that since it's covered there, it's redundant here? That's true.

As a counterpoint though, the code currently looks like,

// test error name existed
if registerAndVerify(d, cc, alice, key) != CheckPassed {
  t.Fatal("Unexpected verification result")
}

As someone reading it for correctness, it's really not obvious that CheckPassed means that ReqNameExisted. To me, CheckPassed just means that the response was consistent. It doesn't tell me what it was though.

Copy link
Member Author

Choose a reason for hiding this comment

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

I guess my point is the purpose of consistencychecks_test.go is to test the consistency verification.

You're saying that since it's covered there, it's redundant here

What I'm trying to say is that since we already have a test for this there, we can quite confident about the checks here. Maybe my sense was wrong :(

Copy link
Member

Choose a reason for hiding this comment

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

I'm not saying you're wrong; just that the tests would be more understandable to me that way. There's nothing in registerAndVerify(d, cc, alice, key) returning CheckPassed that indicates to me that the error name existed. The registration could have equally well succeeded. We know it couldn't, because of the tests elsewhere, as you said, but that just isn't clear when trying to read this linearly.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fair enough. Sorry for wasting your time on this :(.

Copy link
Member

Choose a reason for hiding this comment

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

Sorry for wasting your time on this :(

You certainly did not waste my time. I'm happy that you're challenging my position; please continue to do so. That's how we improve.

case DirectoryProof:
return df.AP.Leaf.Value
case DirectoryProofs:
return df.AP[len(df.AP)-1].Leaf.Value
Copy link
Member

Choose a reason for hiding this comment

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

Is this going to work well with KeyLookupInEpoch ranges?

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 current discussion on KeyLookupInEpoch, we think it's okay to just return only one AP for the lookup (#70 (comment)).

Copy link
Member

Choose a reason for hiding this comment

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

Ok, it's just not obvious to me that AP[len(df.AP)-1] is the right thing, especially when the documentation there says,

latest signed tree roots

and in the KeyLookupInEpoch it probably means most recent?

But, more to the point, GetKey isn't being used yet, so do we really need it? If we do, can we add it when there's a test or use?

Copy link
Member Author

Choose a reason for hiding this comment

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

Oops, I will fix the doc.

This would be used in #93, but I should add a test for it, too.

Copy link
Member Author

@vqhuy vqhuy Nov 27, 2016

Choose a reason for hiding this comment

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

Okay, I added tests and also fixed GetKey in 60a16502c71f4f6ed1e027c63a9bf755f5eee787. Thanks for asking me to write tests.

@@ -40,5 +40,5 @@ func (tb *TemporaryBinding) Serialize(strSig []byte) []byte {
// the existed binding (TOFU).
func (tb *TemporaryBinding) Verify(index, value []byte) bool {
return bytes.Equal(tb.Index, index) &&
(value != nil && bytes.Equal(tb.Value, value))
(value == nil || (value != nil && bytes.Equal(tb.Value, value)))
Copy link
Member

Choose a reason for hiding this comment

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

When is value nil? Also, if we do need this, it should be (value == nil || bytes.Equal(tb.Value, value)).

Copy link
Member

Choose a reason for hiding this comment

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

Ok, I see what's happening here now.

In verifyAuthPath we do,
https://github.com/coniks-sys/coniks-go/blob/master/protocol/consistencychecks.go#L225-L229

Maybe we should do the same in verifyReturnedPromise?

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure. I thought that too.

@vqhuy vqhuy force-pushed the fix-reg branch 2 times, most recently from 687b44f to 60a1650 Compare November 27, 2016 10:45
@vqhuy vqhuy mentioned this pull request Nov 27, 2016
@vqhuy vqhuy changed the title Hotfix: Registration collisions [WIP] CheckBadBinding when registration Nov 27, 2016
@arlolra arlolra removed the bug label Nov 27, 2016
@vqhuy vqhuy force-pushed the fix-reg branch 4 times, most recently from f25cb1a to 75d6c3b Compare November 28, 2016 02:00
@vqhuy vqhuy changed the title [WIP] CheckBadBinding when registration CheckBadBinding when registration Nov 28, 2016
case p.ReqNameExisted:
fmt.Println(`Are you trying to update your binding? Unfortunately, KeyChange hasn't been supported yet.`)
case p.ReqSuccess:
fmt.Println("Oops! Probably the server is trying to sneak in some other key.")
Copy link
Member

Choose a reason for hiding this comment

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

"Oops! The server snuck in some other key."

if err != nil {
fmt.Println("Cannot get the key from the response, error: " + err.Error())
}
fmt.Println("You got [" + string(recvKey) + "] instead of [" + string(key) + "]")
Copy link
Member

Choose a reason for hiding this comment

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

"[ ... ] was registered instead of [ ... ]"

return CheckBadLookupIndex
}
} else {
if !ap.VerifyBinding([]byte(uname), key) {
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't you do the same as below and check key != nil && and remove setting key above?


return nil
if ap.VerifySTR(str.TreeHash) {
return nil
Copy link
Member

Choose a reason for hiding this comment

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

Can you return CheckBadAuthPath here and then nil as the final case?

}

// Verify combines VerifyBinding(), VerifyIndex(), and VerifySTR(),
// and abstracts away the detail of the failure.
// This should be called after the VRF index is verified successfully.
func (ap *AuthenticationPath) Verify(key, value, treeHash []byte) bool {
Copy link
Member

Choose a reason for hiding this comment

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

This is dead code now, no?

Copy link
Member

Choose a reason for hiding this comment

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

Seems so to me, too.

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm, a separate API of the merkletree package, no?

Copy link
Member

@masomel masomel left a comment

Choose a reason for hiding this comment

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

I only have a minor nitpick and a question about VerifyBinding()

case p.CheckBindingsDiffer:
switch response.Error {
case p.ReqNameExisted:
fmt.Println(`Are you trying to update your binding? Unfortunately, KeyChange hasn't been supported yet.`)
Copy link
Member

Choose a reason for hiding this comment

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

nitpicky: s/hasn't been/isn't

// of the proof node of the authentication path.
//
// This should be called only when ap is a proof of inclusion.
func (ap *AuthenticationPath) VerifyBinding(key, value []byte) bool {
Copy link
Member

Choose a reason for hiding this comment

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

I feel like this should also actually check that ap.Leaf.Index == ap.LookupIndex. Correct me if I'm wrong, but I think we got rid of this check entirely since we introduced a.ProofType()?

Copy link
Member

Choose a reason for hiding this comment

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

Yes, rather than the comment,

// This should be called only when ap is a proof of inclusion.

why not just assert ap.ProofType() == ProofOfInclusion as well?

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm, I think I was trying to eliminate some duplicate computations here. Now, I got the idea.

Copy link
Member

@masomel masomel Nov 28, 2016

Choose a reason for hiding this comment

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

why not just assert ap.ProofType() == ProofOfInclusion as well?

This particular check is already done at the cc.verifyAuthPath level, isn't it? I'm referring to the explicit comparison of the leaf's index and the ap.LookupIndex. I don't see this check on the client-side anywhere anymore, and I don't think it's enough to just ensure that the server set ap.ProofType() = ProofOfInclusion.

Copy link
Member Author

Choose a reason for hiding this comment

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

Is what we have in 5420200 right?

the server set ap.ProofType() = ProofOfInclusion.

I'm not sure what it means but are you saying that the ap.ProofType is set by the server? It is computed by the client when it receives an auth path (see https://github.com/coniks-sys/coniks-go/blob/master/merkletree/proof.go#L118).

Copy link
Member

Choose a reason for hiding this comment

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

Oh, ProofType() needs documentation, though.

Copy link
Member Author

Choose a reason for hiding this comment

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

Okay, I'll add it in #132 .

Copy link
Member

Choose a reason for hiding this comment

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

Sorry, one more question: Should the server be setting proofType = undeterminedProof explicitly in merkltree.Get? It seems like a server could set proofType to whatever it wanted and ap.ProofType() wouldn't validate that setting except for in the case of undeterminedProof.

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, since ap.proofType is unexported, it shouldn't be transmitted over the wire. So setting proofType from the server-side has no impact on the client.

Copy link
Member

Choose a reason for hiding this comment

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

Got it, that makes sense. Thanks!

@vqhuy vqhuy force-pushed the fix-reg branch 2 times, most recently from 32aa969 to 6cd5fe7 Compare November 28, 2016 15:58
@@ -7,6 +7,16 @@ import (
"github.com/coniks-sys/coniks-go/utils"
)

func verifyAuthPath(ap *AuthenticationPath, key, value []byte, treehash []byte) bool {
Copy link
Member

Choose a reason for hiding this comment

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

I'm wondering if, rather than inlining the ap.Verify and tb.Verify and ending up w/ a duplicated verifyAuthPath, it would be better to change the return type of those methods to error?

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm, then the merkletree package has to import protocol/error.go, right?

Copy link
Member

Choose a reason for hiding this comment

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

No, you'd do an error type conversion in protocol.

if error == merkletree.BindingsDiffer{
  return CheckBindingsDiffer
}

Copy link
Member Author

Choose a reason for hiding this comment

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

Done in 75cf9d7. What do you think?

}
}
} else { // proof of inclusion
if ap.Leaf.Value != nil {
return ErrBindingsDiffer
Copy link
Member

Choose a reason for hiding this comment

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

Is this true? The returned leaf doesn't have to be empty.

Copy link
Member Author

Choose a reason for hiding this comment

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

I suppose (https://github.com/coniks-sys/coniks-go/blob/master/merkletree/merkletree.go#L101-L102). This asserts that the client won't get a invalid key even it misuses GetKey().

Copy link
Member

Choose a reason for hiding this comment

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

Ok, thank you, I forgot we did that. Is it worth putting a comment here for people like me who don't remember so well?

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure.

people like me who don't remember so well.

I doubt about this :P

if !ap.verifyBinding(key, value) {
return false
if !bytes.Equal(ap.Leaf.Value, value) ||
!ap.Leaf.Commitment.Verify(key, value) {
Copy link
Member

Choose a reason for hiding this comment

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

Should there be a separate error type for the commitment verification failing?

return CheckBadAuthPath
default:
Copy link
Member

Choose a reason for hiding this comment

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

Would it be safer if the default was to panic and add a case for nil?

Copy link
Member Author

Choose a reason for hiding this comment

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

Definitely. Thanks!

@arlolra
Copy link
Member

arlolra commented Nov 28, 2016

Ok, LGTM. A general comment is that we probably need greater test coverage around these error cases in the merkletree, since there wasn't much updating to do there.

@vqhuy
Copy link
Member Author

vqhuy commented Nov 28, 2016

Yes, the tests are on their way.

@vqhuy
Copy link
Member Author

vqhuy commented Nov 29, 2016

I add some more tests in 4a21bea.

@arlolra
Copy link
Member

arlolra commented Nov 29, 2016

Thanks! Looks good.

@vqhuy
Copy link
Member Author

vqhuy commented Nov 29, 2016

Merged in 2c3561a.

@vqhuy vqhuy closed this Nov 29, 2016
@vqhuy vqhuy deleted the fix-reg branch November 29, 2016 05:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants