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

Client-auditor protocol #155

Merged
merged 26 commits into from
Jul 7, 2017
Merged

Client-auditor protocol #155

merged 26 commits into from
Jul 7, 2017

Conversation

masomel
Copy link
Member

@masomel masomel commented Dec 15, 2016

Part of #151

TODO:

  • client-auditor requests and responses
  • auditor state
  • tests

@masomel masomel self-assigned this Dec 15, 2016
@masomel masomel added this to the 0.2.0 milestone Dec 15, 2016
@masomel masomel force-pushed the auditor-protocol branch 2 times, most recently from cb64fbc to da139a7 Compare December 21, 2016 22:39
@masomel masomel changed the title [WIP] Auditing messages Auditing messages Feb 16, 2017
@vqhuy
Copy link
Member

vqhuy commented Feb 17, 2017

My most concern here is how will this fit well with other auditor types (like Cosi and Blockchain). But I'm fine with this PR and happy to reorganize our code as things come later.

@masomel
Copy link
Member Author

masomel commented Feb 17, 2017

My most concern here is how will this fit well with other auditor types (like Cosi and Blockchain).

Right, all of these auditor types are very distinct, but I think CoSi auditors and blockchain auditors are more extensions to the auditing protocol instead of complete replacements.

But I'm fine with this PR and happy to reorganize our code as things come later.

I'm not sure what you have in mind. Is your issue that the auditor protocol is part of the core protocol? Because as I said above, I think it should be. If/when we have more auditing options, like you said, we can re-think whether this makes sense.

func newDirectoryHistory(signKey sign.PublicKey, str *m.SignedTreeRoot) *directoryHistory {
h := new(directoryHistory)
h.signKey = signKey
h.snapshots = make(map[uint64]*m.SignedTreeRoot)
Copy link
Member

Choose a reason for hiding this comment

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

Should str be included in snapshots 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.

Is there a big advantage to including the initially observed str in snapshots right away?

Copy link
Member

Choose a reason for hiding this comment

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

Hmm, no. I just thought it should be here at the first glance. But it doesn't matter :p


// IsKnownDirectory checks to see if an entry for the directory
// address addr exists in the audit log l. IsKnownDirectory() does not
// validate the entries themselves. It returns true if an entry exists,
Copy link
Member

Choose a reason for hiding this comment

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

Nitpicking: double space here ;)

},
}, ReqSuccess
}

func (msg *Response) validate() error {
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 update these assertions for ObservedSTR(s)?

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, I think this makes sense, especially if we're going to have some generic HandleSTRs function.

}

if h := l.histories[req.DirectoryAddr]; h != nil {

Copy link
Member

Choose a reason for hiding this comment

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

I want to swap these 2 code blocks for consistency with directory.go: return error immediately if h==nil. The same for GetObservedSTR.

Copy link
Member Author

Choose a reason for hiding this comment

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

Can you please point to code in directory.go where this happens?

Copy link
Member

Choose a reason for hiding this comment

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

Here we go: https://github.com/coniks-sys/coniks-go/blob/master/protocol/directory.go#L201-L215. I meant we return NewErrorResponse earlier.

Copy link
Member

Choose a reason for hiding this comment

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

@masomel: Please fix this.

Copy link
Member Author

Choose a reason for hiding this comment

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

PTAL at 42da6b3

return false
}

// FIXME: pass Request message as param
Copy link
Member

Choose a reason for hiding this comment

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

Do you mean Response? What do you think if we have a generic function like HandleResponse in consistencychecks.go and call Insert, Update, InsertRange for each case? I would like to call it HandleDirectorySTRs.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah, you're right, this should be Response. I also like the idea of having a generic HandleDirectorySTRs function since there is a lot of potentially shared STR verification code between the auditor and the client. But Insert, Update and InsertRange are specific to the auditor log, though, so I think it makes sense to keep those separate from consistencychecks.go.

Copy link
Member

Choose a reason for hiding this comment

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

But Insert, Update and InsertRange are specific to the auditor log, though, so I think it makes sense to keep those separate from consistencychecks.go.

Yes, of course.

@vqhuy
Copy link
Member

vqhuy commented Mar 2, 2017

Is your issue that the auditor protocol is part of the core protocol? Because as I said above, I think it should be.

Yes, I agree that too.

@masomel
Copy link
Member Author

masomel commented Mar 8, 2017

@c633 @liamsi @arlolra Can you please TAL at this?

@@ -100,6 +100,50 @@ func (cc *ConsistencyChecks) HandleResponse(requestType int, msg *Response,
return CheckPassed
}

// handleDirectorySTRs is supposed to be used by CONIKS clients to
// handle auditor responses, and by CONIKS auditors to handle directory responses.
Copy link
Member

Choose a reason for hiding this comment

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

Hmm, I'm not sure if this is supposed to be here since the file header states Implements all consistency check operations done by a CONIKS client on data received from a CONIKS directory.. Maybe we should move verifySTR and verifySTRConsistency to str.go? If you agree, we can add a TODO here.

Copy link
Member Author

@masomel masomel Mar 8, 2017

Choose a reason for hiding this comment

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

I realized this as well. I've started working on creating a more generic auditor module that contains all the functionality clients and auditors need to verify/audit the STR consistency. What do you think? But I also think this belongs in a different PR since it requires for a verifySTRConsistencyInEpoch to be implemented. I'll add a TODO.

I know you were already working on this when implementing the monitoring protocol. Do you think it would make sense to implement all the STR-related verification separate from the monitoring?

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, I agree! I'll separate these code and open PR for each of them.

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've started this in https://github.com/coniks-sys/coniks-go/tree/generic-auditor What do you think?

Copy link
Member

@liamsi liamsi left a comment

Choose a reason for hiding this comment

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

A few first nitpicks/comments/questions while digging through the code... More comments on the general design later/tomorrow.
Also, there is a merge conflict in this pull (rebase onto master).


// FIXME: pass Response message as param
// masomel: will probably want to write a more generic function
// for "catching up" on a history in case an auditor misses epochs
Copy link
Member

Choose a reason for hiding this comment

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

Before we merge this, the comments should follow golang's documentation convention (start with Insert). The FIXME can stay but shouldn't be the first thing one gets to read. The comment should probably briefly state what the difference/relation between Insert and Update is.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah yes, thanks for pointing this out!

// FIXME: pass Response message as param
func (l *ConiksAuditLog) Update(addr string, newSTR *m.SignedTreeRoot) error {

// panic if we want to update an entry for which we don't have
Copy link
Member

Choose a reason for hiding this comment

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

"... for which we don't know the directory" (?). Also, we don't really panic here, right?

Copy link
Member Author

Choose a reason for hiding this comment

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

No, we don't. I forgot to update the comment here.

res, err := aud.GetObservedSTR(&AuditingRequest{
DirectoryAddr: "test-server"})
obs := res.DirectoryResponse.(*ObservedSTR)
if err != ReqSuccess {
Copy link
Member

Choose a reason for hiding this comment

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

nitpicking: directly catch the error after it occurs (move this one line up)

Copy link
Member Author

@masomel masomel Jun 1, 2017

Choose a reason for hiding this comment

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

Actually, I don't think that would work because I use res below for other operations. Or am I 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.

Seems like this has been removed?

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, this has been updated.

@@ -2,6 +2,7 @@
// on data received from a CONIKS directory.
// These include data binding proof verification,
// and non-equivocation checks.
// TODO: move all STR-verifying functionality to a separate module
Copy link
Member

Choose a reason for hiding this comment

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

Could we open an issue for that so we won't forget about it? Or is this planned to happen in this PR?

Copy link
Member Author

Choose a reason for hiding this comment

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

@c633 and I are already working on this in a separate branch, but we haven't opened the PR yet.

@@ -100,6 +101,50 @@ func (cc *ConsistencyChecks) HandleResponse(requestType int, msg *Response,
return CheckPassed
}

// handleDirectorySTRs is supposed to be used by CONIKS clients to
Copy link
Member

Choose a reason for hiding this comment

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

Case doesn't match with method signature (handleDirectorySTRs vs HandleDirectorySTRs).

}
return nil
case *ObservedSTRs:
if df.STR == nil || len(df.STR) < 1 {
Copy link
Member

Choose a reason for hiding this comment

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

This can be simplified: if len(df.STR) == 0

Copy link
Member Author

Choose a reason for hiding this comment

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

Won't this throw a null pointer error if we don't assert df.STR != nil?

// The signKey param either comes from a client's
// pinned signing key in cc, or an auditor's pinned signing key
// in its history.
// In the case of a client-side consistency check, verifySTRConsistency()
Copy link
Member

Choose a reason for hiding this comment

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

We can remove this line of comment since we're no longer used cc anymore.

func (cc *ConsistencyChecks) verifySTRConsistency(savedSTR, str *DirSTR) error {
// It uses the signing key signKey to verify the STR's signature.
// The signKey param either comes from a client's
// pinned signing key in cc, or an auditor's pinned signing key
Copy link
Member

Choose a reason for hiding this comment

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

Should just be pinned signing key, or an ... (no more cc here).

// HandleDirectorySTRs is supposed to be used by CONIKS clients to
// handle auditor responses, and by CONIKS auditors to handle directory
// responses.
func HandleDirectorySTRs(requestType int, msg *Response, signKey sign.PublicKey,
Copy link
Member

Choose a reason for hiding this comment

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

Since it is being used nowhere and we are working on this in #170, I would like to remove it from this PR.

@@ -90,6 +92,34 @@ type MonitoringRequest struct {
EndEpoch uint64 `json:"end_epoch"`
}

// An AuditingRequest is a message with a CONIKS key directory's address
// as a string that a CONIKS client sends to a CONIKS auditor, or a CONIKS auditor
Copy link
Member

@vqhuy vqhuy Mar 13, 2017

Choose a reason for hiding this comment

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

Is this supposed to be used for an auditor to request to a directory? If so, the DirectoryAddr field seems to be redundant.

Copy link
Member Author

@masomel masomel Mar 13, 2017

Choose a reason for hiding this comment

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

When I first wrote this, it was supposed to be used by both clients and auditors, but this doesn't really make sense. We should have separate message types for auditor-server communication and client-auditor communication. So you're right, having the DirectoryAddr field only makes sense for client-auditor communication.


// An AuditingInEpochRequest is a message with a key directory's address
// as a string and an epoch as a uint64 that a CONIKS client sends to
// a CONIKS auditor, or a CONIKS auditor sends to a CONIKS directory,
Copy link
Member

Choose a reason for hiding this comment

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

@@ -216,6 +262,40 @@ func NewMonitoringProof(ap []*m.AuthenticationPath,
}, ReqSuccess
}

// NewObservedSTR creates the response message a CONIKS auditor
// sends to a client, or a CONIKS directory sends to an auditor,
Copy link
Member

Choose a reason for hiding this comment

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

If we use a pub-sub architecture, will this be used between an auditor and a directory?

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 in either case, pub-sub or request-response, the implementation of the corresponding auditor-directory message would be the same, so it would make sense to use the same function to return a list of requested STRs. Similarly, the response message type for a client-auditor request and an auditor-server request for STRs should be the same type. This being said, since this PR is only for client-auditor communication, I won't add relevant auditor-directory documentation yet.

@masomel masomel changed the title Auditing messages Client-auditor protocol Mar 16, 2017
@liamsi
Copy link
Member

liamsi commented Apr 4, 2017 via email

// computeInitSTRHash is a wrapper for the digest function;
// returns empty string if the STR isn't an initial STR (i.e. str.Epoch != 0)
func computeInitSTRHash(initSTR *DirSTR) string {
if initSTR.Epoch != 0 {
Copy link
Member

Choose a reason for hiding this comment

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

I think we should either remove this check or panic here.

Copy link
Member

Choose a reason for hiding this comment

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

I refactored this function in 92cee27 (it would be used by the clients, too).
Feel free to revert if you don't like it.

Copy link
Member Author

Choose a reason for hiding this comment

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

This refactoring LGTM.

// log; the auditor will add an entry for each CONIKS directory
// the first time it observes an STR for that directory.
func NewAuditLog() ConiksAuditLog {
l := make(map[string]*directoryHistory)
Copy link
Member

Choose a reason for hiding this comment

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

return make(map[string]*directoryHistory)

)

type directoryHistory struct {
name string
Copy link
Member

Choose a reason for hiding this comment

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

What is name? I see below you're putting addr there, but where / why is this used?

Copy link
Member Author

Choose a reason for hiding this comment

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

This used to be dirName for directory domain name (i.e. its address), which is why we assign it addr below. My plan right now is to use this field when auditors query the directories they log every epoch.

// validate the entries themselves. It returns true if an entry exists,
// and false otherwise.
func (l ConiksAuditLog) IsKnownDirectory(dirInitHash string) bool {
h := l[dirInitHash]
Copy link
Member

Choose a reason for hiding this comment

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

_, ok := l[dirInitHash]
return 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.

Still getting used to some Go idioms...

// masomel: will probably want to write a more generic function
// for "catching up" on a history in case an auditor misses epochs
func (l ConiksAuditLog) Insert(addr string, signKey sign.PublicKey,
hist map[uint64]*DirSTR) error {
Copy link
Member

Choose a reason for hiding this comment

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

hist is confusing here ... maybe snaps instead?

hist map[uint64]*DirSTR) error {

// make sure we're getting an initial STR at the very least
if len(hist) < 1 && hist[0].Epoch != 0 {
Copy link
Member

Choose a reason for hiding this comment

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

hist is a map so it's possible that the length is greater than 1 but hist[0] returns nil. Since you're using it in several places below, cache it as initSTR, ok := hist[0] and then check ok here.

Copy link
Member

Choose a reason for hiding this comment

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

Good catch! Can we change it to a slice?

Copy link
Member Author

Choose a reason for hiding this comment

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

A slice would also make sense. Then we would have to check that the epoch for the STR in each element corresponds to the index, which seems fine to me.

// in this particular call, the latestSTR has already been
// inserted into the snapshots map in the loop above
h.updateLatestSTR(hist[endEp-1])
l[dirInitHash] = h
Copy link
Member

Choose a reason for hiding this comment

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

Maybe don't expose the internals of l and add a method, l.Add(dirInitHash, h) or something.

return ErrAuditLog
}

h := l[dirInitHash]
Copy link
Member

Choose a reason for hiding this comment

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

Same thing here, l.Get(dirInitHash) or similar.


// verify the consistency of each new STR before inserting
// into the audit log
err := verifySTRConsistency(signKey, h.snapshots[ep-1], str)
Copy link
Member

Choose a reason for hiding this comment

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

See the comment below, but this would then become (signKey, h.lastestSTR, str)

}

// update the latest STR
h.updateLatestSTR(newSTR)
Copy link
Member

Choose a reason for hiding this comment

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

With the refactor suggested above, these two operations (verifySTRConsistency, updateLatestSTR) can be combined into an h.internalUpdate(newSTR).

Copy link
Member Author

Choose a reason for hiding this comment

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

Because of what I'm doing for #170, I need to see if it makes sense to refactor these operations both on the client and auditor. I'll probably hold off on that for now.

return NewErrorResponse(ReqUnknownDirectory), ReqUnknownDirectory
}

h := l[req.DirInitSTRHash]
Copy link
Member

Choose a reason for hiding this comment

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

Or, maybe we should embrace that it's a map and always just use h, ok := and drop the IsKnownDirectory method.

Copy link
Member Author

Choose a reason for hiding this comment

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

// dirInitHash from the ConiksAuditLog.
// Get() also returns a boolean indicating whether the requested dirInitHash
// is present in the log.
func (l ConiksAuditLog) Get(dirInitHash string) (*directoryHistory, bool) {
Copy link
Member

@vqhuy vqhuy Jul 7, 2017

Choose a reason for hiding this comment

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

Can we unexport this function? Same with Set.

snaps []*DirSTR) error {

// make sure we're getting an initial STR at the very least
if len(snaps) < 1 && snaps[0].Epoch != 0 {
Copy link
Member

Choose a reason for hiding this comment

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

Should it be len(snaps) < 1 || snaps[0].Epoch != 0?

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 yes, this should be an OR. Thank you for catching that.

// (i.e. snaps is missing an epoch between 0 and the latest given)
for i := 1; i < len(snaps); i++ {
str := snaps[i]
if str == nil || str.Epoch != uint64(i) {
Copy link
Member

Choose a reason for hiding this comment

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

The verifySTRConsistency would make sure that str.Epoch == i, so we can remove the check here.

Copy link
Member Author

Choose a reason for hiding this comment

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

This check is only needed because snaps is a slice, and it's being checked in the loop. This property isn't inherent in the consistency check. Plus, verifySTRConsistency already does this check implicitly for one STR when it calls VerifyHashchain.

Copy link
Member

Choose a reason for hiding this comment

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

I do think that this property is inherent in the consistency check, since we are verify using the initial STR, and it would fail if str.Epoch != latestSTR.Epoch + 1, which means str.Epoch == i has to be true.

Copy link
Member Author

@masomel masomel Jul 7, 2017

Choose a reason for hiding this comment

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

What I meant is that i == str.Epoch is only meaningful because we expect the snaps slice to have this property. i isn't meaningful to consistency checks in the general sense, so I don't see why we should impose this particular assumption on the consistency checks. And VerifyHashchain called in verifySTRConsistency already does the str.Epoch != latestSTR.Epoch + 1 check.


// verify the consistency of each new STR before inserting
// into the audit log
err := verifySTRConsistency(signKey, h.latestSTR, str)
Copy link
Member

Choose a reason for hiding this comment

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

if err := verifySTRConsistency(signKey, h.latestSTR, str); err != nil {
  return err
}

@masomel masomel merged commit ae837f6 into master Jul 7, 2017
@vqhuy vqhuy deleted the auditor-protocol branch July 7, 2017 18:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants