-
Notifications
You must be signed in to change notification settings - Fork 30
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
Conversation
tbKey := key | ||
if msg.Error == ReqNameExisted { | ||
tbKey = df.TB.Value | ||
} |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 :(
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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"?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 :(
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 :(.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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)).
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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))) |
There was a problem hiding this comment.
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))
.
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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.
687b44f
to
60a1650
Compare
f25cb1a
to
75d6c3b
Compare
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.") |
There was a problem hiding this comment.
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) + "]") |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this 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.`) |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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()
?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 .
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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!
32aa969
to
6cd5fe7
Compare
@@ -7,6 +7,16 @@ import ( | |||
"github.com/coniks-sys/coniks-go/utils" | |||
) | |||
|
|||
func verifyAuthPath(ap *AuthenticationPath, key, value []byte, treehash []byte) bool { |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
}
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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()
.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely. Thanks!
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. |
Yes, the tests are on their way. |
I add some more tests in 4a21bea. |
Thanks! Looks good. |
Merged in 2c3561a. |
doRequestAndVerify
ignore the result's returned key for the KeyLookupType case? #127