From 1a38aca7806f25ba14ed19977c66dbf07dae170d Mon Sep 17 00:00:00 2001 From: Marcela Melara Date: Wed, 14 Dec 2016 15:43:32 -0500 Subject: [PATCH 01/43] Add client-auditor messages --- protocol/message.go | 122 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 100 insertions(+), 22 deletions(-) diff --git a/protocol/message.go b/protocol/message.go index 5053f8f..0d446c8 100644 --- a/protocol/message.go +++ b/protocol/message.go @@ -15,16 +15,16 @@ const ( ) // A Request message defines the data a CONIKS client must send to a CONIKS -// server for a particular request. +// directory for a particular request. type Request struct { Type int Request interface{} } // A RegistrationRequest is a message with a username as a string and a -// public key as bytes that a CONIKS client sends to a CONIKS server -// to register a new entry (i.e. name-to-key binding) with a CONIKS -// key directory. Optionally, the client can include the user's key +// public key as bytes that a CONIKS client sends to a CONIKS directory +// to register a new entry (i.e. name-to-key binding). +// Optionally, the client can include the user's key // change and visibility policies as boolean values in the // request. These flags are currently unused by the CONIKS protocols. // @@ -38,7 +38,7 @@ type RegistrationRequest struct { } // A KeyLookupRequest is a message with a username as a string -// that a CONIKS client sends to a CONIKS server to retrieve the +// that a CONIKS client sends to a CONIKS directory to retrieve the // public key bound to the given username at the latest epoch. // If the client needs to look up a username's key for a prior epoch, it // must send a KeyLookupInEpochRequest. @@ -51,15 +51,15 @@ type KeyLookupRequest struct { } // A KeyLookupInEpochRequest is a message with a username as a string and -// an epoch as a uint64 that a CONIKS client sends to the server to +// an epoch as a uint64 that a CONIKS client sends to the directory to // retrieve the public key bound to the username in the given epoch. // The client sends this request type when it needs to obtain // a user's key for a past epoch. The client can send a KeyLookupRequest // if it needs to look up a user's key for the latest epoch. // // The response to a successful request is a DirectoryProofs with an AP -// of length 1 containing the auth path for the requested Epoch, and an -// STR covering the epoch range [Epoch, d.LatestSTR().Epoch]. +// of length 1 containing the auth path for the requested Epoch, and a list +// of STRs covering the epoch range [Epoch, d.LatestSTR().Epoch]. type KeyLookupInEpochRequest struct { Username string `json:"username"` Epoch uint64 `json:"epoch"` @@ -67,7 +67,7 @@ type KeyLookupInEpochRequest struct { // A MonitoringRequest is a message with a username as a string and the // start and end epochs of an epoch range as two uint64 that a CONIKS -// client sends to the server to monitor the given user's key in a CONIKS +// client sends to the directory to monitor the given user's key in a CONIKS // key directory, i.e. to ensure that the key bound to the username hasn't // changed unexpectedly. // @@ -90,23 +90,50 @@ 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 to request +// the latest STR the auditor has observed for the given directory. +// If the client needs to request a directory's STR for a prior epoch, it +// must send an AuditingInEpochRequest. +// +// The response to a successful request is an ObservedDirectoryProof. +type AuditingRequest struct { + DirectoryAddr string `json:"directory_addr"` +} + +// 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 to retrieve the STR it observed for the directory in +// the given epoch. The client sends this request type when it needs to +// audit a directory's STR for a prior epoch (i.e. as part of a +// key lookup in epoch check or a monitoring check). The client can send an +// AuditingRequest if it needs to audit a directory's STR for its latest +// epoch. +// +// The response to a successful request is an ObservedDirectoryProofs with +// a list of STRs covering the epoch range [Epoch, d.LatestSTR().Epoch]. +type AuditingInEpochRequest struct { + DirectoryAddr string `json:"directory_addr"` + Epoch uint64 `json:"epoch"` +} + // A Response message indicates the result of a CONIKS client request // with an appropriate error code, and defines the set of cryptographic -// proofs a CONIKS server must return as part of its response. +// proofs a CONIKS directory must return as part of its response. type Response struct { Error ErrorCode DirectoryResponse `json:",omitempty"` } // A DirectoryResponse is a message that includes cryptographic proofs -// about the key directory that a CONIKS key server returns to a -// CONIKS client. +// about the key directory that a CONIKS key directory or auditor returns +// to a CONIKS client. type DirectoryResponse interface{} // A DirectoryProof response includes an authentication path AP for a // given username-to-key binding in the directory, a signed tree root // STR, and optionally a temporary binding for the given binding for a -// single epoch. A CONIKS server returns this DirectoryResponse +// single epoch. A CONIKS directory returns this DirectoryResponse // type upon a RegistrationRequest or a KeyLookupRequest. type DirectoryProof struct { AP *m.AuthenticationPath @@ -116,24 +143,41 @@ type DirectoryProof struct { // A DirectoryProofs response includes a list of authentication paths // AP for a given username-to-key binding in the directory and a list of -// signed tree roots STR for a range of epochs. A CONIKS server returns -// this DirectoryResponse -// type upon a KeyLookupInEpochRequest or a MonitoringRequest. +// signed tree roots STR for a range of epochs. A CONIKS directory returns +// this DirectoryResponse type upon a KeyLookupInEpochRequest or a +// MonitoringRequest. type DirectoryProofs struct { AP []*m.AuthenticationPath STR []*m.SignedTreeRoot } +// An ObservedSTR response includes a single signed tree root +// STR. A CONIKS auditor returns this DirectoryResponse type upon an +// AuditingRequest. +type ObservedSTR struct { + STR *m.SignedTreeRoot +} + +// An ObservedSTRs response includes a list of signed tree roots +// STR. A CONIKS auditor returns this DirectoryResponse type upon an +// AudutingRequest. +type ObservedSTRs struct { + STR []*m.SignedTreeRoot +} + // NewErrorResponse creates a new response message indicating the error -// that occurred while a CONIKS server was processing a client request. +// that occurred while a CONIKS directory or a CONIKS auditor was +// processing a client request. func NewErrorResponse(e ErrorCode) *Response { return &Response{Error: e} } var _ DirectoryResponse = (*DirectoryProof)(nil) var _ DirectoryResponse = (*DirectoryProofs)(nil) +var _ DirectoryResponse = (*ObservedSTR)(nil) +var _ DirectoryResponse = (*ObservedSTRs)(nil) -// NewRegistrationProof creates the response message a CONIKS server +// NewRegistrationProof creates the response message a CONIKS directory // sends to a client upon a RegistrationRequest, // and returns a Response containing a DirectoryProof struct. // directory.Register() passes an authentication path ap, temporary binding @@ -154,7 +198,7 @@ func NewRegistrationProof(ap *m.AuthenticationPath, str *m.SignedTreeRoot, }, e } -// NewKeyLookupProof creates the response message a CONIKS server +// NewKeyLookupProof creates the response message a CONIKS directory // sends to a client upon a KeyLookupRequest, // and returns a Response containing a DirectoryProof struct. // directory.KeyLookup() passes an authentication path ap, temporary binding @@ -175,7 +219,7 @@ func NewKeyLookupProof(ap *m.AuthenticationPath, str *m.SignedTreeRoot, }, e } -// NewKeyLookupInEpochProof creates the response message a CONIKS server +// NewKeyLookupInEpochProof creates the response message a CONIKS directory // sends to a client upon a KeyLookupRequest, // and returns a Response containing a DirectoryProofs struct. // directory.KeyLookupInEpoch() passes an authentication path ap and error @@ -196,7 +240,7 @@ func NewKeyLookupInEpochProof(ap *m.AuthenticationPath, }, e } -// NewMonitoringProof creates the response message a CONIKS server +// NewMonitoringProof creates the response message a CONIKS directory // sends to a client upon a MonitoringRequest, // and returns a Response containing a DirectoryProofs struct. // directory.Monitor() passes a list of authentication paths ap and a @@ -215,6 +259,40 @@ func NewMonitoringProof(ap []*m.AuthenticationPath, }, ReqSuccess } +// NewObservedSTR creates the response message a CONIKS auditor +// sends to a client upon an AuditingRequest, +// and returns a Response containing an ObservedSTR struct. +// auditlog.Audit() passes the signed tree root for the auditor's latest +// observed epoch str. +// +// See auditlog.Audit() for details on the contents of the created +// ObservedSTR. +func NewObservedSTR(str *m.SignedTreeRoot) (*Response, ErrorCode) { + return &Response{ + Error: ReqSuccess, + DirectoryResponse: &ObservedSTR{ + STR: str, + }, + }, ReqSuccess +} + +// NewObservedSTRs creates the response message a CONIKS auditor +// sends to a client upon an AuditingInEpochRequest, +// and returns a Response containing an ObservedSTRs struct. +// auditlog.AuditInEpoch() passes a list of signed tree roots +// that the auditor observed for the requested range of epochs str. +// +// See auditlog.AuditInEpoch() for details on the contents of the created +// ObservedSTRs. +func NewObservedSTRs(str []*m.SignedTreeRoot) (*Response, ErrorCode) { + return &Response{ + Error: ReqSuccess, + DirectoryResponse: &ObservedSTRs{ + STR: str, + }, + }, ReqSuccess +} + func (msg *Response) validate() error { if Errors[msg.Error] { return msg.Error @@ -234,7 +312,7 @@ func (msg *Response) validate() error { } // GetKey returns the key extracted from -// a validated CONIKS server's response. +// a validated CONIKS directory's response. // // If the response contains a single authentication path, // the key is obtained from that authentication path or the From a5e78dedbf0811f335ac44e5e1c20a8ff53f01d2 Mon Sep 17 00:00:00 2001 From: Marcela M Date: Thu, 15 Dec 2016 14:08:41 -0500 Subject: [PATCH 02/43] Add client-auditor messages --- protocol/message.go | 81 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 2 deletions(-) diff --git a/protocol/message.go b/protocol/message.go index f7a7d8d..0d446c8 100644 --- a/protocol/message.go +++ b/protocol/message.go @@ -90,6 +90,33 @@ 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 to request +// the latest STR the auditor has observed for the given directory. +// If the client needs to request a directory's STR for a prior epoch, it +// must send an AuditingInEpochRequest. +// +// The response to a successful request is an ObservedDirectoryProof. +type AuditingRequest struct { + DirectoryAddr string `json:"directory_addr"` +} + +// 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 to retrieve the STR it observed for the directory in +// the given epoch. The client sends this request type when it needs to +// audit a directory's STR for a prior epoch (i.e. as part of a +// key lookup in epoch check or a monitoring check). The client can send an +// AuditingRequest if it needs to audit a directory's STR for its latest +// epoch. +// +// The response to a successful request is an ObservedDirectoryProofs with +// a list of STRs covering the epoch range [Epoch, d.LatestSTR().Epoch]. +type AuditingInEpochRequest struct { + DirectoryAddr string `json:"directory_addr"` + Epoch uint64 `json:"epoch"` +} + // A Response message indicates the result of a CONIKS client request // with an appropriate error code, and defines the set of cryptographic // proofs a CONIKS directory must return as part of its response. @@ -99,7 +126,7 @@ type Response struct { } // A DirectoryResponse is a message that includes cryptographic proofs -// about the key directory that a CONIKS key directory returns +// about the key directory that a CONIKS key directory or auditor returns // to a CONIKS client. type DirectoryResponse interface{} @@ -124,8 +151,22 @@ type DirectoryProofs struct { STR []*m.SignedTreeRoot } +// An ObservedSTR response includes a single signed tree root +// STR. A CONIKS auditor returns this DirectoryResponse type upon an +// AuditingRequest. +type ObservedSTR struct { + STR *m.SignedTreeRoot +} + +// An ObservedSTRs response includes a list of signed tree roots +// STR. A CONIKS auditor returns this DirectoryResponse type upon an +// AudutingRequest. +type ObservedSTRs struct { + STR []*m.SignedTreeRoot +} + // NewErrorResponse creates a new response message indicating the error -// that occurred while a CONIKS directory was +// that occurred while a CONIKS directory or a CONIKS auditor was // processing a client request. func NewErrorResponse(e ErrorCode) *Response { return &Response{Error: e} @@ -133,6 +174,8 @@ func NewErrorResponse(e ErrorCode) *Response { var _ DirectoryResponse = (*DirectoryProof)(nil) var _ DirectoryResponse = (*DirectoryProofs)(nil) +var _ DirectoryResponse = (*ObservedSTR)(nil) +var _ DirectoryResponse = (*ObservedSTRs)(nil) // NewRegistrationProof creates the response message a CONIKS directory // sends to a client upon a RegistrationRequest, @@ -216,6 +259,40 @@ func NewMonitoringProof(ap []*m.AuthenticationPath, }, ReqSuccess } +// NewObservedSTR creates the response message a CONIKS auditor +// sends to a client upon an AuditingRequest, +// and returns a Response containing an ObservedSTR struct. +// auditlog.Audit() passes the signed tree root for the auditor's latest +// observed epoch str. +// +// See auditlog.Audit() for details on the contents of the created +// ObservedSTR. +func NewObservedSTR(str *m.SignedTreeRoot) (*Response, ErrorCode) { + return &Response{ + Error: ReqSuccess, + DirectoryResponse: &ObservedSTR{ + STR: str, + }, + }, ReqSuccess +} + +// NewObservedSTRs creates the response message a CONIKS auditor +// sends to a client upon an AuditingInEpochRequest, +// and returns a Response containing an ObservedSTRs struct. +// auditlog.AuditInEpoch() passes a list of signed tree roots +// that the auditor observed for the requested range of epochs str. +// +// See auditlog.AuditInEpoch() for details on the contents of the created +// ObservedSTRs. +func NewObservedSTRs(str []*m.SignedTreeRoot) (*Response, ErrorCode) { + return &Response{ + Error: ReqSuccess, + DirectoryResponse: &ObservedSTRs{ + STR: str, + }, + }, ReqSuccess +} + func (msg *Response) validate() error { if Errors[msg.Error] { return msg.Error From da139a71ae21bd4c89e79e915b345bf268c20c61 Mon Sep 17 00:00:00 2001 From: Marcela Date: Wed, 21 Dec 2016 17:07:50 -0500 Subject: [PATCH 03/43] Create audit log structure, query API finished --- protocol/auditlog.go | 183 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 protocol/auditlog.go diff --git a/protocol/auditlog.go b/protocol/auditlog.go new file mode 100644 index 0000000..9521cb3 --- /dev/null +++ b/protocol/auditlog.go @@ -0,0 +1,183 @@ +// This module implements a CONIKS audit log that a CONIKS auditor +// maintains. +// An audit log is a mirror of many CONIKS key directories' STR history, +// allowing CONIKS clients to audit the CONIKS directories. + +package protocol + +import ( + "github.com/coniks-sys/coniks-go/crypto/sign" + m "github.com/coniks-sys/coniks-go/merkletree" +) + +type directoryHistory struct { + signKey sign.PublicKey + snapshots map[uint64]*m.SignedTreeRoot + latestSTR *m.SignedTreeRoot +} + +// A ConiksAuditLog maintains the histories +// of all CONIKS directories known to a CONIKS auditor. +// Each history includes the directory's public signing key +// enabling the auditor to verify the corresponding signed +// tree roots. +type ConiksAuditLog struct { + histories map[string]*directoryHistory +} + +func newDirectoryHistory(signKey sign.PublicKey, str *m.SignedTreeRoot) *directoryHistory { + h := new(directoryHistory) + h.signKey = signKey + h.snapshots = make(map[uint64]*m.SignedTreeRoot) + h.latestSTR = str + return h +} + +// NewAuditLog constructs a new ConiksAuditLog. It creates an empty +// 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 := new(ConiksAuditLog) + l.histories = make(map[string]*directoryHistory) + return l +} + +// 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, +// and false otherwise. +func (l *ConiksAuditLog) IsKnownDirectory(addr string) bool { + h := l.histories[addr] + if h != nil { + return true + } + return false +} + +// FIXME: pass Request 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 +func (l *ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, + oldSTRs map[uint64]*m.SignedTreeRoot, latestSTR *m.SignedTreeRoot) error { + + // panic if we want to create a new entry for an addr we already know + if l.IsKnownDirectory(addr) { + panic("[protocol] Trying to add an audit log entry for an existing address") + } + + // create the new directory history + h := newDirectoryHistory(signKey, latestSTR) + + startEp := uint64(0) + endEp := latestSTR.Epoch + + // add each old STR into the history + for ep := startEp; ep < endEp; ep++ { + if str := oldSTRs[ep]; str != nil { + h.snapshots[ep] = str + } + return ErrMalformedDirectoryMessage + } + + // FIXME: verify the consistency of each new STR + return nil +} + +// Update verifies the consistency of a newly observed STR newSTR for +// the directory addr, and inserts the newSTR into addr's directory history +// if the checks (i.e. STR signature and hash chain verifications) pass. +// Update() returns nil if the checks pass, and the appropriate consistency +// check error otherwise. Update() assumes that Insert() has been called for +// addr prior to its first call and thereby expects that an entry for addr +// exists in the audit log l. +// FIXME: pass Request 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 + if !l.IsKnownDirectory(addr) { + panic("[protocol] Trying to update audit log entry for non-existent address") + } + + h := l.histories[addr] + + if err := h.verifySTRConsistency(newSTR); err != nil { + return err + } + + // update the latest STR + h.snapshots[h.latestSTR.Epoch] = h.latestSTR + h.latestSTR = newSTR + return nil +} + +// verifySTRConsistency checks the consistency between 2 snapshots. +// It uses the pinned signing key in the directory history +// to verify the STR's signature and verifies +// the hash chain using the latestSTR stored in the history. +// TODO: dedup this: write generic verifySTRConsistency +func (h *directoryHistory) verifySTRConsistency(str *m.SignedTreeRoot) error { + // verify STR's signature + if !h.signKey.Verify(str.Serialize(), str.Signature) { + return CheckBadSignature + } + if str.VerifyHashChain(h.latestSTR) { + return nil + } + + // TODO: verify the directory's policies as well. See #115 + return CheckBadSTR +} + +// TODO add doc +func (l *ConiksAuditLog) Query(req *AuditingRequest) (*Response, ErrorCode) { + + // make sure the request is well-formed + if len(req.DirectoryAddr) <= 0 { + return NewErrorResponse(ErrMalformedClientMessage), + ErrMalformedClientMessage + } + + if h := l.histories[req.DirectoryAddr]; h != nil { + return NewObservedSTR(h.latestSTR) + } + + // FIXME: return ErrUnknownDirectory + return nil, ErrDirectory +} + +// TODO: add doc +func (l *ConiksAuditLog) QueryInEpoch(req *AuditingInEpochRequest) (*Response, + ErrorCode) { + + // make sure the request is well-formed + if len(req.DirectoryAddr) <= 0 { + return NewErrorResponse(ErrMalformedClientMessage), + ErrMalformedClientMessage + } + + if h := l.histories[req.DirectoryAddr]; h != nil { + + // also make sure the epoch is well-formed + if req.Epoch > h.latestSTR.Epoch { + return NewErrorResponse(ErrMalformedClientMessage), + ErrMalformedClientMessage + } + + var strs []*m.SignedTreeRoot + startEp := req.Epoch + endEp := h.latestSTR.Epoch + + for ep := startEp; ep < endEp; ep++ { + str := h.snapshots[ep] + strs = append(strs, str) + } + + // don't forget to append the latest STR + strs = append(strs, h.latestSTR) + + return NewObservedSTRs(strs) + } + + // FIXME: return ErrUnknownDirectory + return nil, ErrDirectory +} From 3cd61f13178ff8a1ebfa565e36b4bfea08160deb Mon Sep 17 00:00:00 2001 From: Marcela Melara Date: Thu, 16 Feb 2017 15:06:23 -0500 Subject: [PATCH 04/43] Add/Update docs to include auditor, add ReqUnknownDirectory auditor error --- protocol/auditlog.go | 47 ++++++++++++++++++++++++++++++++++++-------- protocol/error.go | 5 ++++- protocol/message.go | 29 ++++++++++++++------------- 3 files changed, 58 insertions(+), 23 deletions(-) diff --git a/protocol/auditlog.go b/protocol/auditlog.go index 9521cb3..3f50141 100644 --- a/protocol/auditlog.go +++ b/protocol/auditlog.go @@ -128,8 +128,23 @@ func (h *directoryHistory) verifySTRConsistency(str *m.SignedTreeRoot) error { return CheckBadSTR } -// TODO add doc -func (l *ConiksAuditLog) Query(req *AuditingRequest) (*Response, ErrorCode) { +// GetObservedSTR gets the observed STR for the CONIKS directory address indicated +// in the AuditingRequest req received from a CONIKS client from the auditor's latest +// directory history entry, and returns a tuple of the form +// (response, error). +// The response (which also includes the error code) is supposed to +// be sent back to the client. The returned error is used by the auditor +// for logging purposes. +// +// A request without a directory address is considered +// malformed, and causes GetObservedSTR() to return a +// message.NewErrorResponse(ErrMalformedClientMessage) tuple. +// GetObservedSTR() returns a message.NewObservedSTR(str) tuple. +// str is the signed tree root the auditor has observed for the latest epoch. +// If the auditor doesn't have any history entries for the requested CONIKS +// directory, GetObservedSTR() returns a +// message.NewErrorResponse(ReqUnknownDirectory) tuple. +func (l *ConiksAuditLog) GetObservedSTR(req *AuditingRequest) (*Response, ErrorCode) { // make sure the request is well-formed if len(req.DirectoryAddr) <= 0 { @@ -141,12 +156,29 @@ func (l *ConiksAuditLog) Query(req *AuditingRequest) (*Response, ErrorCode) { return NewObservedSTR(h.latestSTR) } - // FIXME: return ErrUnknownDirectory - return nil, ErrDirectory + return NewErrorResponse(ReqUnknownDirectory), ReqUnknownDirectory } -// TODO: add doc -func (l *ConiksAuditLog) QueryInEpoch(req *AuditingInEpochRequest) (*Response, +// GetObservedSTRInEpoch gets the observed STR for the CONIKS directory address +// for a prior directory history entry indicated in the +// AuditingInEpochRequest req received from a CONIKS client, +// and returns a tuple of the form (response, error). +// The response (which also includes the error code) is supposed to +// be sent back to the client. The returned error is used by the auditor +// for logging purposes. +// +// A request without a directory address or with an epoch greater than the latest +// observed epoch of this directory is considered malformed, and causes +// GetObservedSTRInEpoch() to return a +// message.NewErrorResponse(ErrMalformedClientMessage) tuple. +// GetObservedSTRInEpoch() returns a message.NewObservedSTRs(strs) tuple. +// strs is a list of STRs for the epoch range [ep, +// l.histories[req.DirectoryAddr].latestSTR.Epoch], where ep is the past epoch +// for which the client has requested the observed STR. +// If the auditor doesn't have any history entries for the requested CONIKS +// directory, GetObservedSTR() returns a +// message.NewErrorResponse(ReqUnknownDirectory) tuple. +func (l *ConiksAuditLog) GetObservedSTRInEpoch(req *AuditingInEpochRequest) (*Response, ErrorCode) { // make sure the request is well-formed @@ -178,6 +210,5 @@ func (l *ConiksAuditLog) QueryInEpoch(req *AuditingInEpochRequest) (*Response, return NewObservedSTRs(strs) } - // FIXME: return ErrUnknownDirectory - return nil, ErrDirectory + return NewErrorResponse(ReqUnknownDirectory), ReqUnknownDirectory } diff --git a/protocol/error.go b/protocol/error.go index 866d508..86c2bf4 100644 --- a/protocol/error.go +++ b/protocol/error.go @@ -8,7 +8,8 @@ package protocol // An ErrorCode implements the built-in error interface type. type ErrorCode int -// These codes indicate the status of a client-server message exchange. +// These codes indicate the status of a client-server or client-auditor message +// exchange. // Codes prefixed by "Req" indicate different client request results. // Codes prefixed by "Err" indicate an internal server error or a malformed // message. @@ -16,6 +17,8 @@ const ( ReqSuccess ErrorCode = iota + 100 ReqNameExisted ReqNameNotFound + // auditor->client: no observed history for the requested directory + ReqUnknownDirectory ErrDirectory ErrMalformedClientMessage diff --git a/protocol/message.go b/protocol/message.go index 0d446c8..03b62e0 100644 --- a/protocol/message.go +++ b/protocol/message.go @@ -91,26 +91,27 @@ type MonitoringRequest struct { } // An AuditingRequest is a message with a CONIKS key directory's address -// as a string that a CONIKS client sends to a CONIKS auditor to request -// the latest STR the auditor has observed for the given directory. -// If the client needs to request a directory's STR for a prior epoch, it +// as a string that a CONIKS client sends to a CONIKS auditor, or a CONIKS auditor +// sends to a CONIKS directory, to request the given directory's latest STR. +// If the client/auditor needs to request a directory's STR for a prior epoch, it // must send an AuditingInEpochRequest. // -// The response to a successful request is an ObservedDirectoryProof. +// The response to a successful request is an ObservedSTR. type AuditingRequest struct { DirectoryAddr string `json:"directory_addr"` } // 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 to retrieve the STR it observed for the directory in -// the given epoch. The client sends this request type when it needs to +// a CONIKS auditor, or a CONIKS auditor sends to a CONIKS directory, +// to retrieve the STR for the directory in +// the given epoch. The client/auditor sends this request type when it needs to // audit a directory's STR for a prior epoch (i.e. as part of a -// key lookup in epoch check or a monitoring check). The client can send an -// AuditingRequest if it needs to audit a directory's STR for its latest -// epoch. +// key lookup in epoch check, a monitoring check, or an auditor update). +// The client/auditor can send an AuditingRequest if it needs to audit a +// directory's STR for its latest epoch. // -// The response to a successful request is an ObservedDirectoryProofs with +// The response to a successful request is an ObservedSTRs with // a list of STRs covering the epoch range [Epoch, d.LatestSTR().Epoch]. type AuditingInEpochRequest struct { DirectoryAddr string `json:"directory_addr"` @@ -260,10 +261,10 @@ func NewMonitoringProof(ap []*m.AuthenticationPath, } // NewObservedSTR creates the response message a CONIKS auditor -// sends to a client upon an AuditingRequest, -// and returns a Response containing an ObservedSTR struct. -// auditlog.Audit() passes the signed tree root for the auditor's latest -// observed epoch str. +// sends to a client, or a CONIKS directory sends to an auditor, +// upon an AuditingRequest, and returns a Response containing an ObservedSTR struct. +// auditlog.GetObservedSTR(), or directory.GetLatestSTR(), passes the signed +// tree root for the directory's latest str. // // See auditlog.Audit() for details on the contents of the created // ObservedSTR. From 6577d92928edb8b80448c9bfebb02e62b6d46257 Mon Sep 17 00:00:00 2001 From: Marcela Melara Date: Thu, 16 Feb 2017 15:20:16 -0500 Subject: [PATCH 05/43] Use single generic verifySTRConsistency to be used by client and auditor --- protocol/auditlog.go | 20 +------------------- protocol/consistencychecks.go | 15 +++++++++------ 2 files changed, 10 insertions(+), 25 deletions(-) diff --git a/protocol/auditlog.go b/protocol/auditlog.go index 3f50141..373a7f4 100644 --- a/protocol/auditlog.go +++ b/protocol/auditlog.go @@ -100,7 +100,7 @@ func (l *ConiksAuditLog) Update(addr string, newSTR *m.SignedTreeRoot) error { h := l.histories[addr] - if err := h.verifySTRConsistency(newSTR); err != nil { + if err := verifySTRConsistency(h.signKey, h.latestSTR, newSTR); err != nil { return err } @@ -110,24 +110,6 @@ func (l *ConiksAuditLog) Update(addr string, newSTR *m.SignedTreeRoot) error { return nil } -// verifySTRConsistency checks the consistency between 2 snapshots. -// It uses the pinned signing key in the directory history -// to verify the STR's signature and verifies -// the hash chain using the latestSTR stored in the history. -// TODO: dedup this: write generic verifySTRConsistency -func (h *directoryHistory) verifySTRConsistency(str *m.SignedTreeRoot) error { - // verify STR's signature - if !h.signKey.Verify(str.Serialize(), str.Signature) { - return CheckBadSignature - } - if str.VerifyHashChain(h.latestSTR) { - return nil - } - - // TODO: verify the directory's policies as well. See #115 - return CheckBadSTR -} - // GetObservedSTR gets the observed STR for the CONIKS directory address indicated // in the AuditingRequest req received from a CONIKS client from the auditor's latest // directory history entry, and returns a tuple of the form diff --git a/protocol/consistencychecks.go b/protocol/consistencychecks.go index ba4a19e..a6d5840 100644 --- a/protocol/consistencychecks.go +++ b/protocol/consistencychecks.go @@ -117,7 +117,7 @@ func (cc *ConsistencyChecks) updateSTR(requestType int, msg *Response) error { return nil } // Otherwise, expect that we've entered a new epoch - if err := cc.verifySTRConsistency(cc.SavedSTR, str); err != nil { + if err := verifySTRConsistency(cc.signKey, cc.SavedSTR, str); err != nil { return err } @@ -140,12 +140,15 @@ func (cc *ConsistencyChecks) verifySTR(str *m.SignedTreeRoot) error { } // verifySTRConsistency checks the consistency between 2 snapshots. -// It uses the pinned signing key in cc -// to verify the STR's signature and should not verify -// the hash chain using the STR stored in cc. -func (cc *ConsistencyChecks) verifySTRConsistency(savedSTR, str *m.SignedTreeRoot) 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 +// in its history. +// In the case of a client-side consistency check, verifySTRConsistency() +// should not verify the hash chain using the STR stored in cc. +func verifySTRConsistency(signKey sign.PublicKey, savedSTR, str *m.SignedTreeRoot) error { // verify STR's signature - if !cc.signKey.Verify(str.Serialize(), str.Signature) { + if !signKey.Verify(str.Serialize(), str.Signature) { return CheckBadSignature } if str.VerifyHashChain(savedSTR) { From 618d9c0e5bb3e1547614d6e6e55deb71096d4532 Mon Sep 17 00:00:00 2001 From: Marcela M Date: Tue, 7 Mar 2017 19:10:08 -0500 Subject: [PATCH 06/43] Add tests for audit log, debug audit log --- protocol/auditlog.go | 21 ++-- protocol/auditlog_test.go | 220 ++++++++++++++++++++++++++++++++++++++ protocol/error.go | 5 +- 3 files changed, 236 insertions(+), 10 deletions(-) create mode 100644 protocol/auditlog_test.go diff --git a/protocol/auditlog.go b/protocol/auditlog.go index 373a7f4..116d8e7 100644 --- a/protocol/auditlog.go +++ b/protocol/auditlog.go @@ -44,7 +44,7 @@ func NewAuditLog() *ConiksAuditLog { // 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, +// validate the entries themselves. It returns true if an entry exists, // and false otherwise. func (l *ConiksAuditLog) IsKnownDirectory(addr string) bool { h := l.histories[addr] @@ -54,15 +54,15 @@ func (l *ConiksAuditLog) IsKnownDirectory(addr string) bool { return false } -// FIXME: pass Request message as param +// 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 func (l *ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, oldSTRs map[uint64]*m.SignedTreeRoot, latestSTR *m.SignedTreeRoot) error { - // panic if we want to create a new entry for an addr we already know + // error if we want to create a new entry for an addr we already know if l.IsKnownDirectory(addr) { - panic("[protocol] Trying to add an audit log entry for an existing address") + return ErrAuditLog } // create the new directory history @@ -73,12 +73,15 @@ func (l *ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, // add each old STR into the history for ep := startEp; ep < endEp; ep++ { - if str := oldSTRs[ep]; str != nil { - h.snapshots[ep] = str + str := oldSTRs[ep] + if str == nil { + return ErrMalformedDirectoryMessage } - return ErrMalformedDirectoryMessage + h.snapshots[ep] = str } + l.histories[addr] = h + // FIXME: verify the consistency of each new STR return nil } @@ -90,12 +93,12 @@ func (l *ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, // check error otherwise. Update() assumes that Insert() has been called for // addr prior to its first call and thereby expects that an entry for addr // exists in the audit log l. -// FIXME: pass Request message as param +// 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 if !l.IsKnownDirectory(addr) { - panic("[protocol] Trying to update audit log entry for non-existent address") + return ErrAuditLog } h := l.histories[addr] diff --git a/protocol/auditlog_test.go b/protocol/auditlog_test.go new file mode 100644 index 0000000..de489ef --- /dev/null +++ b/protocol/auditlog_test.go @@ -0,0 +1,220 @@ +package protocol + +import ( + m "github.com/coniks-sys/coniks-go/merkletree" + "testing" +) + +func TestInsertEmptyHistory(t *testing.T) { + // let's just create basic test directory and an empty audit log + d, pk := NewTestDirectory(t, true) + aud := NewAuditLog() + + err := aud.Insert("test-server", pk, nil, d.LatestSTR()) + if err != nil { + t.Fatal("Error inserting new server history") + } +} + +func TestUpdateHistory(t *testing.T) { + // let's just create basic test directory and an empty audit log + d, pk := NewTestDirectory(t, true) + aud := NewAuditLog() + + err := aud.Insert("test-server", pk, nil, d.LatestSTR()) + if err != nil { + t.Fatal("Error inserting new server history") + } + + // update the directory so we can update the audit log + d.Update() + err = aud.Update("test-server", d.LatestSTR()) + + if err != nil { + t.Fatal("Error updating the server history") + } +} + +func TestInsertPriorHistory(t *testing.T) { + // let's just create basic test directory and an empty audit log + d, pk := NewTestDirectory(t, true) + aud := NewAuditLog() + + // create 10 epochs + priorSTRs := make(map[uint64]*m.SignedTreeRoot) + for i := 0; i < 10; i++ { + priorSTRs[d.LatestSTR().Epoch] = d.LatestSTR() + d.Update() + } + + // now insert + err := aud.Insert("test-server", pk, priorSTRs, d.LatestSTR()) + if err != nil { + t.Fatal("Error inserting new server history with prior STRs") + } +} + +func TestInsertExistingHistory(t *testing.T) { + // let's just create basic test directory and an empty audit log + d, pk := NewTestDirectory(t, true) + aud := NewAuditLog() + err := aud.Insert("test-server", pk, nil, d.LatestSTR()) + if err != nil { + t.Fatal("Error inserting new server history") + } + + // let's make sure that we can't re-insert a new server history into our log + err = aud.Insert("test-server", pk, nil, d.LatestSTR()) + if err != ErrAuditLog { + t.Fatal("Expected an ErrAuditLog when inserting an existing server history") + } +} + +func TestUpdateUnknownHistory(t *testing.T) { + // let's just create basic test directory and an empty audit log + d, pk := NewTestDirectory(t, true) + aud := NewAuditLog() + err := aud.Insert("test-server", pk, nil, d.LatestSTR()) + if err != nil { + t.Fatal("Error inserting new server history") + } + + // let's make sure that we can't re-insert a new server history into our log + err = aud.Update("unknown", d.LatestSTR()) + if err != ErrAuditLog { + t.Fatal("Expected an ErrAuditLog when updating an unknown server history") + } +} + +func TestGetObservedSTR(t *testing.T) { + // let's just create basic test directory and an empty audit log + d, pk := NewTestDirectory(t, true) + aud := NewAuditLog() + err := aud.Insert("test-server", pk, nil, d.LatestSTR()) + if err != nil { + t.Fatal("Error inserting new server history") + } + + res, err := aud.GetObservedSTR(&AuditingRequest{ + DirectoryAddr: "test-server"}) + obs := res.DirectoryResponse.(*ObservedSTR) + if err != ReqSuccess { + t.Fatal("Unable to get latest observed STR") + } + if obs.STR == nil { + t.Fatal("Expect returned STR to be not nil") + } + if obs.STR.Epoch != d.LatestSTR().Epoch { + t.Fatal("Unexpected epoch for returned STR") + } +} + +func TestGetObservedSTRInEpoch(t *testing.T) { + // let's just create basic test directory and an empty audit log + d, pk := NewTestDirectory(t, true) + aud := NewAuditLog() + + // create 10 epochs + priorSTRs := make(map[uint64]*m.SignedTreeRoot) + for i := 0; i < 10; i++ { + priorSTRs[d.LatestSTR().Epoch] = d.LatestSTR() + d.Update() + } + + // now insert into the log + err := aud.Insert("test-server", pk, priorSTRs, d.LatestSTR()) + if err != nil { + t.Fatal("Error inserting new server history with prior STRs") + } + + res, err := aud.GetObservedSTRInEpoch(&AuditingInEpochRequest{ + DirectoryAddr: "test-server", + Epoch: uint64(6)}) + obs := res.DirectoryResponse.(*ObservedSTRs) + if err != ReqSuccess { + t.Fatal("Unable to get latest range of STRs") + } + if obs.STR == nil { + t.Fatal("Expect returned STR to be not nil") + } + if len(obs.STR) != 5 { + t.Fatal("Expect 5 returned STRs") + } + if obs.STR[0].Epoch != 6 || obs.STR[4].Epoch != d.LatestSTR().Epoch { + t.Fatal("Unexpected epoch for returned STRs") + } +} + +func TestGetObservedSTRUnknown(t *testing.T) { + // let's just create basic test directory and an empty audit log + d, pk := NewTestDirectory(t, true) + aud := NewAuditLog() + + // create 10 epochs + priorSTRs := make(map[uint64]*m.SignedTreeRoot) + for i := 0; i < 10; i++ { + priorSTRs[d.LatestSTR().Epoch] = d.LatestSTR() + d.Update() + } + + // now insert into the log + err := aud.Insert("test-server", pk, priorSTRs, d.LatestSTR()) + if err != nil { + t.Fatal("Error inserting new server history with prior STRs") + } + + _, err = aud.GetObservedSTR(&AuditingRequest{ + DirectoryAddr: "unknown"}) + if err != ReqUnknownDirectory { + t.Fatal("Expect ReqUnknownDirectory for latest STR") + } + + _, err = aud.GetObservedSTRInEpoch(&AuditingInEpochRequest{ + DirectoryAddr: "unknown", + Epoch: uint64(6)}) + if err != ReqUnknownDirectory { + t.Fatal("Expect ReqUnknownDirectory for older STR") + } + +} + +func TestGetObservedSTRMalformed(t *testing.T) { + // let's just create basic test directory and an empty audit log + d, pk := NewTestDirectory(t, true) + aud := NewAuditLog() + + // create 10 epochs + priorSTRs := make(map[uint64]*m.SignedTreeRoot) + for i := 0; i < 10; i++ { + priorSTRs[d.LatestSTR().Epoch] = d.LatestSTR() + d.Update() + } + + // now insert into the log + err := aud.Insert("test-server", pk, priorSTRs, d.LatestSTR()) + if err != nil { + t.Fatal("Error inserting new server history with prior STRs") + } + + _, err = aud.GetObservedSTR(&AuditingRequest{ + DirectoryAddr: ""}) + if err != ErrMalformedClientMessage { + t.Fatal("Expect ErrMalFormedClientMessage for latest STR") + } + + _, err = aud.GetObservedSTRInEpoch(&AuditingInEpochRequest{ + DirectoryAddr: "", + Epoch: uint64(6)}) + if err != ErrMalformedClientMessage { + t.Fatal("Expect ErrMalformedClientMessage for older STR") + } + + // also test the epoch range + _, err = aud.GetObservedSTRInEpoch(&AuditingInEpochRequest{ + DirectoryAddr: "", + Epoch: uint64(20)}) + if err != ErrMalformedClientMessage { + t.Fatal("Expect ErrMalformedClientMessage for older STR") + } + +} diff --git a/protocol/error.go b/protocol/error.go index 86c2bf4..97016b9 100644 --- a/protocol/error.go +++ b/protocol/error.go @@ -11,7 +11,7 @@ type ErrorCode int // These codes indicate the status of a client-server or client-auditor message // exchange. // Codes prefixed by "Req" indicate different client request results. -// Codes prefixed by "Err" indicate an internal server error or a malformed +// Codes prefixed by "Err" indicate an internal server/auditor error or a malformed // message. const ( ReqSuccess ErrorCode = iota + 100 @@ -21,6 +21,7 @@ const ( ReqUnknownDirectory ErrDirectory + ErrAuditLog ErrMalformedClientMessage ErrMalformedDirectoryMessage ) @@ -49,6 +50,7 @@ const ( var Errors = map[ErrorCode]bool{ ErrMalformedClientMessage: true, ErrDirectory: true, + ErrAuditLog: true, ErrMalformedDirectoryMessage: true, } @@ -60,6 +62,7 @@ var ( ErrMalformedClientMessage: "[coniks] Malformed client message", ErrDirectory: "[coniks] Directory error", + ErrAuditLog: "[coniks] Audit log error", ErrMalformedDirectoryMessage: "[coniks] Malformed directory message", CheckPassed: "[coniks] Consistency checks passed", From 67188eb86f0093a76087c9e3644a3233441b0de7 Mon Sep 17 00:00:00 2001 From: Marcela M Date: Tue, 7 Mar 2017 19:19:54 -0500 Subject: [PATCH 07/43] Add assertions to validate auditor messages on client --- protocol/auditlog_test.go | 2 +- protocol/error.go | 3 +++ protocol/message.go | 10 ++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/protocol/auditlog_test.go b/protocol/auditlog_test.go index de489ef..ca45ca7 100644 --- a/protocol/auditlog_test.go +++ b/protocol/auditlog_test.go @@ -134,7 +134,7 @@ func TestGetObservedSTRInEpoch(t *testing.T) { if err != ReqSuccess { t.Fatal("Unable to get latest range of STRs") } - if obs.STR == nil { + if obs.STR == nil || len(obs.STR) < 1 { t.Fatal("Expect returned STR to be not nil") } if len(obs.STR) != 5 { diff --git a/protocol/error.go b/protocol/error.go index 97016b9..4e55a25 100644 --- a/protocol/error.go +++ b/protocol/error.go @@ -24,6 +24,7 @@ const ( ErrAuditLog ErrMalformedClientMessage ErrMalformedDirectoryMessage + ErrMalformedAuditorMessage ) // These codes indicate the result @@ -52,6 +53,7 @@ var Errors = map[ErrorCode]bool{ ErrDirectory: true, ErrAuditLog: true, ErrMalformedDirectoryMessage: true, + ErrMalformedAuditorMessage: true, } var ( @@ -64,6 +66,7 @@ var ( ErrDirectory: "[coniks] Directory error", ErrAuditLog: "[coniks] Audit log error", ErrMalformedDirectoryMessage: "[coniks] Malformed directory message", + ErrMalformedAuditorMessage: "[coniks] Malformed auditor message", CheckPassed: "[coniks] Consistency checks passed", CheckBadSignature: "[coniks] Directory's signature on STR or TB is invalid", diff --git a/protocol/message.go b/protocol/message.go index 03b62e0..30b34a5 100644 --- a/protocol/message.go +++ b/protocol/message.go @@ -307,6 +307,16 @@ func (msg *Response) validate() error { case *DirectoryProofs: // TODO: also do above assertions here return nil + case *ObservedSTR: + if df.STR == nil { + return ErrMalformedAuditorMessage + } + return nil + case *ObservedSTRs: + if df.STR == nil || len(df.STR) < 1 { + return ErrMalformedAuditorMessage + } + return nil default: panic("[coniks] Malformed response") } From 2379d42b2f594a3f6edd40b39484d770ab383a16 Mon Sep 17 00:00:00 2001 From: Marcela M Date: Tue, 7 Mar 2017 20:31:16 -0500 Subject: [PATCH 08/43] Add generic STR response handler --- protocol/consistencychecks.go | 53 +++++++++++++++++++++++++++++++++-- protocol/error.go | 2 +- protocol/message.go | 4 ++- 3 files changed, 54 insertions(+), 5 deletions(-) diff --git a/protocol/consistencychecks.go b/protocol/consistencychecks.go index a6d5840..fcdd530 100644 --- a/protocol/consistencychecks.go +++ b/protocol/consistencychecks.go @@ -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. +func HandleDirectorySTRs(requestType int, msg *Response, signKey sign.PublicKey, + savedSTR *m.SignedTreeRoot, e error, isClient bool) error { + var str *m.SignedTreeRoot + if err := msg.validate(); err != nil { + return e + } + + switch requestType { + case AuditType: + if _, ok := msg.DirectoryResponse.(*ObservedSTR); !ok { + return e + } + str = msg.DirectoryResponse.(*ObservedSTR).STR + // TODO: compare the STR with the saved one on the client + // if the auditor has returned a more recent STR, should the + // client update its savedSTR? Should this force a new round of + // monitoring? + case AuditInEpochType: + // this is the default request type for an auditor + // since the auditor conservatively assumes it may + // have missed epochs + + // FIXME + //if _, ok := msg.DirectoryResponse.(*ObservedSTRs); !ok { + // return e + //} + //str = msg.DirectoryResponse.(*ObservedSTRs).STR + default: + panic("[coniks] Unknown auditing request type") + } + // we assume the requestType is AuditInEpochType if we're here + + // verify the timeliness of the STR if we're the auditor + // check the consistency of the newly received STRs + // FIXME: use `XXInEpoch` version of verifySTRConsistency + if err := verifySTRConsistency(signKey, savedSTR, str); err != nil { + return err + } + + return nil +} + func (cc *ConsistencyChecks) updateSTR(requestType int, msg *Response) error { var str *m.SignedTreeRoot switch requestType { @@ -110,9 +154,6 @@ func (cc *ConsistencyChecks) updateSTR(requestType int, msg *Response) error { cc.SavedSTR = str return nil } - // FIXME: check whether the STR was issued on time and whatnot. - // Maybe it has something to do w/ #81 and client transitioning between epochs. - // Try to verify w/ what's been saved if err := cc.verifySTR(str); err == nil { return nil } @@ -132,6 +173,12 @@ func (cc *ConsistencyChecks) updateSTR(requestType int, msg *Response) error { // verifySTR checks whether the received STR is the same with // the SavedSTR using reflect.DeepEqual(). +// FIXME: check whether the STR was issued on time and whatnot. +// Maybe it has something to do w/ #81 and client transitioning between epochs. +// Try to verify w/ what's been saved +// FIXME: make this generic so the auditor can also verify the timeliness of the +// STR etc. Might make sense to separate the comparison, which is only done on the client, +// from the rest. func (cc *ConsistencyChecks) verifySTR(str *m.SignedTreeRoot) error { if reflect.DeepEqual(cc.SavedSTR, str) { return nil diff --git a/protocol/error.go b/protocol/error.go index 4e55a25..be16002 100644 --- a/protocol/error.go +++ b/protocol/error.go @@ -66,7 +66,7 @@ var ( ErrDirectory: "[coniks] Directory error", ErrAuditLog: "[coniks] Audit log error", ErrMalformedDirectoryMessage: "[coniks] Malformed directory message", - ErrMalformedAuditorMessage: "[coniks] Malformed auditor message", + ErrMalformedAuditorMessage: "[coniks] Malformed auditor message", CheckPassed: "[coniks] Consistency checks passed", CheckBadSignature: "[coniks] Directory's signature on STR or TB is invalid", diff --git a/protocol/message.go b/protocol/message.go index 30b34a5..e6ffc17 100644 --- a/protocol/message.go +++ b/protocol/message.go @@ -12,6 +12,8 @@ const ( KeyLookupType KeyLookupInEpochType MonitoringType + AuditType + AuditInEpochType ) // A Request message defines the data a CONIKS client must send to a CONIKS @@ -161,7 +163,7 @@ type ObservedSTR struct { // An ObservedSTRs response includes a list of signed tree roots // STR. A CONIKS auditor returns this DirectoryResponse type upon an -// AudutingRequest. +// AuditingInEpochRequest. type ObservedSTRs struct { STR []*m.SignedTreeRoot } From 7b70110efbdb8ee588fd3cbc58016e47fbfa0a93 Mon Sep 17 00:00:00 2001 From: Marcela M Date: Fri, 10 Mar 2017 14:59:27 -0500 Subject: [PATCH 09/43] Add TODO to move all generic STR auditing code to a separate module --- protocol/consistencychecks.go | 1 + 1 file changed, 1 insertion(+) diff --git a/protocol/consistencychecks.go b/protocol/consistencychecks.go index fcdd530..45193bb 100644 --- a/protocol/consistencychecks.go +++ b/protocol/consistencychecks.go @@ -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 package protocol From b63eacf23edead2e044d5f0b07e0bcba3383c405 Mon Sep 17 00:00:00 2001 From: Marcela M Date: Fri, 10 Mar 2017 16:44:49 -0500 Subject: [PATCH 10/43] [WIP] Refactor generic auditing functionality --- protocol/auditlog.go | 67 ++++++++++++++------- protocol/auditor.go | 108 ++++++++++++++++++++++++++++++++++ protocol/consistencychecks.go | 101 ++++--------------------------- 3 files changed, 165 insertions(+), 111 deletions(-) create mode 100644 protocol/auditor.go diff --git a/protocol/auditlog.go b/protocol/auditlog.go index 116d8e7..8b0f5e4 100644 --- a/protocol/auditlog.go +++ b/protocol/auditlog.go @@ -11,9 +11,8 @@ import ( ) type directoryHistory struct { - signKey sign.PublicKey + auditState *auditorState snapshots map[uint64]*m.SignedTreeRoot - latestSTR *m.SignedTreeRoot } // A ConiksAuditLog maintains the histories @@ -26,10 +25,10 @@ type ConiksAuditLog struct { } func newDirectoryHistory(signKey sign.PublicKey, str *m.SignedTreeRoot) *directoryHistory { - h := new(directoryHistory) - h.signKey = signKey - h.snapshots = make(map[uint64]*m.SignedTreeRoot) - h.latestSTR = str + h := &directoryHistory{ + auditState: newAuditorState(signKey, str), + snapshots: make(map[uint64]*m.SignedTreeRoot), + } return h } @@ -37,8 +36,9 @@ func newDirectoryHistory(signKey sign.PublicKey, str *m.SignedTreeRoot) *directo // 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 := new(ConiksAuditLog) - l.histories = make(map[string]*directoryHistory) + l := &ConiksAuditLog{ + histories: make(map[string]*directoryHistory), + } return l } @@ -54,7 +54,6 @@ func (l *ConiksAuditLog) IsKnownDirectory(addr string) bool { return false } -// 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 func (l *ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, @@ -82,18 +81,16 @@ func (l *ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, l.histories[addr] = h - // FIXME: verify the consistency of each new STR return nil } -// Update verifies the consistency of a newly observed STR newSTR for -// the directory addr, and inserts the newSTR into addr's directory history -// if the checks (i.e. STR signature and hash chain verifications) pass. -// Update() returns nil if the checks pass, and the appropriate consistency -// check error otherwise. Update() assumes that Insert() has been called for +// Update adds the newSTR into addr's directory history +// and returns nil if no error occurs, and an ErrAuditLog error +// otherwise. Update() assumes that Insert() has been called for // addr prior to its first call and thereby expects that an entry for addr // exists in the audit log l. -// FIXME: pass Response message as param +// Update() also assumes that auditor.verifySTRConsistency() has been +// called for newSTR and that this check has passed. func (l *ConiksAuditLog) Update(addr string, newSTR *m.SignedTreeRoot) error { // panic if we want to update an entry for which we don't have @@ -103,13 +100,43 @@ func (l *ConiksAuditLog) Update(addr string, newSTR *m.SignedTreeRoot) error { h := l.histories[addr] - if err := verifySTRConsistency(h.signKey, h.latestSTR, newSTR); err != nil { - return err + // update the latest STR + h.snapshots[h.latestSTR.Epoch] = h.auditState.latestSTR + h.auditState.latestSTR = newSTR + return nil +} + +// Update adds the newSTR into addr's directory history +// and returns nil if no error occurs, and an ErrAuditLog error +// otherwise. Update() assumes that Insert() has been called for +// addr prior to its first call and thereby expects that an entry for addr +// exists in the audit log l. +// Update() also assumes that auditor.verifySTRConsistency() has been +// called for newSTR and that this check has passed. +func (l *ConiksAuditLog) UpdateRange(addr string, strs[] *m.SignedTreeRoot) error { + + // panic if we want to update an entry for which we don't have + if !l.IsKnownDirectory(addr) { + return ErrAuditLog + } + + h := l.histories[addr] + + startEp := h.auditState.latestSTR.Epoch + endEp := latestSTR.Epoch + + // add each old STR into the history + for ep := startEp; ep < endEp; ep++ { + str := oldSTRs[ep] + if str == nil { + return ErrMalformedDirectoryMessage + } + h.snapshots[ep] = str } // update the latest STR - h.snapshots[h.latestSTR.Epoch] = h.latestSTR - h.latestSTR = newSTR + h.snapshots[h.latestSTR.Epoch] = h.auditState.latestSTR + h.auditState.latestSTR = newSTR return nil } diff --git a/protocol/auditor.go b/protocol/auditor.go new file mode 100644 index 0000000..1f30e38 --- /dev/null +++ b/protocol/auditor.go @@ -0,0 +1,108 @@ +// This module implements a generic CONIKS auditor, i.e. the +// functionality that clients and auditors need to verify +// a server's STR history. + +package protocol + +import ( + "github.com/coniks-sys/coniks-go/crypto/sign" + m "github.com/coniks-sys/coniks-go/merkletree" +) + +type auditorState struct { + signKey sign.PublicKey + latestSTR *m.SignedTreeRoot +} + +func newAuditorState(signKey sign.PublicKey, latest *m.SignedTreeRoot) *auditorState { + a := &auditorState{ + signKey: signKey, + latestSTR: latest, + } + return a +} + +// handleDirectorySTRs is supposed to be used by CONIKS clients to +// handle auditor responses, and by CONIKS auditors to handle directory +// responses. +func (a *auditorState) HandleDirectorySTRs(requestType int, msg *Response, + e error, isClient bool) error { + if err := msg.validate(); err != nil { + return e + } + + switch requestType { + case AuditType: + if _, ok := msg.DirectoryResponse.(*ObservedSTR); !ok { + return e + } + case AuditInEpochType: + // this is the default request type for an auditor + // since the auditor conservatively assumes it may + // have missed epochs + + if _, ok := msg.DirectoryResponse.(*ObservedSTRs); !ok { + return e + } + default: + panic("[coniks] Unknown auditing request type") + } + + // clients only care about comparing the STR with the savedSTR + // TODO: if the auditor has returned a more recent STR, + // should the client update its savedSTR? Should this + // force a new round of monitoring? + if isClient { + a.compareSavedSTR(requestType, msg) + } + + // we assume the requestType is AuditInEpochType if we're here + + // verify the timeliness of the STR if we're the auditor + // check the consistency of the newly received STRs + if err := a.verifySTRConsistencyRange(strs); err != nil { + return err + } + + return nil +} + +// verifySTR checks whether the received STR is the same with +// the saved STR in the audit state using reflect.DeepEqual(). +// FIXME: check whether the STR was issued on time and whatnot. +// Maybe it has something to do w/ #81 and client transitioning between epochs. +// Try to verify w/ what's been saved +func (a *auditorState) verifySTR(str *m.SignedTreeRoot) error { + return a.compareSavedSTR(str) +} + +func (a *auditorState) compareSavedSTR(str *m.SignedTreeRoot) error { + if reflect.DeepEqual(a.latestSTR, str) { + return nil + } + return CheckBadSTR +} + +// verifySTRConsistency checks the consistency between 2 snapshots. +// 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 +// in its history. +// In the case of a client-side consistency check, verifySTRConsistency() +// should not verify the hash chain using the STR stored in cc. +func (a *auditorState) verifySTRConsistency(str *m.SignedTreeRoot) error { + // verify STR's signature + if !a.signKey.Verify(str.Serialize(), str.Signature) { + return CheckBadSignature + } + if str.VerifyHashChain(a.latestSTR) { + return nil + } + + // TODO: verify the directory's policies as well. See #115 + return CheckBadSTR +} + +func (a *auditorState) verifySTRConsistencyRange(strs []*m.SignedTreeRoot) error { + // FIXME: implement me +} diff --git a/protocol/consistencychecks.go b/protocol/consistencychecks.go index 45193bb..965a709 100644 --- a/protocol/consistencychecks.go +++ b/protocol/consistencychecks.go @@ -26,15 +26,14 @@ import ( // subsequent responses from the ConiksDirectory to any // client request. type ConsistencyChecks struct { - // SavedSTR stores the latest verified signed tree root. - SavedSTR *m.SignedTreeRoot + // auditState stores the latest verified signed tree root + // as well as the server's signing key + auditState *auditorState Bindings map[string][]byte // extensions settings useTBs bool TBs map[string]*TemporaryBinding - - signKey sign.PublicKey } // NewCC creates an instance of ConsistencyChecks using @@ -46,10 +45,9 @@ func NewCC(savedSTR *m.SignedTreeRoot, useTBs bool, signKey sign.PublicKey) *Con panic("[coniks] Currently the server is forced to use TBs") } cc := &ConsistencyChecks{ - SavedSTR: savedSTR, + auditState: newAuditorState(signKey, savedSTR), Bindings: make(map[string][]byte), useTBs: useTBs, - signKey: signKey, } if useTBs { cc.TBs = make(map[string]*TemporaryBinding) @@ -101,65 +99,21 @@ 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. -func HandleDirectorySTRs(requestType int, msg *Response, signKey sign.PublicKey, - savedSTR *m.SignedTreeRoot, e error, isClient bool) error { - var str *m.SignedTreeRoot - if err := msg.validate(); err != nil { - return e - } - - switch requestType { - case AuditType: - if _, ok := msg.DirectoryResponse.(*ObservedSTR); !ok { - return e - } - str = msg.DirectoryResponse.(*ObservedSTR).STR - // TODO: compare the STR with the saved one on the client - // if the auditor has returned a more recent STR, should the - // client update its savedSTR? Should this force a new round of - // monitoring? - case AuditInEpochType: - // this is the default request type for an auditor - // since the auditor conservatively assumes it may - // have missed epochs - - // FIXME - //if _, ok := msg.DirectoryResponse.(*ObservedSTRs); !ok { - // return e - //} - //str = msg.DirectoryResponse.(*ObservedSTRs).STR - default: - panic("[coniks] Unknown auditing request type") - } - // we assume the requestType is AuditInEpochType if we're here - - // verify the timeliness of the STR if we're the auditor - // check the consistency of the newly received STRs - // FIXME: use `XXInEpoch` version of verifySTRConsistency - if err := verifySTRConsistency(signKey, savedSTR, str); err != nil { - return err - } - - return nil -} - func (cc *ConsistencyChecks) updateSTR(requestType int, msg *Response) error { var str *m.SignedTreeRoot switch requestType { case RegistrationType, KeyLookupType: str = msg.DirectoryResponse.(*DirectoryProof).STR // First response - if cc.SavedSTR == nil { - cc.SavedSTR = str + if cc.auditState.latestSTR == nil { + cc.auditState.latestSTR = str return nil } - if err := cc.verifySTR(str); err == nil { + if err := cc.auditState.verfySTR(str); err == nil { return nil } // Otherwise, expect that we've entered a new epoch - if err := verifySTRConsistency(cc.signKey, cc.SavedSTR, str); err != nil { + if err := cc.auditState.verifySTRConsistency(str); err != nil { return err } @@ -168,45 +122,10 @@ func (cc *ConsistencyChecks) updateSTR(requestType int, msg *Response) error { } // And update the saved STR - cc.SavedSTR = str + cc.auditState.latestSTR = str return nil } -// verifySTR checks whether the received STR is the same with -// the SavedSTR using reflect.DeepEqual(). -// FIXME: check whether the STR was issued on time and whatnot. -// Maybe it has something to do w/ #81 and client transitioning between epochs. -// Try to verify w/ what's been saved -// FIXME: make this generic so the auditor can also verify the timeliness of the -// STR etc. Might make sense to separate the comparison, which is only done on the client, -// from the rest. -func (cc *ConsistencyChecks) verifySTR(str *m.SignedTreeRoot) error { - if reflect.DeepEqual(cc.SavedSTR, str) { - return nil - } - return CheckBadSTR -} - -// verifySTRConsistency checks the consistency between 2 snapshots. -// 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 -// in its history. -// In the case of a client-side consistency check, verifySTRConsistency() -// should not verify the hash chain using the STR stored in cc. -func verifySTRConsistency(signKey sign.PublicKey, savedSTR, str *m.SignedTreeRoot) error { - // verify STR's signature - if !signKey.Verify(str.Serialize(), str.Signature) { - return CheckBadSignature - } - if str.VerifyHashChain(savedSTR) { - return nil - } - - // TODO: verify the directory's policies as well. See #115 - return CheckBadSTR -} - func (cc *ConsistencyChecks) checkConsistency(requestType int, msg *Response, uname string, key []byte) ErrorCode { var err error @@ -375,7 +294,7 @@ func (cc *ConsistencyChecks) verifyReturnedPromise(df *DirectoryProof, } // verify TB's Signature - if !cc.signKey.Verify(tb.Serialize(str.Signature), tb.Signature) { + if !cc.auditState.signKey.Verify(tb.Serialize(str.Signature), tb.Signature) { return CheckBadSignature } From 5a923dbf27f70ccea31f1dc96a8f6faaeca14140 Mon Sep 17 00:00:00 2001 From: Marcela Melara Date: Wed, 14 Dec 2016 15:43:32 -0500 Subject: [PATCH 11/43] Add client-auditor messages --- protocol/message.go | 122 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 120 insertions(+), 2 deletions(-) diff --git a/protocol/message.go b/protocol/message.go index c666568..8e557c6 100644 --- a/protocol/message.go +++ b/protocol/message.go @@ -90,6 +90,60 @@ type MonitoringRequest struct { EndEpoch uint64 } +// An AuditingRequest is a message with a CONIKS key directory's address +// as a string that a CONIKS client sends to a CONIKS auditor to request +// the latest STR the auditor has observed for the given directory. +// If the client needs to request a directory's STR for a prior epoch, it +// must send an AuditingInEpochRequest. +// +// The response to a successful request is an ObservedDirectoryProof. +type AuditingRequest struct { + DirectoryAddr string `json:"directory_addr"` +} + +// 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 to retrieve the STR it observed for the directory in +// the given epoch. The client sends this request type when it needs to +// audit a directory's STR for a prior epoch (i.e. as part of a +// key lookup in epoch check or a monitoring check). The client can send an +// AuditingRequest if it needs to audit a directory's STR for its latest +// epoch. +// +// The response to a successful request is an ObservedDirectoryProofs with +// a list of STRs covering the epoch range [Epoch, d.LatestSTR().Epoch]. +type AuditingInEpochRequest struct { + DirectoryAddr string `json:"directory_addr"` + Epoch uint64 `json:"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 to request +// the latest STR the auditor has observed for the given directory. +// If the client needs to request a directory's STR for a prior epoch, it +// must send an AuditingInEpochRequest. +// +// The response to a successful request is an ObservedDirectoryProof. +type AuditingRequest struct { + DirectoryAddr string `json:"directory_addr"` +} + +// 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 to retrieve the STR it observed for the directory in +// the given epoch. The client sends this request type when it needs to +// audit a directory's STR for a prior epoch (i.e. as part of a +// key lookup in epoch check or a monitoring check). The client can send an +// AuditingRequest if it needs to audit a directory's STR for its latest +// epoch. +// +// The response to a successful request is an ObservedDirectoryProofs with +// a list of STRs covering the epoch range [Epoch, d.LatestSTR().Epoch]. +type AuditingInEpochRequest struct { + DirectoryAddr string `json:"directory_addr"` + Epoch uint64 `json:"epoch"` +} + // A Response message indicates the result of a CONIKS client request // with an appropriate error code, and defines the set of cryptographic // proofs a CONIKS directory must return as part of its response. @@ -99,7 +153,7 @@ type Response struct { } // A DirectoryResponse is a message that includes cryptographic proofs -// about the key directory that a CONIKS key directory returns +// about the key directory that a CONIKS key directory or auditor returns // to a CONIKS client. type DirectoryResponse interface{} @@ -124,8 +178,36 @@ type DirectoryProofs struct { STR []*DirSTR } +// An ObservedSTR response includes a single signed tree root +// STR. A CONIKS auditor returns this DirectoryResponse type upon an +// AuditingRequest. +type ObservedSTR struct { + STR *m.SignedTreeRoot +} + +// An ObservedSTRs response includes a list of signed tree roots +// STR. A CONIKS auditor returns this DirectoryResponse type upon an +// AudutingRequest. +type ObservedSTRs struct { + STR []*m.SignedTreeRoot +} + +// An ObservedSTR response includes a single signed tree root +// STR. A CONIKS auditor returns this DirectoryResponse type upon an +// AuditingRequest. +type ObservedSTR struct { + STR *m.SignedTreeRoot +} + +// An ObservedSTRs response includes a list of signed tree roots +// STR. A CONIKS auditor returns this DirectoryResponse type upon an +// AudutingRequest. +type ObservedSTRs struct { + STR []*m.SignedTreeRoot +} + // NewErrorResponse creates a new response message indicating the error -// that occurred while a CONIKS directory was +// that occurred while a CONIKS directory or a CONIKS auditor was // processing a client request. func NewErrorResponse(e ErrorCode) *Response { return &Response{Error: e} @@ -133,6 +215,8 @@ func NewErrorResponse(e ErrorCode) *Response { var _ DirectoryResponse = (*DirectoryProof)(nil) var _ DirectoryResponse = (*DirectoryProofs)(nil) +var _ DirectoryResponse = (*ObservedSTR)(nil) +var _ DirectoryResponse = (*ObservedSTRs)(nil) // NewRegistrationProof creates the response message a CONIKS directory // sends to a client upon a RegistrationRequest, @@ -216,6 +300,40 @@ func NewMonitoringProof(ap []*m.AuthenticationPath, }, ReqSuccess } +// NewObservedSTR creates the response message a CONIKS auditor +// sends to a client upon an AuditingRequest, +// and returns a Response containing an ObservedSTR struct. +// auditlog.Audit() passes the signed tree root for the auditor's latest +// observed epoch str. +// +// See auditlog.Audit() for details on the contents of the created +// ObservedSTR. +func NewObservedSTR(str *m.SignedTreeRoot) (*Response, ErrorCode) { + return &Response{ + Error: ReqSuccess, + DirectoryResponse: &ObservedSTR{ + STR: str, + }, + }, ReqSuccess +} + +// NewObservedSTRs creates the response message a CONIKS auditor +// sends to a client upon an AuditingInEpochRequest, +// and returns a Response containing an ObservedSTRs struct. +// auditlog.AuditInEpoch() passes a list of signed tree roots +// that the auditor observed for the requested range of epochs str. +// +// See auditlog.AuditInEpoch() for details on the contents of the created +// ObservedSTRs. +func NewObservedSTRs(str []*m.SignedTreeRoot) (*Response, ErrorCode) { + return &Response{ + Error: ReqSuccess, + DirectoryResponse: &ObservedSTRs{ + STR: str, + }, + }, ReqSuccess +} + func (msg *Response) validate() error { if Errors[msg.Error] { return msg.Error From d9ed76dc517b2617277c5ae9c31295f756a87ced Mon Sep 17 00:00:00 2001 From: Marcela Date: Wed, 21 Dec 2016 17:07:50 -0500 Subject: [PATCH 12/43] Create audit log structure, query API finished --- protocol/auditlog.go | 183 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 protocol/auditlog.go diff --git a/protocol/auditlog.go b/protocol/auditlog.go new file mode 100644 index 0000000..9521cb3 --- /dev/null +++ b/protocol/auditlog.go @@ -0,0 +1,183 @@ +// This module implements a CONIKS audit log that a CONIKS auditor +// maintains. +// An audit log is a mirror of many CONIKS key directories' STR history, +// allowing CONIKS clients to audit the CONIKS directories. + +package protocol + +import ( + "github.com/coniks-sys/coniks-go/crypto/sign" + m "github.com/coniks-sys/coniks-go/merkletree" +) + +type directoryHistory struct { + signKey sign.PublicKey + snapshots map[uint64]*m.SignedTreeRoot + latestSTR *m.SignedTreeRoot +} + +// A ConiksAuditLog maintains the histories +// of all CONIKS directories known to a CONIKS auditor. +// Each history includes the directory's public signing key +// enabling the auditor to verify the corresponding signed +// tree roots. +type ConiksAuditLog struct { + histories map[string]*directoryHistory +} + +func newDirectoryHistory(signKey sign.PublicKey, str *m.SignedTreeRoot) *directoryHistory { + h := new(directoryHistory) + h.signKey = signKey + h.snapshots = make(map[uint64]*m.SignedTreeRoot) + h.latestSTR = str + return h +} + +// NewAuditLog constructs a new ConiksAuditLog. It creates an empty +// 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 := new(ConiksAuditLog) + l.histories = make(map[string]*directoryHistory) + return l +} + +// 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, +// and false otherwise. +func (l *ConiksAuditLog) IsKnownDirectory(addr string) bool { + h := l.histories[addr] + if h != nil { + return true + } + return false +} + +// FIXME: pass Request 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 +func (l *ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, + oldSTRs map[uint64]*m.SignedTreeRoot, latestSTR *m.SignedTreeRoot) error { + + // panic if we want to create a new entry for an addr we already know + if l.IsKnownDirectory(addr) { + panic("[protocol] Trying to add an audit log entry for an existing address") + } + + // create the new directory history + h := newDirectoryHistory(signKey, latestSTR) + + startEp := uint64(0) + endEp := latestSTR.Epoch + + // add each old STR into the history + for ep := startEp; ep < endEp; ep++ { + if str := oldSTRs[ep]; str != nil { + h.snapshots[ep] = str + } + return ErrMalformedDirectoryMessage + } + + // FIXME: verify the consistency of each new STR + return nil +} + +// Update verifies the consistency of a newly observed STR newSTR for +// the directory addr, and inserts the newSTR into addr's directory history +// if the checks (i.e. STR signature and hash chain verifications) pass. +// Update() returns nil if the checks pass, and the appropriate consistency +// check error otherwise. Update() assumes that Insert() has been called for +// addr prior to its first call and thereby expects that an entry for addr +// exists in the audit log l. +// FIXME: pass Request 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 + if !l.IsKnownDirectory(addr) { + panic("[protocol] Trying to update audit log entry for non-existent address") + } + + h := l.histories[addr] + + if err := h.verifySTRConsistency(newSTR); err != nil { + return err + } + + // update the latest STR + h.snapshots[h.latestSTR.Epoch] = h.latestSTR + h.latestSTR = newSTR + return nil +} + +// verifySTRConsistency checks the consistency between 2 snapshots. +// It uses the pinned signing key in the directory history +// to verify the STR's signature and verifies +// the hash chain using the latestSTR stored in the history. +// TODO: dedup this: write generic verifySTRConsistency +func (h *directoryHistory) verifySTRConsistency(str *m.SignedTreeRoot) error { + // verify STR's signature + if !h.signKey.Verify(str.Serialize(), str.Signature) { + return CheckBadSignature + } + if str.VerifyHashChain(h.latestSTR) { + return nil + } + + // TODO: verify the directory's policies as well. See #115 + return CheckBadSTR +} + +// TODO add doc +func (l *ConiksAuditLog) Query(req *AuditingRequest) (*Response, ErrorCode) { + + // make sure the request is well-formed + if len(req.DirectoryAddr) <= 0 { + return NewErrorResponse(ErrMalformedClientMessage), + ErrMalformedClientMessage + } + + if h := l.histories[req.DirectoryAddr]; h != nil { + return NewObservedSTR(h.latestSTR) + } + + // FIXME: return ErrUnknownDirectory + return nil, ErrDirectory +} + +// TODO: add doc +func (l *ConiksAuditLog) QueryInEpoch(req *AuditingInEpochRequest) (*Response, + ErrorCode) { + + // make sure the request is well-formed + if len(req.DirectoryAddr) <= 0 { + return NewErrorResponse(ErrMalformedClientMessage), + ErrMalformedClientMessage + } + + if h := l.histories[req.DirectoryAddr]; h != nil { + + // also make sure the epoch is well-formed + if req.Epoch > h.latestSTR.Epoch { + return NewErrorResponse(ErrMalformedClientMessage), + ErrMalformedClientMessage + } + + var strs []*m.SignedTreeRoot + startEp := req.Epoch + endEp := h.latestSTR.Epoch + + for ep := startEp; ep < endEp; ep++ { + str := h.snapshots[ep] + strs = append(strs, str) + } + + // don't forget to append the latest STR + strs = append(strs, h.latestSTR) + + return NewObservedSTRs(strs) + } + + // FIXME: return ErrUnknownDirectory + return nil, ErrDirectory +} From 146e91a1e1b94309e63ec98ecb348a387bbbae6c Mon Sep 17 00:00:00 2001 From: Marcela Melara Date: Thu, 16 Feb 2017 15:06:23 -0500 Subject: [PATCH 13/43] Add/Update docs to include auditor, add ReqUnknownDirectory auditor error --- protocol/auditlog.go | 47 ++++++++++++++++++++++++++++++++++++-------- protocol/error.go | 5 ++++- protocol/message.go | 29 ++++++++++++++------------- 3 files changed, 58 insertions(+), 23 deletions(-) diff --git a/protocol/auditlog.go b/protocol/auditlog.go index 9521cb3..3f50141 100644 --- a/protocol/auditlog.go +++ b/protocol/auditlog.go @@ -128,8 +128,23 @@ func (h *directoryHistory) verifySTRConsistency(str *m.SignedTreeRoot) error { return CheckBadSTR } -// TODO add doc -func (l *ConiksAuditLog) Query(req *AuditingRequest) (*Response, ErrorCode) { +// GetObservedSTR gets the observed STR for the CONIKS directory address indicated +// in the AuditingRequest req received from a CONIKS client from the auditor's latest +// directory history entry, and returns a tuple of the form +// (response, error). +// The response (which also includes the error code) is supposed to +// be sent back to the client. The returned error is used by the auditor +// for logging purposes. +// +// A request without a directory address is considered +// malformed, and causes GetObservedSTR() to return a +// message.NewErrorResponse(ErrMalformedClientMessage) tuple. +// GetObservedSTR() returns a message.NewObservedSTR(str) tuple. +// str is the signed tree root the auditor has observed for the latest epoch. +// If the auditor doesn't have any history entries for the requested CONIKS +// directory, GetObservedSTR() returns a +// message.NewErrorResponse(ReqUnknownDirectory) tuple. +func (l *ConiksAuditLog) GetObservedSTR(req *AuditingRequest) (*Response, ErrorCode) { // make sure the request is well-formed if len(req.DirectoryAddr) <= 0 { @@ -141,12 +156,29 @@ func (l *ConiksAuditLog) Query(req *AuditingRequest) (*Response, ErrorCode) { return NewObservedSTR(h.latestSTR) } - // FIXME: return ErrUnknownDirectory - return nil, ErrDirectory + return NewErrorResponse(ReqUnknownDirectory), ReqUnknownDirectory } -// TODO: add doc -func (l *ConiksAuditLog) QueryInEpoch(req *AuditingInEpochRequest) (*Response, +// GetObservedSTRInEpoch gets the observed STR for the CONIKS directory address +// for a prior directory history entry indicated in the +// AuditingInEpochRequest req received from a CONIKS client, +// and returns a tuple of the form (response, error). +// The response (which also includes the error code) is supposed to +// be sent back to the client. The returned error is used by the auditor +// for logging purposes. +// +// A request without a directory address or with an epoch greater than the latest +// observed epoch of this directory is considered malformed, and causes +// GetObservedSTRInEpoch() to return a +// message.NewErrorResponse(ErrMalformedClientMessage) tuple. +// GetObservedSTRInEpoch() returns a message.NewObservedSTRs(strs) tuple. +// strs is a list of STRs for the epoch range [ep, +// l.histories[req.DirectoryAddr].latestSTR.Epoch], where ep is the past epoch +// for which the client has requested the observed STR. +// If the auditor doesn't have any history entries for the requested CONIKS +// directory, GetObservedSTR() returns a +// message.NewErrorResponse(ReqUnknownDirectory) tuple. +func (l *ConiksAuditLog) GetObservedSTRInEpoch(req *AuditingInEpochRequest) (*Response, ErrorCode) { // make sure the request is well-formed @@ -178,6 +210,5 @@ func (l *ConiksAuditLog) QueryInEpoch(req *AuditingInEpochRequest) (*Response, return NewObservedSTRs(strs) } - // FIXME: return ErrUnknownDirectory - return nil, ErrDirectory + return NewErrorResponse(ReqUnknownDirectory), ReqUnknownDirectory } diff --git a/protocol/error.go b/protocol/error.go index 866d508..86c2bf4 100644 --- a/protocol/error.go +++ b/protocol/error.go @@ -8,7 +8,8 @@ package protocol // An ErrorCode implements the built-in error interface type. type ErrorCode int -// These codes indicate the status of a client-server message exchange. +// These codes indicate the status of a client-server or client-auditor message +// exchange. // Codes prefixed by "Req" indicate different client request results. // Codes prefixed by "Err" indicate an internal server error or a malformed // message. @@ -16,6 +17,8 @@ const ( ReqSuccess ErrorCode = iota + 100 ReqNameExisted ReqNameNotFound + // auditor->client: no observed history for the requested directory + ReqUnknownDirectory ErrDirectory ErrMalformedClientMessage diff --git a/protocol/message.go b/protocol/message.go index 8e557c6..f0f560e 100644 --- a/protocol/message.go +++ b/protocol/message.go @@ -118,26 +118,27 @@ type AuditingInEpochRequest struct { } // An AuditingRequest is a message with a CONIKS key directory's address -// as a string that a CONIKS client sends to a CONIKS auditor to request -// the latest STR the auditor has observed for the given directory. -// If the client needs to request a directory's STR for a prior epoch, it +// as a string that a CONIKS client sends to a CONIKS auditor, or a CONIKS auditor +// sends to a CONIKS directory, to request the given directory's latest STR. +// If the client/auditor needs to request a directory's STR for a prior epoch, it // must send an AuditingInEpochRequest. // -// The response to a successful request is an ObservedDirectoryProof. +// The response to a successful request is an ObservedSTR. type AuditingRequest struct { DirectoryAddr string `json:"directory_addr"` } // 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 to retrieve the STR it observed for the directory in -// the given epoch. The client sends this request type when it needs to +// a CONIKS auditor, or a CONIKS auditor sends to a CONIKS directory, +// to retrieve the STR for the directory in +// the given epoch. The client/auditor sends this request type when it needs to // audit a directory's STR for a prior epoch (i.e. as part of a -// key lookup in epoch check or a monitoring check). The client can send an -// AuditingRequest if it needs to audit a directory's STR for its latest -// epoch. +// key lookup in epoch check, a monitoring check, or an auditor update). +// The client/auditor can send an AuditingRequest if it needs to audit a +// directory's STR for its latest epoch. // -// The response to a successful request is an ObservedDirectoryProofs with +// The response to a successful request is an ObservedSTRs with // a list of STRs covering the epoch range [Epoch, d.LatestSTR().Epoch]. type AuditingInEpochRequest struct { DirectoryAddr string `json:"directory_addr"` @@ -301,10 +302,10 @@ func NewMonitoringProof(ap []*m.AuthenticationPath, } // NewObservedSTR creates the response message a CONIKS auditor -// sends to a client upon an AuditingRequest, -// and returns a Response containing an ObservedSTR struct. -// auditlog.Audit() passes the signed tree root for the auditor's latest -// observed epoch str. +// sends to a client, or a CONIKS directory sends to an auditor, +// upon an AuditingRequest, and returns a Response containing an ObservedSTR struct. +// auditlog.GetObservedSTR(), or directory.GetLatestSTR(), passes the signed +// tree root for the directory's latest str. // // See auditlog.Audit() for details on the contents of the created // ObservedSTR. From 90e2517a580b48a920be49f21c291e5b0d602bd4 Mon Sep 17 00:00:00 2001 From: Marcela Melara Date: Thu, 16 Feb 2017 15:20:16 -0500 Subject: [PATCH 14/43] Use single generic verifySTRConsistency to be used by client and auditor --- protocol/auditlog.go | 20 +------------------- protocol/consistencychecks.go | 15 +++++++++------ 2 files changed, 10 insertions(+), 25 deletions(-) diff --git a/protocol/auditlog.go b/protocol/auditlog.go index 3f50141..373a7f4 100644 --- a/protocol/auditlog.go +++ b/protocol/auditlog.go @@ -100,7 +100,7 @@ func (l *ConiksAuditLog) Update(addr string, newSTR *m.SignedTreeRoot) error { h := l.histories[addr] - if err := h.verifySTRConsistency(newSTR); err != nil { + if err := verifySTRConsistency(h.signKey, h.latestSTR, newSTR); err != nil { return err } @@ -110,24 +110,6 @@ func (l *ConiksAuditLog) Update(addr string, newSTR *m.SignedTreeRoot) error { return nil } -// verifySTRConsistency checks the consistency between 2 snapshots. -// It uses the pinned signing key in the directory history -// to verify the STR's signature and verifies -// the hash chain using the latestSTR stored in the history. -// TODO: dedup this: write generic verifySTRConsistency -func (h *directoryHistory) verifySTRConsistency(str *m.SignedTreeRoot) error { - // verify STR's signature - if !h.signKey.Verify(str.Serialize(), str.Signature) { - return CheckBadSignature - } - if str.VerifyHashChain(h.latestSTR) { - return nil - } - - // TODO: verify the directory's policies as well. See #115 - return CheckBadSTR -} - // GetObservedSTR gets the observed STR for the CONIKS directory address indicated // in the AuditingRequest req received from a CONIKS client from the auditor's latest // directory history entry, and returns a tuple of the form diff --git a/protocol/consistencychecks.go b/protocol/consistencychecks.go index 856b706..dd8c98b 100644 --- a/protocol/consistencychecks.go +++ b/protocol/consistencychecks.go @@ -117,7 +117,7 @@ func (cc *ConsistencyChecks) updateSTR(requestType int, msg *Response) error { return nil } // Otherwise, expect that we've entered a new epoch - if err := cc.verifySTRConsistency(cc.SavedSTR, str); err != nil { + if err := verifySTRConsistency(cc.signKey, cc.SavedSTR, str); err != nil { return err } @@ -140,12 +140,15 @@ func (cc *ConsistencyChecks) verifySTR(str *DirSTR) error { } // verifySTRConsistency checks the consistency between 2 snapshots. -// It uses the pinned signing key in cc -// to verify the STR's signature and should not verify -// the hash chain using the STR stored in cc. -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 +// in its history. +// In the case of a client-side consistency check, verifySTRConsistency() +// should not verify the hash chain using the STR stored in cc. +func verifySTRConsistency(signKey sign.PublicKey, savedSTR, str *m.SignedTreeRoot) error { // verify STR's signature - if !cc.signKey.Verify(str.Serialize(), str.Signature) { + if !signKey.Verify(str.Serialize(), str.Signature) { return CheckBadSignature } if str.VerifyHashChain(savedSTR) { From e8178c17bd7f866b98fe2ecdbf075a51e228d02b Mon Sep 17 00:00:00 2001 From: Marcela M Date: Tue, 7 Mar 2017 19:10:08 -0500 Subject: [PATCH 15/43] Add tests for audit log, debug audit log --- protocol/auditlog.go | 21 ++-- protocol/auditlog_test.go | 220 ++++++++++++++++++++++++++++++++++++++ protocol/error.go | 5 +- 3 files changed, 236 insertions(+), 10 deletions(-) create mode 100644 protocol/auditlog_test.go diff --git a/protocol/auditlog.go b/protocol/auditlog.go index 373a7f4..116d8e7 100644 --- a/protocol/auditlog.go +++ b/protocol/auditlog.go @@ -44,7 +44,7 @@ func NewAuditLog() *ConiksAuditLog { // 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, +// validate the entries themselves. It returns true if an entry exists, // and false otherwise. func (l *ConiksAuditLog) IsKnownDirectory(addr string) bool { h := l.histories[addr] @@ -54,15 +54,15 @@ func (l *ConiksAuditLog) IsKnownDirectory(addr string) bool { return false } -// FIXME: pass Request message as param +// 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 func (l *ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, oldSTRs map[uint64]*m.SignedTreeRoot, latestSTR *m.SignedTreeRoot) error { - // panic if we want to create a new entry for an addr we already know + // error if we want to create a new entry for an addr we already know if l.IsKnownDirectory(addr) { - panic("[protocol] Trying to add an audit log entry for an existing address") + return ErrAuditLog } // create the new directory history @@ -73,12 +73,15 @@ func (l *ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, // add each old STR into the history for ep := startEp; ep < endEp; ep++ { - if str := oldSTRs[ep]; str != nil { - h.snapshots[ep] = str + str := oldSTRs[ep] + if str == nil { + return ErrMalformedDirectoryMessage } - return ErrMalformedDirectoryMessage + h.snapshots[ep] = str } + l.histories[addr] = h + // FIXME: verify the consistency of each new STR return nil } @@ -90,12 +93,12 @@ func (l *ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, // check error otherwise. Update() assumes that Insert() has been called for // addr prior to its first call and thereby expects that an entry for addr // exists in the audit log l. -// FIXME: pass Request message as param +// 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 if !l.IsKnownDirectory(addr) { - panic("[protocol] Trying to update audit log entry for non-existent address") + return ErrAuditLog } h := l.histories[addr] diff --git a/protocol/auditlog_test.go b/protocol/auditlog_test.go new file mode 100644 index 0000000..de489ef --- /dev/null +++ b/protocol/auditlog_test.go @@ -0,0 +1,220 @@ +package protocol + +import ( + m "github.com/coniks-sys/coniks-go/merkletree" + "testing" +) + +func TestInsertEmptyHistory(t *testing.T) { + // let's just create basic test directory and an empty audit log + d, pk := NewTestDirectory(t, true) + aud := NewAuditLog() + + err := aud.Insert("test-server", pk, nil, d.LatestSTR()) + if err != nil { + t.Fatal("Error inserting new server history") + } +} + +func TestUpdateHistory(t *testing.T) { + // let's just create basic test directory and an empty audit log + d, pk := NewTestDirectory(t, true) + aud := NewAuditLog() + + err := aud.Insert("test-server", pk, nil, d.LatestSTR()) + if err != nil { + t.Fatal("Error inserting new server history") + } + + // update the directory so we can update the audit log + d.Update() + err = aud.Update("test-server", d.LatestSTR()) + + if err != nil { + t.Fatal("Error updating the server history") + } +} + +func TestInsertPriorHistory(t *testing.T) { + // let's just create basic test directory and an empty audit log + d, pk := NewTestDirectory(t, true) + aud := NewAuditLog() + + // create 10 epochs + priorSTRs := make(map[uint64]*m.SignedTreeRoot) + for i := 0; i < 10; i++ { + priorSTRs[d.LatestSTR().Epoch] = d.LatestSTR() + d.Update() + } + + // now insert + err := aud.Insert("test-server", pk, priorSTRs, d.LatestSTR()) + if err != nil { + t.Fatal("Error inserting new server history with prior STRs") + } +} + +func TestInsertExistingHistory(t *testing.T) { + // let's just create basic test directory and an empty audit log + d, pk := NewTestDirectory(t, true) + aud := NewAuditLog() + err := aud.Insert("test-server", pk, nil, d.LatestSTR()) + if err != nil { + t.Fatal("Error inserting new server history") + } + + // let's make sure that we can't re-insert a new server history into our log + err = aud.Insert("test-server", pk, nil, d.LatestSTR()) + if err != ErrAuditLog { + t.Fatal("Expected an ErrAuditLog when inserting an existing server history") + } +} + +func TestUpdateUnknownHistory(t *testing.T) { + // let's just create basic test directory and an empty audit log + d, pk := NewTestDirectory(t, true) + aud := NewAuditLog() + err := aud.Insert("test-server", pk, nil, d.LatestSTR()) + if err != nil { + t.Fatal("Error inserting new server history") + } + + // let's make sure that we can't re-insert a new server history into our log + err = aud.Update("unknown", d.LatestSTR()) + if err != ErrAuditLog { + t.Fatal("Expected an ErrAuditLog when updating an unknown server history") + } +} + +func TestGetObservedSTR(t *testing.T) { + // let's just create basic test directory and an empty audit log + d, pk := NewTestDirectory(t, true) + aud := NewAuditLog() + err := aud.Insert("test-server", pk, nil, d.LatestSTR()) + if err != nil { + t.Fatal("Error inserting new server history") + } + + res, err := aud.GetObservedSTR(&AuditingRequest{ + DirectoryAddr: "test-server"}) + obs := res.DirectoryResponse.(*ObservedSTR) + if err != ReqSuccess { + t.Fatal("Unable to get latest observed STR") + } + if obs.STR == nil { + t.Fatal("Expect returned STR to be not nil") + } + if obs.STR.Epoch != d.LatestSTR().Epoch { + t.Fatal("Unexpected epoch for returned STR") + } +} + +func TestGetObservedSTRInEpoch(t *testing.T) { + // let's just create basic test directory and an empty audit log + d, pk := NewTestDirectory(t, true) + aud := NewAuditLog() + + // create 10 epochs + priorSTRs := make(map[uint64]*m.SignedTreeRoot) + for i := 0; i < 10; i++ { + priorSTRs[d.LatestSTR().Epoch] = d.LatestSTR() + d.Update() + } + + // now insert into the log + err := aud.Insert("test-server", pk, priorSTRs, d.LatestSTR()) + if err != nil { + t.Fatal("Error inserting new server history with prior STRs") + } + + res, err := aud.GetObservedSTRInEpoch(&AuditingInEpochRequest{ + DirectoryAddr: "test-server", + Epoch: uint64(6)}) + obs := res.DirectoryResponse.(*ObservedSTRs) + if err != ReqSuccess { + t.Fatal("Unable to get latest range of STRs") + } + if obs.STR == nil { + t.Fatal("Expect returned STR to be not nil") + } + if len(obs.STR) != 5 { + t.Fatal("Expect 5 returned STRs") + } + if obs.STR[0].Epoch != 6 || obs.STR[4].Epoch != d.LatestSTR().Epoch { + t.Fatal("Unexpected epoch for returned STRs") + } +} + +func TestGetObservedSTRUnknown(t *testing.T) { + // let's just create basic test directory and an empty audit log + d, pk := NewTestDirectory(t, true) + aud := NewAuditLog() + + // create 10 epochs + priorSTRs := make(map[uint64]*m.SignedTreeRoot) + for i := 0; i < 10; i++ { + priorSTRs[d.LatestSTR().Epoch] = d.LatestSTR() + d.Update() + } + + // now insert into the log + err := aud.Insert("test-server", pk, priorSTRs, d.LatestSTR()) + if err != nil { + t.Fatal("Error inserting new server history with prior STRs") + } + + _, err = aud.GetObservedSTR(&AuditingRequest{ + DirectoryAddr: "unknown"}) + if err != ReqUnknownDirectory { + t.Fatal("Expect ReqUnknownDirectory for latest STR") + } + + _, err = aud.GetObservedSTRInEpoch(&AuditingInEpochRequest{ + DirectoryAddr: "unknown", + Epoch: uint64(6)}) + if err != ReqUnknownDirectory { + t.Fatal("Expect ReqUnknownDirectory for older STR") + } + +} + +func TestGetObservedSTRMalformed(t *testing.T) { + // let's just create basic test directory and an empty audit log + d, pk := NewTestDirectory(t, true) + aud := NewAuditLog() + + // create 10 epochs + priorSTRs := make(map[uint64]*m.SignedTreeRoot) + for i := 0; i < 10; i++ { + priorSTRs[d.LatestSTR().Epoch] = d.LatestSTR() + d.Update() + } + + // now insert into the log + err := aud.Insert("test-server", pk, priorSTRs, d.LatestSTR()) + if err != nil { + t.Fatal("Error inserting new server history with prior STRs") + } + + _, err = aud.GetObservedSTR(&AuditingRequest{ + DirectoryAddr: ""}) + if err != ErrMalformedClientMessage { + t.Fatal("Expect ErrMalFormedClientMessage for latest STR") + } + + _, err = aud.GetObservedSTRInEpoch(&AuditingInEpochRequest{ + DirectoryAddr: "", + Epoch: uint64(6)}) + if err != ErrMalformedClientMessage { + t.Fatal("Expect ErrMalformedClientMessage for older STR") + } + + // also test the epoch range + _, err = aud.GetObservedSTRInEpoch(&AuditingInEpochRequest{ + DirectoryAddr: "", + Epoch: uint64(20)}) + if err != ErrMalformedClientMessage { + t.Fatal("Expect ErrMalformedClientMessage for older STR") + } + +} diff --git a/protocol/error.go b/protocol/error.go index 86c2bf4..97016b9 100644 --- a/protocol/error.go +++ b/protocol/error.go @@ -11,7 +11,7 @@ type ErrorCode int // These codes indicate the status of a client-server or client-auditor message // exchange. // Codes prefixed by "Req" indicate different client request results. -// Codes prefixed by "Err" indicate an internal server error or a malformed +// Codes prefixed by "Err" indicate an internal server/auditor error or a malformed // message. const ( ReqSuccess ErrorCode = iota + 100 @@ -21,6 +21,7 @@ const ( ReqUnknownDirectory ErrDirectory + ErrAuditLog ErrMalformedClientMessage ErrMalformedDirectoryMessage ) @@ -49,6 +50,7 @@ const ( var Errors = map[ErrorCode]bool{ ErrMalformedClientMessage: true, ErrDirectory: true, + ErrAuditLog: true, ErrMalformedDirectoryMessage: true, } @@ -60,6 +62,7 @@ var ( ErrMalformedClientMessage: "[coniks] Malformed client message", ErrDirectory: "[coniks] Directory error", + ErrAuditLog: "[coniks] Audit log error", ErrMalformedDirectoryMessage: "[coniks] Malformed directory message", CheckPassed: "[coniks] Consistency checks passed", From badbafcc60b6609eb6b604440c283b3a6eb80d38 Mon Sep 17 00:00:00 2001 From: Marcela M Date: Tue, 7 Mar 2017 19:19:54 -0500 Subject: [PATCH 16/43] Add assertions to validate auditor messages on client --- protocol/auditlog_test.go | 2 +- protocol/error.go | 3 +++ protocol/message.go | 10 ++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/protocol/auditlog_test.go b/protocol/auditlog_test.go index de489ef..ca45ca7 100644 --- a/protocol/auditlog_test.go +++ b/protocol/auditlog_test.go @@ -134,7 +134,7 @@ func TestGetObservedSTRInEpoch(t *testing.T) { if err != ReqSuccess { t.Fatal("Unable to get latest range of STRs") } - if obs.STR == nil { + if obs.STR == nil || len(obs.STR) < 1 { t.Fatal("Expect returned STR to be not nil") } if len(obs.STR) != 5 { diff --git a/protocol/error.go b/protocol/error.go index 97016b9..4e55a25 100644 --- a/protocol/error.go +++ b/protocol/error.go @@ -24,6 +24,7 @@ const ( ErrAuditLog ErrMalformedClientMessage ErrMalformedDirectoryMessage + ErrMalformedAuditorMessage ) // These codes indicate the result @@ -52,6 +53,7 @@ var Errors = map[ErrorCode]bool{ ErrDirectory: true, ErrAuditLog: true, ErrMalformedDirectoryMessage: true, + ErrMalformedAuditorMessage: true, } var ( @@ -64,6 +66,7 @@ var ( ErrDirectory: "[coniks] Directory error", ErrAuditLog: "[coniks] Audit log error", ErrMalformedDirectoryMessage: "[coniks] Malformed directory message", + ErrMalformedAuditorMessage: "[coniks] Malformed auditor message", CheckPassed: "[coniks] Consistency checks passed", CheckBadSignature: "[coniks] Directory's signature on STR or TB is invalid", diff --git a/protocol/message.go b/protocol/message.go index f0f560e..744fa7b 100644 --- a/protocol/message.go +++ b/protocol/message.go @@ -348,6 +348,16 @@ func (msg *Response) validate() error { case *DirectoryProofs: // TODO: also do above assertions here return nil + case *ObservedSTR: + if df.STR == nil { + return ErrMalformedAuditorMessage + } + return nil + case *ObservedSTRs: + if df.STR == nil || len(df.STR) < 1 { + return ErrMalformedAuditorMessage + } + return nil default: panic("[coniks] Malformed response") } From 3bd8653379978fe6571b6481737ee928df8631ed Mon Sep 17 00:00:00 2001 From: Marcela M Date: Tue, 7 Mar 2017 20:31:16 -0500 Subject: [PATCH 17/43] Add generic STR response handler --- protocol/consistencychecks.go | 55 ++++++++++++++++++++++++++++++++--- protocol/error.go | 2 +- protocol/message.go | 4 ++- 3 files changed, 55 insertions(+), 6 deletions(-) diff --git a/protocol/consistencychecks.go b/protocol/consistencychecks.go index dd8c98b..97efa1c 100644 --- a/protocol/consistencychecks.go +++ b/protocol/consistencychecks.go @@ -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. +func HandleDirectorySTRs(requestType int, msg *Response, signKey sign.PublicKey, + savedSTR *m.SignedTreeRoot, e error, isClient bool) error { + var str *m.SignedTreeRoot + if err := msg.validate(); err != nil { + return e + } + + switch requestType { + case AuditType: + if _, ok := msg.DirectoryResponse.(*ObservedSTR); !ok { + return e + } + str = msg.DirectoryResponse.(*ObservedSTR).STR + // TODO: compare the STR with the saved one on the client + // if the auditor has returned a more recent STR, should the + // client update its savedSTR? Should this force a new round of + // monitoring? + case AuditInEpochType: + // this is the default request type for an auditor + // since the auditor conservatively assumes it may + // have missed epochs + + // FIXME + //if _, ok := msg.DirectoryResponse.(*ObservedSTRs); !ok { + // return e + //} + //str = msg.DirectoryResponse.(*ObservedSTRs).STR + default: + panic("[coniks] Unknown auditing request type") + } + // we assume the requestType is AuditInEpochType if we're here + + // verify the timeliness of the STR if we're the auditor + // check the consistency of the newly received STRs + // FIXME: use `XXInEpoch` version of verifySTRConsistency + if err := verifySTRConsistency(signKey, savedSTR, str); err != nil { + return err + } + + return nil +} + func (cc *ConsistencyChecks) updateSTR(requestType int, msg *Response) error { var str *DirSTR switch requestType { @@ -110,9 +154,6 @@ func (cc *ConsistencyChecks) updateSTR(requestType int, msg *Response) error { cc.SavedSTR = str return nil } - // FIXME: check whether the STR was issued on time and whatnot. - // Maybe it has something to do w/ #81 and client transitioning between epochs. - // Try to verify w/ what's been saved if err := cc.verifySTR(str); err == nil { return nil } @@ -132,7 +173,13 @@ func (cc *ConsistencyChecks) updateSTR(requestType int, msg *Response) error { // verifySTR checks whether the received STR is the same with // the SavedSTR using reflect.DeepEqual(). -func (cc *ConsistencyChecks) verifySTR(str *DirSTR) error { +// FIXME: check whether the STR was issued on time and whatnot. +// Maybe it has something to do w/ #81 and client transitioning between epochs. +// Try to verify w/ what's been saved +// FIXME: make this generic so the auditor can also verify the timeliness of the +// STR etc. Might make sense to separate the comparison, which is only done on the client, +// from the rest. +func (cc *ConsistencyChecks) verifySTR(str *m.SignedTreeRoot) error { if reflect.DeepEqual(cc.SavedSTR, str) { return nil } diff --git a/protocol/error.go b/protocol/error.go index 4e55a25..be16002 100644 --- a/protocol/error.go +++ b/protocol/error.go @@ -66,7 +66,7 @@ var ( ErrDirectory: "[coniks] Directory error", ErrAuditLog: "[coniks] Audit log error", ErrMalformedDirectoryMessage: "[coniks] Malformed directory message", - ErrMalformedAuditorMessage: "[coniks] Malformed auditor message", + ErrMalformedAuditorMessage: "[coniks] Malformed auditor message", CheckPassed: "[coniks] Consistency checks passed", CheckBadSignature: "[coniks] Directory's signature on STR or TB is invalid", diff --git a/protocol/message.go b/protocol/message.go index 744fa7b..d5c26c5 100644 --- a/protocol/message.go +++ b/protocol/message.go @@ -12,6 +12,8 @@ const ( KeyLookupType KeyLookupInEpochType MonitoringType + AuditType + AuditInEpochType ) // A Request message defines the data a CONIKS client must send to a CONIKS @@ -202,7 +204,7 @@ type ObservedSTR struct { // An ObservedSTRs response includes a list of signed tree roots // STR. A CONIKS auditor returns this DirectoryResponse type upon an -// AudutingRequest. +// AuditingInEpochRequest. type ObservedSTRs struct { STR []*m.SignedTreeRoot } From c2d107ff03c7a93caac52df7df970ae49cadb49f Mon Sep 17 00:00:00 2001 From: Marcela M Date: Fri, 10 Mar 2017 14:59:27 -0500 Subject: [PATCH 18/43] # This is a combination of 2 commits. # The first commit's message is: Add TODO to move all generic STR auditing code to a separate module # The 2nd commit message will be skipped: # Use single generic verifySTRConsistency to be used by client and auditor --- protocol/consistencychecks.go | 1 + protocol/message.go | 31 ++++++++++++++++++++++++++++--- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/protocol/consistencychecks.go b/protocol/consistencychecks.go index 97efa1c..c04bc8a 100644 --- a/protocol/consistencychecks.go +++ b/protocol/consistencychecks.go @@ -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 package protocol diff --git a/protocol/message.go b/protocol/message.go index d5c26c5..82b918c 100644 --- a/protocol/message.go +++ b/protocol/message.go @@ -147,6 +147,34 @@ type AuditingInEpochRequest struct { Epoch uint64 `json:"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 +// sends to a CONIKS directory, to request the given directory's latest STR. +// If the client/auditor needs to request a directory's STR for a prior epoch, it +// must send an AuditingInEpochRequest. +// +// The response to a successful request is an ObservedSTR. +type AuditingRequest struct { + DirectoryAddr string `json:"directory_addr"` +} + +// 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, +// to retrieve the STR for the directory in +// the given epoch. The client/auditor sends this request type when it needs to +// audit a directory's STR for a prior epoch (i.e. as part of a +// key lookup in epoch check, a monitoring check, or an auditor update). +// The client/auditor can send an AuditingRequest if it needs to audit a +// directory's STR for its latest epoch. +// +// The response to a successful request is an ObservedSTRs with +// a list of STRs covering the epoch range [Epoch, d.LatestSTR().Epoch]. +type AuditingInEpochRequest struct { + DirectoryAddr string `json:"directory_addr"` + Epoch uint64 `json:"epoch"` +} + // A Response message indicates the result of a CONIKS client request // with an appropriate error code, and defines the set of cryptographic // proofs a CONIKS directory must return as part of its response. @@ -308,9 +336,6 @@ func NewMonitoringProof(ap []*m.AuthenticationPath, // upon an AuditingRequest, and returns a Response containing an ObservedSTR struct. // auditlog.GetObservedSTR(), or directory.GetLatestSTR(), passes the signed // tree root for the directory's latest str. -// -// See auditlog.Audit() for details on the contents of the created -// ObservedSTR. func NewObservedSTR(str *m.SignedTreeRoot) (*Response, ErrorCode) { return &Response{ Error: ReqSuccess, From 13d3eb2e1ddcf93c042ebbd60372f8a4f34da68e Mon Sep 17 00:00:00 2001 From: Marcela Melara Date: Sun, 12 Mar 2017 18:00:08 -0400 Subject: [PATCH 19/43] Fix documentation --- protocol/auditlog.go | 32 ++++++++++++++++++++++---------- protocol/auditlog_test.go | 12 ++++++++---- protocol/consistencychecks.go | 5 +++-- 3 files changed, 33 insertions(+), 16 deletions(-) diff --git a/protocol/auditlog.go b/protocol/auditlog.go index 116d8e7..a2a505a 100644 --- a/protocol/auditlog.go +++ b/protocol/auditlog.go @@ -54,6 +54,17 @@ func (l *ConiksAuditLog) IsKnownDirectory(addr string) bool { return false } +// Insert creates a new directory history for the key directory addr +// and inserts it into the audit log l. +// The directory history is initialized with the key directory's +// signing key signKey, a list of STRs representing the +// directory's prior history oldSTRs, and the directory's latest STR +// latestSTR. +// Insert() returns an ErrAuditLog if the auditor attempts to create +// a new history for a known directory, an ErrMalformedDirectoryMessage +// if oldSTRs is malformed, and nil otherwise. +// Insert() only creates the initial entry in the log for addr. Use Update() +// to insert newly observed STRs for addr in subsequent epochs. // 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 @@ -96,7 +107,7 @@ func (l *ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, // 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 + // error if we want to update the entry for an addr we don't know if !l.IsKnownDirectory(addr) { return ErrAuditLog } @@ -113,8 +124,9 @@ func (l *ConiksAuditLog) Update(addr string, newSTR *m.SignedTreeRoot) error { return nil } -// GetObservedSTR gets the observed STR for the CONIKS directory address indicated -// in the AuditingRequest req received from a CONIKS client from the auditor's latest +// GetObservedSTR gets the observed STR for the CONIKS directory address +// indicated in the AuditingRequest req received from a CONIKS client from +// the auditor's latest // directory history entry, and returns a tuple of the form // (response, error). // The response (which also includes the error code) is supposed to @@ -144,22 +156,22 @@ func (l *ConiksAuditLog) GetObservedSTR(req *AuditingRequest) (*Response, ErrorC return NewErrorResponse(ReqUnknownDirectory), ReqUnknownDirectory } -// GetObservedSTRInEpoch gets the observed STR for the CONIKS directory address -// for a prior directory history entry indicated in the +// GetObservedSTRInEpoch gets the observed STR for the CONIKS directory +// address for a prior directory history entry indicated in the // AuditingInEpochRequest req received from a CONIKS client, // and returns a tuple of the form (response, error). // The response (which also includes the error code) is supposed to // be sent back to the client. The returned error is used by the auditor // for logging purposes. // -// A request without a directory address or with an epoch greater than the latest -// observed epoch of this directory is considered malformed, and causes -// GetObservedSTRInEpoch() to return a +// A request without a directory address or with an epoch greater than +// the latest observed epoch of this directory is considered malformed, +// and causes GetObservedSTRInEpoch() to return a // message.NewErrorResponse(ErrMalformedClientMessage) tuple. // GetObservedSTRInEpoch() returns a message.NewObservedSTRs(strs) tuple. // strs is a list of STRs for the epoch range [ep, -// l.histories[req.DirectoryAddr].latestSTR.Epoch], where ep is the past epoch -// for which the client has requested the observed STR. +// l.histories[req.DirectoryAddr].latestSTR.Epoch], where ep is the past +// epoch for which the client has requested the observed STR. // If the auditor doesn't have any history entries for the requested CONIKS // directory, GetObservedSTR() returns a // message.NewErrorResponse(ReqUnknownDirectory) tuple. diff --git a/protocol/auditlog_test.go b/protocol/auditlog_test.go index ca45ca7..b57f543 100644 --- a/protocol/auditlog_test.go +++ b/protocol/auditlog_test.go @@ -63,7 +63,8 @@ func TestInsertExistingHistory(t *testing.T) { t.Fatal("Error inserting new server history") } - // let's make sure that we can't re-insert a new server history into our log + // let's make sure that we can't re-insert a new server + // history into our log err = aud.Insert("test-server", pk, nil, d.LatestSTR()) if err != ErrAuditLog { t.Fatal("Expected an ErrAuditLog when inserting an existing server history") @@ -79,7 +80,8 @@ func TestUpdateUnknownHistory(t *testing.T) { t.Fatal("Error inserting new server history") } - // let's make sure that we can't re-insert a new server history into our log + // let's make sure that we can't re-insert a new server + // history into our log err = aud.Update("unknown", d.LatestSTR()) if err != ErrAuditLog { t.Fatal("Expected an ErrAuditLog when updating an unknown server history") @@ -97,10 +99,11 @@ func TestGetObservedSTR(t *testing.T) { res, err := aud.GetObservedSTR(&AuditingRequest{ DirectoryAddr: "test-server"}) - obs := res.DirectoryResponse.(*ObservedSTR) if err != ReqSuccess { t.Fatal("Unable to get latest observed STR") } + + obs := res.DirectoryResponse.(*ObservedSTR) if obs.STR == nil { t.Fatal("Expect returned STR to be not nil") } @@ -130,10 +133,11 @@ func TestGetObservedSTRInEpoch(t *testing.T) { res, err := aud.GetObservedSTRInEpoch(&AuditingInEpochRequest{ DirectoryAddr: "test-server", Epoch: uint64(6)}) - obs := res.DirectoryResponse.(*ObservedSTRs) if err != ReqSuccess { t.Fatal("Unable to get latest range of STRs") } + + obs := res.DirectoryResponse.(*ObservedSTRs) if obs.STR == nil || len(obs.STR) < 1 { t.Fatal("Expect returned STR to be not nil") } diff --git a/protocol/consistencychecks.go b/protocol/consistencychecks.go index c04bc8a..a587488 100644 --- a/protocol/consistencychecks.go +++ b/protocol/consistencychecks.go @@ -101,8 +101,9 @@ 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. +// 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, savedSTR *m.SignedTreeRoot, e error, isClient bool) error { var str *m.SignedTreeRoot From 0d0d31267c7a04ebf14dcf7036374ca6204a2050 Mon Sep 17 00:00:00 2001 From: Marcela Melara Date: Sun, 12 Mar 2017 18:27:33 -0400 Subject: [PATCH 20/43] Use DirSTR instead of merkletree.SignedTreeRoot in auditlog --- protocol/auditlog.go | 15 +++++++-------- protocol/auditlog_test.go | 9 ++++----- protocol/consistencychecks.go | 8 ++++---- protocol/message.go | 20 +++----------------- 4 files changed, 18 insertions(+), 34 deletions(-) diff --git a/protocol/auditlog.go b/protocol/auditlog.go index a2a505a..037f7c9 100644 --- a/protocol/auditlog.go +++ b/protocol/auditlog.go @@ -7,13 +7,12 @@ package protocol import ( "github.com/coniks-sys/coniks-go/crypto/sign" - m "github.com/coniks-sys/coniks-go/merkletree" ) type directoryHistory struct { signKey sign.PublicKey - snapshots map[uint64]*m.SignedTreeRoot - latestSTR *m.SignedTreeRoot + snapshots map[uint64]*DirSTR + latestSTR *DirSTR } // A ConiksAuditLog maintains the histories @@ -25,10 +24,10 @@ type ConiksAuditLog struct { histories map[string]*directoryHistory } -func newDirectoryHistory(signKey sign.PublicKey, str *m.SignedTreeRoot) *directoryHistory { +func newDirectoryHistory(signKey sign.PublicKey, str *DirSTR) *directoryHistory { h := new(directoryHistory) h.signKey = signKey - h.snapshots = make(map[uint64]*m.SignedTreeRoot) + h.snapshots = make(map[uint64]*DirSTR) h.latestSTR = str return h } @@ -69,7 +68,7 @@ func (l *ConiksAuditLog) IsKnownDirectory(addr string) bool { // 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, - oldSTRs map[uint64]*m.SignedTreeRoot, latestSTR *m.SignedTreeRoot) error { + oldSTRs map[uint64]*DirSTR, latestSTR *DirSTR) error { // error if we want to create a new entry for an addr we already know if l.IsKnownDirectory(addr) { @@ -105,7 +104,7 @@ func (l *ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, // addr prior to its first call and thereby expects that an entry for addr // exists in the audit log l. // FIXME: pass Response message as param -func (l *ConiksAuditLog) Update(addr string, newSTR *m.SignedTreeRoot) error { +func (l *ConiksAuditLog) Update(addr string, newSTR *DirSTR) error { // error if we want to update the entry for an addr we don't know if !l.IsKnownDirectory(addr) { @@ -192,7 +191,7 @@ func (l *ConiksAuditLog) GetObservedSTRInEpoch(req *AuditingInEpochRequest) (*Re ErrMalformedClientMessage } - var strs []*m.SignedTreeRoot + var strs []*DirSTR startEp := req.Epoch endEp := h.latestSTR.Epoch diff --git a/protocol/auditlog_test.go b/protocol/auditlog_test.go index b57f543..7783532 100644 --- a/protocol/auditlog_test.go +++ b/protocol/auditlog_test.go @@ -1,7 +1,6 @@ package protocol import ( - m "github.com/coniks-sys/coniks-go/merkletree" "testing" ) @@ -41,7 +40,7 @@ func TestInsertPriorHistory(t *testing.T) { aud := NewAuditLog() // create 10 epochs - priorSTRs := make(map[uint64]*m.SignedTreeRoot) + priorSTRs := make(map[uint64]*DirSTR) for i := 0; i < 10; i++ { priorSTRs[d.LatestSTR().Epoch] = d.LatestSTR() d.Update() @@ -118,7 +117,7 @@ func TestGetObservedSTRInEpoch(t *testing.T) { aud := NewAuditLog() // create 10 epochs - priorSTRs := make(map[uint64]*m.SignedTreeRoot) + priorSTRs := make(map[uint64]*DirSTR) for i := 0; i < 10; i++ { priorSTRs[d.LatestSTR().Epoch] = d.LatestSTR() d.Update() @@ -155,7 +154,7 @@ func TestGetObservedSTRUnknown(t *testing.T) { aud := NewAuditLog() // create 10 epochs - priorSTRs := make(map[uint64]*m.SignedTreeRoot) + priorSTRs := make(map[uint64]*DirSTR) for i := 0; i < 10; i++ { priorSTRs[d.LatestSTR().Epoch] = d.LatestSTR() d.Update() @@ -188,7 +187,7 @@ func TestGetObservedSTRMalformed(t *testing.T) { aud := NewAuditLog() // create 10 epochs - priorSTRs := make(map[uint64]*m.SignedTreeRoot) + priorSTRs := make(map[uint64]*DirSTR) for i := 0; i < 10; i++ { priorSTRs[d.LatestSTR().Epoch] = d.LatestSTR() d.Update() diff --git a/protocol/consistencychecks.go b/protocol/consistencychecks.go index a587488..9521a3f 100644 --- a/protocol/consistencychecks.go +++ b/protocol/consistencychecks.go @@ -105,8 +105,8 @@ func (cc *ConsistencyChecks) HandleResponse(requestType int, msg *Response, // handle auditor responses, and by CONIKS auditors to handle directory // responses. func HandleDirectorySTRs(requestType int, msg *Response, signKey sign.PublicKey, - savedSTR *m.SignedTreeRoot, e error, isClient bool) error { - var str *m.SignedTreeRoot + savedSTR *DirSTR, e error, isClient bool) error { + var str *DirSTR if err := msg.validate(); err != nil { return e } @@ -181,7 +181,7 @@ func (cc *ConsistencyChecks) updateSTR(requestType int, msg *Response) error { // FIXME: make this generic so the auditor can also verify the timeliness of the // STR etc. Might make sense to separate the comparison, which is only done on the client, // from the rest. -func (cc *ConsistencyChecks) verifySTR(str *m.SignedTreeRoot) error { +func (cc *ConsistencyChecks) verifySTR(str *DirSTR) error { if reflect.DeepEqual(cc.SavedSTR, str) { return nil } @@ -195,7 +195,7 @@ func (cc *ConsistencyChecks) verifySTR(str *m.SignedTreeRoot) error { // in its history. // In the case of a client-side consistency check, verifySTRConsistency() // should not verify the hash chain using the STR stored in cc. -func verifySTRConsistency(signKey sign.PublicKey, savedSTR, str *m.SignedTreeRoot) error { +func verifySTRConsistency(signKey sign.PublicKey, savedSTR, str *DirSTR) error { // verify STR's signature if !signKey.Verify(str.Serialize(), str.Signature) { return CheckBadSignature diff --git a/protocol/message.go b/protocol/message.go index 82b918c..684b1a3 100644 --- a/protocol/message.go +++ b/protocol/message.go @@ -213,28 +213,14 @@ type DirectoryProofs struct { // STR. A CONIKS auditor returns this DirectoryResponse type upon an // AuditingRequest. type ObservedSTR struct { - STR *m.SignedTreeRoot + STR *DirSTR } // An ObservedSTRs response includes a list of signed tree roots // STR. A CONIKS auditor returns this DirectoryResponse type upon an // AudutingRequest. type ObservedSTRs struct { - STR []*m.SignedTreeRoot -} - -// An ObservedSTR response includes a single signed tree root -// STR. A CONIKS auditor returns this DirectoryResponse type upon an -// AuditingRequest. -type ObservedSTR struct { - STR *m.SignedTreeRoot -} - -// An ObservedSTRs response includes a list of signed tree roots -// STR. A CONIKS auditor returns this DirectoryResponse type upon an -// AuditingInEpochRequest. -type ObservedSTRs struct { - STR []*m.SignedTreeRoot + STR []*DirSTR } // NewErrorResponse creates a new response message indicating the error @@ -353,7 +339,7 @@ func NewObservedSTR(str *m.SignedTreeRoot) (*Response, ErrorCode) { // // See auditlog.AuditInEpoch() for details on the contents of the created // ObservedSTRs. -func NewObservedSTRs(str []*m.SignedTreeRoot) (*Response, ErrorCode) { +func NewObservedSTRs(str []*DirSTR) (*Response, ErrorCode) { return &Response{ Error: ReqSuccess, DirectoryResponse: &ObservedSTRs{ From 8b1eeebb35f8d78ff140c2795a50f8f7c02c2a25 Mon Sep 17 00:00:00 2001 From: Marcela M Date: Tue, 4 Apr 2017 17:43:13 -0400 Subject: [PATCH 21/43] Remove all references to auditor-directory communication, make auditor response message generic --- protocol/auditlog.go | 53 +++++------------------- protocol/auditlog_test.go | 38 ++++++++++-------- protocol/consistencychecks.go | 49 +--------------------- protocol/message.go | 76 ++++++++--------------------------- 4 files changed, 49 insertions(+), 167 deletions(-) diff --git a/protocol/auditlog.go b/protocol/auditlog.go index 037f7c9..762e054 100644 --- a/protocol/auditlog.go +++ b/protocol/auditlog.go @@ -123,41 +123,9 @@ func (l *ConiksAuditLog) Update(addr string, newSTR *DirSTR) error { return nil } -// GetObservedSTR gets the observed STR for the CONIKS directory address -// indicated in the AuditingRequest req received from a CONIKS client from -// the auditor's latest -// directory history entry, and returns a tuple of the form -// (response, error). -// The response (which also includes the error code) is supposed to -// be sent back to the client. The returned error is used by the auditor -// for logging purposes. -// -// A request without a directory address is considered -// malformed, and causes GetObservedSTR() to return a -// message.NewErrorResponse(ErrMalformedClientMessage) tuple. -// GetObservedSTR() returns a message.NewObservedSTR(str) tuple. -// str is the signed tree root the auditor has observed for the latest epoch. -// If the auditor doesn't have any history entries for the requested CONIKS -// directory, GetObservedSTR() returns a -// message.NewErrorResponse(ReqUnknownDirectory) tuple. -func (l *ConiksAuditLog) GetObservedSTR(req *AuditingRequest) (*Response, ErrorCode) { - - // make sure the request is well-formed - if len(req.DirectoryAddr) <= 0 { - return NewErrorResponse(ErrMalformedClientMessage), - ErrMalformedClientMessage - } - - if h := l.histories[req.DirectoryAddr]; h != nil { - return NewObservedSTR(h.latestSTR) - } - - return NewErrorResponse(ReqUnknownDirectory), ReqUnknownDirectory -} - -// GetObservedSTRInEpoch gets the observed STR for the CONIKS directory -// address for a prior directory history entry indicated in the -// AuditingInEpochRequest req received from a CONIKS client, +// GetObservedSTRs gets the observed STR for the CONIKS directory +// address for a directory history entry indicated in the +// AuditingRequest req received from a CONIKS client, // and returns a tuple of the form (response, error). // The response (which also includes the error code) is supposed to // be sent back to the client. The returned error is used by the auditor @@ -165,16 +133,17 @@ func (l *ConiksAuditLog) GetObservedSTR(req *AuditingRequest) (*Response, ErrorC // // A request without a directory address or with an epoch greater than // the latest observed epoch of this directory is considered malformed, -// and causes GetObservedSTRInEpoch() to return a +// and causes GetObservedSTRs() to return a // message.NewErrorResponse(ErrMalformedClientMessage) tuple. -// GetObservedSTRInEpoch() returns a message.NewObservedSTRs(strs) tuple. +// GetObservedSTRs() returns a message.NewSTRList(strs) tuple. // strs is a list of STRs for the epoch range [ep, -// l.histories[req.DirectoryAddr].latestSTR.Epoch], where ep is the past -// epoch for which the client has requested the observed STR. +// l.histories[req.DirectoryAddr].latestSTR.Epoch], where ep is the epoch for +// which the client has requested the observed STR; i.e. if ep == the latest epoch, +// the list returned is of length 1. // If the auditor doesn't have any history entries for the requested CONIKS -// directory, GetObservedSTR() returns a +// directory, GetObservedSTRs() returns a // message.NewErrorResponse(ReqUnknownDirectory) tuple. -func (l *ConiksAuditLog) GetObservedSTRInEpoch(req *AuditingInEpochRequest) (*Response, +func (l *ConiksAuditLog) GetObservedSTRs(req *AuditingRequest) (*Response, ErrorCode) { // make sure the request is well-formed @@ -203,7 +172,7 @@ func (l *ConiksAuditLog) GetObservedSTRInEpoch(req *AuditingInEpochRequest) (*Re // don't forget to append the latest STR strs = append(strs, h.latestSTR) - return NewObservedSTRs(strs) + return NewSTRList(strs) } return NewErrorResponse(ReqUnknownDirectory), ReqUnknownDirectory diff --git a/protocol/auditlog_test.go b/protocol/auditlog_test.go index 7783532..896e081 100644 --- a/protocol/auditlog_test.go +++ b/protocol/auditlog_test.go @@ -87,7 +87,7 @@ func TestUpdateUnknownHistory(t *testing.T) { } } -func TestGetObservedSTR(t *testing.T) { +func TestGetLatestObservedSTR(t *testing.T) { // let's just create basic test directory and an empty audit log d, pk := NewTestDirectory(t, true) aud := NewAuditLog() @@ -96,17 +96,18 @@ func TestGetObservedSTR(t *testing.T) { t.Fatal("Error inserting new server history") } - res, err := aud.GetObservedSTR(&AuditingRequest{ - DirectoryAddr: "test-server"}) + res, err := aud.GetObservedSTRs(&AuditingRequest{ + DirectoryAddr: "test-server", + Epoch: uint64(d.LatestSTR().Epoch)}) if err != ReqSuccess { t.Fatal("Unable to get latest observed STR") } - obs := res.DirectoryResponse.(*ObservedSTR) - if obs.STR == nil { + obs := res.DirectoryResponse.(*STRList) + if len(obs.STR) != 1 { t.Fatal("Expect returned STR to be not nil") } - if obs.STR.Epoch != d.LatestSTR().Epoch { + if obs.STR[0].Epoch != d.LatestSTR().Epoch { t.Fatal("Unexpected epoch for returned STR") } } @@ -129,15 +130,16 @@ func TestGetObservedSTRInEpoch(t *testing.T) { t.Fatal("Error inserting new server history with prior STRs") } - res, err := aud.GetObservedSTRInEpoch(&AuditingInEpochRequest{ + res, err := aud.GetObservedSTRs(&AuditingRequest{ DirectoryAddr: "test-server", - Epoch: uint64(6)}) + Epoch: uint64(6)}) + if err != ReqSuccess { t.Fatal("Unable to get latest range of STRs") } - obs := res.DirectoryResponse.(*ObservedSTRs) - if obs.STR == nil || len(obs.STR) < 1 { + obs := res.DirectoryResponse.(*STRList) + if len(obs.STR) == 0 { t.Fatal("Expect returned STR to be not nil") } if len(obs.STR) != 5 { @@ -166,13 +168,14 @@ func TestGetObservedSTRUnknown(t *testing.T) { t.Fatal("Error inserting new server history with prior STRs") } - _, err = aud.GetObservedSTR(&AuditingRequest{ - DirectoryAddr: "unknown"}) + _, err = aud.GetObservedSTRs(&AuditingRequest{ + DirectoryAddr: "unknown", + Epoch: uint64(d.LatestSTR().Epoch)}) if err != ReqUnknownDirectory { t.Fatal("Expect ReqUnknownDirectory for latest STR") } - _, err = aud.GetObservedSTRInEpoch(&AuditingInEpochRequest{ + _, err = aud.GetObservedSTRs(&AuditingRequest{ DirectoryAddr: "unknown", Epoch: uint64(6)}) if err != ReqUnknownDirectory { @@ -199,13 +202,14 @@ func TestGetObservedSTRMalformed(t *testing.T) { t.Fatal("Error inserting new server history with prior STRs") } - _, err = aud.GetObservedSTR(&AuditingRequest{ - DirectoryAddr: ""}) + _, err = aud.GetObservedSTRs(&AuditingRequest{ + DirectoryAddr: "", + Epoch: uint64(d.LatestSTR().Epoch)}) if err != ErrMalformedClientMessage { t.Fatal("Expect ErrMalFormedClientMessage for latest STR") } - _, err = aud.GetObservedSTRInEpoch(&AuditingInEpochRequest{ + _, err = aud.GetObservedSTRs(&AuditingRequest{ DirectoryAddr: "", Epoch: uint64(6)}) if err != ErrMalformedClientMessage { @@ -213,7 +217,7 @@ func TestGetObservedSTRMalformed(t *testing.T) { } // also test the epoch range - _, err = aud.GetObservedSTRInEpoch(&AuditingInEpochRequest{ + _, err = aud.GetObservedSTRs(&AuditingRequest{ DirectoryAddr: "", Epoch: uint64(20)}) if err != ErrMalformedClientMessage { diff --git a/protocol/consistencychecks.go b/protocol/consistencychecks.go index 9521a3f..87d6141 100644 --- a/protocol/consistencychecks.go +++ b/protocol/consistencychecks.go @@ -101,51 +101,6 @@ 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. -func HandleDirectorySTRs(requestType int, msg *Response, signKey sign.PublicKey, - savedSTR *DirSTR, e error, isClient bool) error { - var str *DirSTR - if err := msg.validate(); err != nil { - return e - } - - switch requestType { - case AuditType: - if _, ok := msg.DirectoryResponse.(*ObservedSTR); !ok { - return e - } - str = msg.DirectoryResponse.(*ObservedSTR).STR - // TODO: compare the STR with the saved one on the client - // if the auditor has returned a more recent STR, should the - // client update its savedSTR? Should this force a new round of - // monitoring? - case AuditInEpochType: - // this is the default request type for an auditor - // since the auditor conservatively assumes it may - // have missed epochs - - // FIXME - //if _, ok := msg.DirectoryResponse.(*ObservedSTRs); !ok { - // return e - //} - //str = msg.DirectoryResponse.(*ObservedSTRs).STR - default: - panic("[coniks] Unknown auditing request type") - } - // we assume the requestType is AuditInEpochType if we're here - - // verify the timeliness of the STR if we're the auditor - // check the consistency of the newly received STRs - // FIXME: use `XXInEpoch` version of verifySTRConsistency - if err := verifySTRConsistency(signKey, savedSTR, str); err != nil { - return err - } - - return nil -} - func (cc *ConsistencyChecks) updateSTR(requestType int, msg *Response) error { var str *DirSTR switch requestType { @@ -191,10 +146,8 @@ func (cc *ConsistencyChecks) verifySTR(str *DirSTR) error { // verifySTRConsistency checks the consistency between 2 snapshots. // 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 +// pinned signing key, or an auditor's pinned signing key // in its history. -// In the case of a client-side consistency check, verifySTRConsistency() -// should not verify the hash chain using the STR stored in cc. func verifySTRConsistency(signKey sign.PublicKey, savedSTR, str *DirSTR) error { // verify STR's signature if !signKey.Verify(str.Serialize(), str.Signature) { diff --git a/protocol/message.go b/protocol/message.go index 684b1a3..2905cf8 100644 --- a/protocol/message.go +++ b/protocol/message.go @@ -13,7 +13,6 @@ const ( KeyLookupInEpochType MonitoringType AuditType - AuditInEpochType ) // A Request message defines the data a CONIKS client must send to a CONIKS @@ -148,29 +147,13 @@ type AuditingInEpochRequest struct { } // 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 -// sends to a CONIKS directory, to request the given directory's latest STR. -// If the client/auditor needs to request a directory's STR for a prior epoch, it -// must send an AuditingInEpochRequest. -// -// The response to a successful request is an ObservedSTR. -type AuditingRequest struct { - DirectoryAddr string `json:"directory_addr"` -} - -// 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, -// to retrieve the STR for the directory in -// the given epoch. The client/auditor sends this request type when it needs to -// audit a directory's STR for a prior epoch (i.e. as part of a -// key lookup in epoch check, a monitoring check, or an auditor update). -// The client/auditor can send an AuditingRequest if it needs to audit a -// directory's STR for its latest epoch. +// a CONIKS auditor to request the given directory's STR at the given +// epoch. // // The response to a successful request is an ObservedSTRs with // a list of STRs covering the epoch range [Epoch, d.LatestSTR().Epoch]. -type AuditingInEpochRequest struct { +type AuditingRequest struct { DirectoryAddr string `json:"directory_addr"` Epoch uint64 `json:"epoch"` } @@ -209,17 +192,10 @@ type DirectoryProofs struct { STR []*DirSTR } -// An ObservedSTR response includes a single signed tree root -// STR. A CONIKS auditor returns this DirectoryResponse type upon an -// AuditingRequest. -type ObservedSTR struct { - STR *DirSTR -} - -// An ObservedSTRs response includes a list of signed tree roots +// An STRList response includes a list of signed tree roots // STR. A CONIKS auditor returns this DirectoryResponse type upon an // AudutingRequest. -type ObservedSTRs struct { +type STRList struct { STR []*DirSTR } @@ -232,8 +208,7 @@ func NewErrorResponse(e ErrorCode) *Response { var _ DirectoryResponse = (*DirectoryProof)(nil) var _ DirectoryResponse = (*DirectoryProofs)(nil) -var _ DirectoryResponse = (*ObservedSTR)(nil) -var _ DirectoryResponse = (*ObservedSTRs)(nil) +var _ DirectoryResponse = (*STRList)(nil) // NewRegistrationProof creates the response message a CONIKS directory // sends to a client upon a RegistrationRequest, @@ -317,32 +292,18 @@ 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, -// upon an AuditingRequest, and returns a Response containing an ObservedSTR struct. -// auditlog.GetObservedSTR(), or directory.GetLatestSTR(), passes the signed -// tree root for the directory's latest str. -func NewObservedSTR(str *m.SignedTreeRoot) (*Response, ErrorCode) { - return &Response{ - Error: ReqSuccess, - DirectoryResponse: &ObservedSTR{ - STR: str, - }, - }, ReqSuccess -} - -// NewObservedSTRs creates the response message a CONIKS auditor -// sends to a client upon an AuditingInEpochRequest, -// and returns a Response containing an ObservedSTRs struct. -// auditlog.AuditInEpoch() passes a list of signed tree roots +// NewSTRList creates the response message a CONIKS auditor +// sends to a client upon an AuditingRequest, +// and returns a Response containing an STRList struct. +// auditlog.GetObservedSTRs() passes a list of one or more signed tree roots // that the auditor observed for the requested range of epochs str. // -// See auditlog.AuditInEpoch() for details on the contents of the created -// ObservedSTRs. -func NewObservedSTRs(str []*DirSTR) (*Response, ErrorCode) { +// See auditlog.GetObservedSTRs() for details on the contents of the created +// STRList. +func NewSTRList(str []*DirSTR) (*Response, ErrorCode) { return &Response{ Error: ReqSuccess, - DirectoryResponse: &ObservedSTRs{ + DirectoryResponse: &STRList{ STR: str, }, }, ReqSuccess @@ -361,13 +322,8 @@ func (msg *Response) validate() error { case *DirectoryProofs: // TODO: also do above assertions here return nil - case *ObservedSTR: - if df.STR == nil { - return ErrMalformedAuditorMessage - } - return nil - case *ObservedSTRs: - if df.STR == nil || len(df.STR) < 1 { + case *STRList: + if len(df.STR) == 0 { return ErrMalformedAuditorMessage } return nil From 3872e329fdddd6f44e4bf0f95b5880bafcc75e1a Mon Sep 17 00:00:00 2001 From: Marcela M Date: Wed, 5 Apr 2017 16:02:48 -0400 Subject: [PATCH 22/43] STRList -> STRHistoryRange --- protocol/auditlog.go | 4 +- protocol/auditlog_test.go | 12 +++--- protocol/message.go | 85 ++++++++------------------------------- protocol/str.go | 2 +- 4 files changed, 26 insertions(+), 77 deletions(-) diff --git a/protocol/auditlog.go b/protocol/auditlog.go index 762e054..63cb3d8 100644 --- a/protocol/auditlog.go +++ b/protocol/auditlog.go @@ -135,7 +135,7 @@ func (l *ConiksAuditLog) Update(addr string, newSTR *DirSTR) error { // the latest observed epoch of this directory is considered malformed, // and causes GetObservedSTRs() to return a // message.NewErrorResponse(ErrMalformedClientMessage) tuple. -// GetObservedSTRs() returns a message.NewSTRList(strs) tuple. +// GetObservedSTRs() returns a message.NewSTRHistoryRange(strs) tuple. // strs is a list of STRs for the epoch range [ep, // l.histories[req.DirectoryAddr].latestSTR.Epoch], where ep is the epoch for // which the client has requested the observed STR; i.e. if ep == the latest epoch, @@ -172,7 +172,7 @@ func (l *ConiksAuditLog) GetObservedSTRs(req *AuditingRequest) (*Response, // don't forget to append the latest STR strs = append(strs, h.latestSTR) - return NewSTRList(strs) + return NewSTRHistoryRange(strs) } return NewErrorResponse(ReqUnknownDirectory), ReqUnknownDirectory diff --git a/protocol/auditlog_test.go b/protocol/auditlog_test.go index 896e081..7bd9439 100644 --- a/protocol/auditlog_test.go +++ b/protocol/auditlog_test.go @@ -98,12 +98,12 @@ func TestGetLatestObservedSTR(t *testing.T) { res, err := aud.GetObservedSTRs(&AuditingRequest{ DirectoryAddr: "test-server", - Epoch: uint64(d.LatestSTR().Epoch)}) + Epoch: uint64(d.LatestSTR().Epoch)}) if err != ReqSuccess { t.Fatal("Unable to get latest observed STR") } - obs := res.DirectoryResponse.(*STRList) + obs := res.DirectoryResponse.(*STRHistoryRange) if len(obs.STR) != 1 { t.Fatal("Expect returned STR to be not nil") } @@ -132,13 +132,13 @@ func TestGetObservedSTRInEpoch(t *testing.T) { res, err := aud.GetObservedSTRs(&AuditingRequest{ DirectoryAddr: "test-server", - Epoch: uint64(6)}) + Epoch: uint64(6)}) if err != ReqSuccess { t.Fatal("Unable to get latest range of STRs") } - obs := res.DirectoryResponse.(*STRList) + obs := res.DirectoryResponse.(*STRHistoryRange) if len(obs.STR) == 0 { t.Fatal("Expect returned STR to be not nil") } @@ -170,7 +170,7 @@ func TestGetObservedSTRUnknown(t *testing.T) { _, err = aud.GetObservedSTRs(&AuditingRequest{ DirectoryAddr: "unknown", - Epoch: uint64(d.LatestSTR().Epoch)}) + Epoch: uint64(d.LatestSTR().Epoch)}) if err != ReqUnknownDirectory { t.Fatal("Expect ReqUnknownDirectory for latest STR") } @@ -204,7 +204,7 @@ func TestGetObservedSTRMalformed(t *testing.T) { _, err = aud.GetObservedSTRs(&AuditingRequest{ DirectoryAddr: "", - Epoch: uint64(d.LatestSTR().Epoch)}) + Epoch: uint64(d.LatestSTR().Epoch)}) if err != ErrMalformedClientMessage { t.Fatal("Expect ErrMalFormedClientMessage for latest STR") } diff --git a/protocol/message.go b/protocol/message.go index 2905cf8..e2217da 100644 --- a/protocol/message.go +++ b/protocol/message.go @@ -91,71 +91,16 @@ type MonitoringRequest struct { EndEpoch uint64 } -// An AuditingRequest is a message with a CONIKS key directory's address -// as a string that a CONIKS client sends to a CONIKS auditor to request -// the latest STR the auditor has observed for the given directory. -// If the client needs to request a directory's STR for a prior epoch, it -// must send an AuditingInEpochRequest. -// -// The response to a successful request is an ObservedDirectoryProof. -type AuditingRequest struct { - DirectoryAddr string `json:"directory_addr"` -} - -// 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 to retrieve the STR it observed for the directory in -// the given epoch. The client sends this request type when it needs to -// audit a directory's STR for a prior epoch (i.e. as part of a -// key lookup in epoch check or a monitoring check). The client can send an -// AuditingRequest if it needs to audit a directory's STR for its latest -// epoch. -// -// The response to a successful request is an ObservedDirectoryProofs with -// a list of STRs covering the epoch range [Epoch, d.LatestSTR().Epoch]. -type AuditingInEpochRequest struct { - DirectoryAddr string `json:"directory_addr"` - Epoch uint64 `json:"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 -// sends to a CONIKS directory, to request the given directory's latest STR. -// If the client/auditor needs to request a directory's STR for a prior epoch, it -// must send an AuditingInEpochRequest. -// -// The response to a successful request is an ObservedSTR. -type AuditingRequest struct { - DirectoryAddr string `json:"directory_addr"` -} - -// 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, -// to retrieve the STR for the directory in -// the given epoch. The client/auditor sends this request type when it needs to -// audit a directory's STR for a prior epoch (i.e. as part of a -// key lookup in epoch check, a monitoring check, or an auditor update). -// The client/auditor can send an AuditingRequest if it needs to audit a -// directory's STR for its latest epoch. -// -// The response to a successful request is an ObservedSTRs with -// a list of STRs covering the epoch range [Epoch, d.LatestSTR().Epoch]. -type AuditingInEpochRequest struct { - DirectoryAddr string `json:"directory_addr"` - Epoch uint64 `json:"epoch"` -} - // An AuditingRequest is a message with a CONIKS key directory's address // as a string and an epoch as a uint64 that a CONIKS client sends to // a CONIKS auditor to request the given directory's STR at the given // epoch. // -// The response to a successful request is an ObservedSTRs with +// The response to a successful request is an STRHistoryRange with // a list of STRs covering the epoch range [Epoch, d.LatestSTR().Epoch]. type AuditingRequest struct { - DirectoryAddr string `json:"directory_addr"` - Epoch uint64 `json:"epoch"` + DirectoryAddr string + Epoch uint64 } // A Response message indicates the result of a CONIKS client request @@ -192,10 +137,12 @@ type DirectoryProofs struct { STR []*DirSTR } -// An STRList response includes a list of signed tree roots -// STR. A CONIKS auditor returns this DirectoryResponse type upon an +// An STRHistoryRange response includes a list of signed tree roots +// STR representing a range of the STR hash chain. If the range only +// covers the latest epoch, the list only contains a single STR. +// A CONIKS auditor returns this DirectoryResponse type upon an // AudutingRequest. -type STRList struct { +type STRHistoryRange struct { STR []*DirSTR } @@ -208,7 +155,7 @@ func NewErrorResponse(e ErrorCode) *Response { var _ DirectoryResponse = (*DirectoryProof)(nil) var _ DirectoryResponse = (*DirectoryProofs)(nil) -var _ DirectoryResponse = (*STRList)(nil) +var _ DirectoryResponse = (*STRHistoryRange)(nil) // NewRegistrationProof creates the response message a CONIKS directory // sends to a client upon a RegistrationRequest, @@ -292,18 +239,18 @@ func NewMonitoringProof(ap []*m.AuthenticationPath, }, ReqSuccess } -// NewSTRList creates the response message a CONIKS auditor +// NewSTRHistoryRange creates the response message a CONIKS auditor // sends to a client upon an AuditingRequest, -// and returns a Response containing an STRList struct. +// and returns a Response containing an STRHistoryRange struct. // auditlog.GetObservedSTRs() passes a list of one or more signed tree roots // that the auditor observed for the requested range of epochs str. // // See auditlog.GetObservedSTRs() for details on the contents of the created -// STRList. -func NewSTRList(str []*DirSTR) (*Response, ErrorCode) { +// STRHistoryRange. +func NewSTRHistoryRange(str []*DirSTR) (*Response, ErrorCode) { return &Response{ Error: ReqSuccess, - DirectoryResponse: &STRList{ + DirectoryResponse: &STRHistoryRange{ STR: str, }, }, ReqSuccess @@ -322,7 +269,9 @@ func (msg *Response) validate() error { case *DirectoryProofs: // TODO: also do above assertions here return nil - case *STRList: + case *STRHistoryRange: + // treat the STRHistoryRange as an auditor response + // bc validate is only called by a client if len(df.STR) == 0 { return ErrMalformedAuditorMessage } diff --git a/protocol/str.go b/protocol/str.go index db92f96..826f173 100644 --- a/protocol/str.go +++ b/protocol/str.go @@ -19,7 +19,7 @@ func NewDirSTR(str *merkletree.SignedTreeRoot) *DirSTR { // Serialize overrides merkletree.SignedTreeRoot.Serialize func (str *DirSTR) Serialize() []byte { - return append(str.SerializeInternal(), str.Policies.Serialize()...) + return append(str.SignedTreeRoot.SerializeInternal(), str.Policies.Serialize()...) } // VerifyHashChain wraps merkletree.SignedTreeRoot.VerifyHashChain From 1f609a8ac903d5b9f649c1fca15fd89c68bee366 Mon Sep 17 00:00:00 2001 From: Marcela Date: Thu, 1 Jun 2017 16:19:18 -0700 Subject: [PATCH 23/43] Fail sooner in GetObservedSTRs --- protocol/auditlog.go | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/protocol/auditlog.go b/protocol/auditlog.go index 63cb3d8..03a2aea 100644 --- a/protocol/auditlog.go +++ b/protocol/auditlog.go @@ -152,28 +152,29 @@ func (l *ConiksAuditLog) GetObservedSTRs(req *AuditingRequest) (*Response, ErrMalformedClientMessage } - if h := l.histories[req.DirectoryAddr]; h != nil { + h := l.histories[req.DirectoryAddr] - // also make sure the epoch is well-formed - if req.Epoch > h.latestSTR.Epoch { - return NewErrorResponse(ErrMalformedClientMessage), - ErrMalformedClientMessage - } - - var strs []*DirSTR - startEp := req.Epoch - endEp := h.latestSTR.Epoch + if h == nil { + return NewErrorResponse(ReqUnknownDirectory), ReqUnknownDirectory + } - for ep := startEp; ep < endEp; ep++ { - str := h.snapshots[ep] - strs = append(strs, str) - } + // also make sure the epoch is well-formed + if req.Epoch > h.latestSTR.Epoch { + return NewErrorResponse(ErrMalformedClientMessage), + ErrMalformedClientMessage + } - // don't forget to append the latest STR - strs = append(strs, h.latestSTR) + var strs []*DirSTR + startEp := req.Epoch + endEp := h.latestSTR.Epoch - return NewSTRHistoryRange(strs) + for ep := startEp; ep < endEp; ep++ { + str := h.snapshots[ep] + strs = append(strs, str) } - return NewErrorResponse(ReqUnknownDirectory), ReqUnknownDirectory + // don't forget to append the latest STR + strs = append(strs, h.latestSTR) + + return NewSTRHistoryRange(strs) } From 477cddf16df3514685971b4884e0c7d9d12cfa5e Mon Sep 17 00:00:00 2001 From: Marcela Date: Fri, 2 Jun 2017 18:04:21 -0700 Subject: [PATCH 24/43] Revert changes to protocol/str.go --- protocol/str.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/str.go b/protocol/str.go index 826f173..db92f96 100644 --- a/protocol/str.go +++ b/protocol/str.go @@ -19,7 +19,7 @@ func NewDirSTR(str *merkletree.SignedTreeRoot) *DirSTR { // Serialize overrides merkletree.SignedTreeRoot.Serialize func (str *DirSTR) Serialize() []byte { - return append(str.SignedTreeRoot.SerializeInternal(), str.Policies.Serialize()...) + return append(str.SerializeInternal(), str.Policies.Serialize()...) } // VerifyHashChain wraps merkletree.SignedTreeRoot.VerifyHashChain From c6d106c3458f1886402fe1aae16238a589cc435a Mon Sep 17 00:00:00 2001 From: Marcela M Date: Mon, 26 Jun 2017 14:59:22 -0400 Subject: [PATCH 25/43] Always request epoch range in AuditingRequest, fix Insert() bug --- protocol/auditlog.go | 51 ++++++++++++++++++++++----------------- protocol/auditlog_test.go | 46 +++++++++++++++++++++++++---------- protocol/message.go | 12 +++++---- 3 files changed, 69 insertions(+), 40 deletions(-) diff --git a/protocol/auditlog.go b/protocol/auditlog.go index 03a2aea..5bafbab 100644 --- a/protocol/auditlog.go +++ b/protocol/auditlog.go @@ -75,6 +75,12 @@ func (l *ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, return ErrAuditLog } + // make sure we have oldEpochs unless we're inserting right + // at the start of a directory's history + if oldSTRs == nil && latestSTR.Epoch > 0 { + return ErrMalformedDirectoryMessage + } + // create the new directory history h := newDirectoryHistory(signKey, latestSTR) @@ -82,6 +88,9 @@ func (l *ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, endEp := latestSTR.Epoch // add each old STR into the history + // This loop automatically catches if oldSTRs is malformed + // (i.e. oldSTRs starts at epoch 0 and + // len(oldSTRs) != latestSTR.Epoch-1) for ep := startEp; ep < endEp; ep++ { str := oldSTRs[ep] if str == nil { @@ -90,6 +99,9 @@ func (l *ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, h.snapshots[ep] = str } + // don't forget to add the latest STR + h.snapshots[endEp] = latestSTR + l.histories[addr] = h // FIXME: verify the consistency of each new STR @@ -123,23 +135,21 @@ func (l *ConiksAuditLog) Update(addr string, newSTR *DirSTR) error { return nil } -// GetObservedSTRs gets the observed STR for the CONIKS directory -// address for a directory history entry indicated in the -// AuditingRequest req received from a CONIKS client, -// and returns a tuple of the form (response, error). -// The response (which also includes the error code) is supposed to -// be sent back to the client. The returned error is used by the auditor +// GetObservedSTRs gets a range of observed STRs for the CONIKS directory +// address indicated in the AuditingRequest req received from a +// CONIKS client, and returns a tuple of the form (response, error). +// The response (which also includes the error code) is sent back to +// the client. The returned error is used by the auditor // for logging purposes. // -// A request without a directory address or with an epoch greater than -// the latest observed epoch of this directory is considered malformed, -// and causes GetObservedSTRs() to return a +// A request without a directory address, with a StartEpoch or EndEpoch +// greater than the latest observed epoch of this directory, or with +// at StartEpoch > EndEpoch is considered +// malformed and causes GetObservedSTRs() to return a // message.NewErrorResponse(ErrMalformedClientMessage) tuple. // GetObservedSTRs() returns a message.NewSTRHistoryRange(strs) tuple. -// strs is a list of STRs for the epoch range [ep, -// l.histories[req.DirectoryAddr].latestSTR.Epoch], where ep is the epoch for -// which the client has requested the observed STR; i.e. if ep == the latest epoch, -// the list returned is of length 1. +// strs is a list of STRs for the epoch range [StartEpoch, EndEpoch]; +// if StartEpoch == EndEpoch, the list returned is of length 1. // If the auditor doesn't have any history entries for the requested CONIKS // directory, GetObservedSTRs() returns a // message.NewErrorResponse(ReqUnknownDirectory) tuple. @@ -147,7 +157,7 @@ func (l *ConiksAuditLog) GetObservedSTRs(req *AuditingRequest) (*Response, ErrorCode) { // make sure the request is well-formed - if len(req.DirectoryAddr) <= 0 { + if len(req.DirectoryAddr) <= 0 || req.StartEpoch > req.EndEpoch { return NewErrorResponse(ErrMalformedClientMessage), ErrMalformedClientMessage } @@ -158,23 +168,20 @@ func (l *ConiksAuditLog) GetObservedSTRs(req *AuditingRequest) (*Response, return NewErrorResponse(ReqUnknownDirectory), ReqUnknownDirectory } - // also make sure the epoch is well-formed - if req.Epoch > h.latestSTR.Epoch { + // also make sure the epoch range is well-formed + if req.StartEpoch > h.latestSTR.Epoch || req.EndEpoch > h.latestSTR.Epoch { return NewErrorResponse(ErrMalformedClientMessage), ErrMalformedClientMessage } var strs []*DirSTR - startEp := req.Epoch - endEp := h.latestSTR.Epoch - - for ep := startEp; ep < endEp; ep++ { + for ep := req.StartEpoch; ep < req.EndEpoch; ep++ { str := h.snapshots[ep] strs = append(strs, str) } - // don't forget to append the latest STR - strs = append(strs, h.latestSTR) + // don't forget to append the STR for EndEpoch + strs = append(strs, h.snapshots[req.EndEpoch]) return NewSTRHistoryRange(strs) } diff --git a/protocol/auditlog_test.go b/protocol/auditlog_test.go index 7bd9439..f24f0c3 100644 --- a/protocol/auditlog_test.go +++ b/protocol/auditlog_test.go @@ -98,7 +98,8 @@ func TestGetLatestObservedSTR(t *testing.T) { res, err := aud.GetObservedSTRs(&AuditingRequest{ DirectoryAddr: "test-server", - Epoch: uint64(d.LatestSTR().Epoch)}) + StartEpoch: uint64(d.LatestSTR().Epoch), + EndEpoch: uint64(d.LatestSTR().Epoch)}) if err != ReqSuccess { t.Fatal("Unable to get latest observed STR") } @@ -132,7 +133,8 @@ func TestGetObservedSTRInEpoch(t *testing.T) { res, err := aud.GetObservedSTRs(&AuditingRequest{ DirectoryAddr: "test-server", - Epoch: uint64(6)}) + StartEpoch: uint64(6), + EndEpoch: uint64(8)}) if err != ReqSuccess { t.Fatal("Unable to get latest range of STRs") @@ -142,10 +144,10 @@ func TestGetObservedSTRInEpoch(t *testing.T) { if len(obs.STR) == 0 { t.Fatal("Expect returned STR to be not nil") } - if len(obs.STR) != 5 { - t.Fatal("Expect 5 returned STRs") + if len(obs.STR) != 3 { + t.Fatal("Expect 3 returned STRs") } - if obs.STR[0].Epoch != 6 || obs.STR[4].Epoch != d.LatestSTR().Epoch { + if obs.STR[0].Epoch != 6 || obs.STR[2].Epoch != 8 { t.Fatal("Unexpected epoch for returned STRs") } } @@ -170,14 +172,16 @@ func TestGetObservedSTRUnknown(t *testing.T) { _, err = aud.GetObservedSTRs(&AuditingRequest{ DirectoryAddr: "unknown", - Epoch: uint64(d.LatestSTR().Epoch)}) + StartEpoch: uint64(0), + EndEpoch: uint64(d.LatestSTR().Epoch)}) if err != ReqUnknownDirectory { t.Fatal("Expect ReqUnknownDirectory for latest STR") } _, err = aud.GetObservedSTRs(&AuditingRequest{ DirectoryAddr: "unknown", - Epoch: uint64(6)}) + StartEpoch: uint64(6), + EndEpoch: uint64(8)}) if err != ReqUnknownDirectory { t.Fatal("Expect ReqUnknownDirectory for older STR") } @@ -204,24 +208,40 @@ func TestGetObservedSTRMalformed(t *testing.T) { _, err = aud.GetObservedSTRs(&AuditingRequest{ DirectoryAddr: "", - Epoch: uint64(d.LatestSTR().Epoch)}) + StartEpoch: uint64(0), + EndEpoch: uint64(d.LatestSTR().Epoch)}) if err != ErrMalformedClientMessage { t.Fatal("Expect ErrMalFormedClientMessage for latest STR") } _, err = aud.GetObservedSTRs(&AuditingRequest{ DirectoryAddr: "", - Epoch: uint64(6)}) + StartEpoch: uint64(4), + EndEpoch: uint64(6)}) if err != ErrMalformedClientMessage { t.Fatal("Expect ErrMalformedClientMessage for older STR") } // also test the epoch range _, err = aud.GetObservedSTRs(&AuditingRequest{ - DirectoryAddr: "", - Epoch: uint64(20)}) + DirectoryAddr: "test-server", + StartEpoch: uint64(6), + EndEpoch: uint64(4)}) if err != ErrMalformedClientMessage { - t.Fatal("Expect ErrMalformedClientMessage for older STR") + t.Fatal("Expect ErrMalformedClientMessage for bad end epoch") + } + _, err = aud.GetObservedSTRs(&AuditingRequest{ + DirectoryAddr: "test-server", + StartEpoch: uint64(11), + EndEpoch: uint64(11)}) + if err != ErrMalformedClientMessage { + t.Fatal("Expect ErrMalformedClientMessage for bad start epoch") + } + _, err = aud.GetObservedSTRs(&AuditingRequest{ + DirectoryAddr: "test-server", + StartEpoch: uint64(6), + EndEpoch: uint64(11)}) + if err != ErrMalformedClientMessage { + t.Fatal("Expect ErrMalformedClientMessage for out-of-bounds epoch range") } - } diff --git a/protocol/message.go b/protocol/message.go index e2217da..1b5310f 100644 --- a/protocol/message.go +++ b/protocol/message.go @@ -92,15 +92,17 @@ type MonitoringRequest struct { } // An AuditingRequest is a message with a CONIKS key directory's address -// as a string and an epoch as a uint64 that a CONIKS client sends to -// a CONIKS auditor to request the given directory's STR at the given -// epoch. +// as a string, and a StartEpoch and an EndEpoch as uint64's that a CONIKS +// client sends to a CONIKS auditor to request the given directory's +// STRs for the given epoch range. To obtain a single STR, the client +// must set StartEpoch = EndEpoch in the request. // // The response to a successful request is an STRHistoryRange with -// a list of STRs covering the epoch range [Epoch, d.LatestSTR().Epoch]. +// a list of STRs covering the epoch range [StartEpoch, EndEpoch]. type AuditingRequest struct { DirectoryAddr string - Epoch uint64 + StartEpoch uint64 + EndEpoch uint64 } // A Response message indicates the result of a CONIKS client request From 9843f4aae6e81f1aef044bacbdc9d27cdc269a56 Mon Sep 17 00:00:00 2001 From: Marcela M Date: Mon, 3 Jul 2017 15:56:12 -0400 Subject: [PATCH 26/43] Index audit log by hash of directory's initial STR --- protocol/auditlog.go | 84 ++++++++++++++++++++++++--------------- protocol/auditlog_test.go | 76 +++++++++++++++-------------------- protocol/message.go | 11 +++-- 3 files changed, 92 insertions(+), 79 deletions(-) diff --git a/protocol/auditlog.go b/protocol/auditlog.go index 5bafbab..d1d8ba1 100644 --- a/protocol/auditlog.go +++ b/protocol/auditlog.go @@ -6,26 +6,44 @@ package protocol import ( + "github.com/coniks-sys/coniks-go/crypto" "github.com/coniks-sys/coniks-go/crypto/sign" ) type directoryHistory struct { + dirName string signKey sign.PublicKey snapshots map[uint64]*DirSTR latestSTR *DirSTR } // A ConiksAuditLog maintains the histories -// of all CONIKS directories known to a CONIKS auditor. -// Each history includes the directory's public signing key -// enabling the auditor to verify the corresponding signed -// tree roots. +// of all CONIKS directories known to a CONIKS auditor, +// indexing the histories by the hash of a directory's initial +// STR. +// Each history includes the directory's domain name as a string, its +// public signing key enabling the auditor to verify the corresponding +// signed tree roots, and a list with all observed snapshots in +// chronological order. type ConiksAuditLog struct { - histories map[string]*directoryHistory + histories map[[crypto.HashSizeByte]byte]*directoryHistory } -func newDirectoryHistory(signKey sign.PublicKey, str *DirSTR) *directoryHistory { +// computeInitSTRHash is a wrapper for the digest function; +// returns nil if the STR isn't an initial STR (i.e. str.Epoch != 0) +func computeInitSTRHash(initSTR *DirSTR) [crypto.HashSizeByte]byte { + var initSTRHash [crypto.HashSizeByte]byte + + if initSTR.Epoch == 0 { + // ugh HACK: need to call copy to convert slice into the array + copy(initSTRHash[:], crypto.Digest(initSTR.Serialize())) + } + return initSTRHash +} + +func newDirectoryHistory(dirName string, signKey sign.PublicKey, str *DirSTR) *directoryHistory { h := new(directoryHistory) + h.dirName = dirName h.signKey = signKey h.snapshots = make(map[uint64]*DirSTR) h.latestSTR = str @@ -37,16 +55,17 @@ func newDirectoryHistory(signKey sign.PublicKey, str *DirSTR) *directoryHistory // the first time it observes an STR for that directory. func NewAuditLog() *ConiksAuditLog { l := new(ConiksAuditLog) - l.histories = make(map[string]*directoryHistory) + l.histories = make(map[[crypto.HashSizeByte]byte]*directoryHistory) return l } // IsKnownDirectory checks to see if an entry for the directory -// address addr exists in the audit log l. IsKnownDirectory() does not +// (indexed by the hash of its initial STR dirInitHash) exists +// in the audit log l. IsKnownDirectory() does not // validate the entries themselves. It returns true if an entry exists, // and false otherwise. -func (l *ConiksAuditLog) IsKnownDirectory(addr string) bool { - h := l.histories[addr] +func (l *ConiksAuditLog) IsKnownDirectory(dirInitHash [crypto.HashSizeByte]byte) bool { + h := l.histories[dirInitHash] if h != nil { return true } @@ -70,19 +89,26 @@ func (l *ConiksAuditLog) IsKnownDirectory(addr string) bool { func (l *ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, oldSTRs map[uint64]*DirSTR, latestSTR *DirSTR) error { - // error if we want to create a new entry for an addr we already know - if l.IsKnownDirectory(addr) { - return ErrAuditLog - } - // make sure we have oldEpochs unless we're inserting right // at the start of a directory's history if oldSTRs == nil && latestSTR.Epoch > 0 { return ErrMalformedDirectoryMessage } + // compute the hash of the initial STR + dirInitHash := computeInitSTRHash(latestSTR) + if oldSTRs != nil { + dirInitHash = computeInitSTRHash(oldSTRs[0]) + } + + // error if we want to create a new entry for a directory + // we already know + if l.IsKnownDirectory(dirInitHash) { + return ErrAuditLog + } + // create the new directory history - h := newDirectoryHistory(signKey, latestSTR) + h := newDirectoryHistory(addr, signKey, latestSTR) startEp := uint64(0) endEp := latestSTR.Epoch @@ -101,8 +127,7 @@ func (l *ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, // don't forget to add the latest STR h.snapshots[endEp] = latestSTR - - l.histories[addr] = h + l.histories[dirInitHash] = h // FIXME: verify the consistency of each new STR return nil @@ -116,14 +141,14 @@ func (l *ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, // addr prior to its first call and thereby expects that an entry for addr // exists in the audit log l. // FIXME: pass Response message as param -func (l *ConiksAuditLog) Update(addr string, newSTR *DirSTR) error { +func (l *ConiksAuditLog) Update(dirInitHash [crypto.HashSizeByte]byte, newSTR *DirSTR) error { // error if we want to update the entry for an addr we don't know - if !l.IsKnownDirectory(addr) { + if !l.IsKnownDirectory(dirInitHash) { return ErrAuditLog } - h := l.histories[addr] + h := l.histories[dirInitHash] if err := verifySTRConsistency(h.signKey, h.latestSTR, newSTR); err != nil { return err @@ -156,20 +181,15 @@ func (l *ConiksAuditLog) Update(addr string, newSTR *DirSTR) error { func (l *ConiksAuditLog) GetObservedSTRs(req *AuditingRequest) (*Response, ErrorCode) { - // make sure the request is well-formed - if len(req.DirectoryAddr) <= 0 || req.StartEpoch > req.EndEpoch { - return NewErrorResponse(ErrMalformedClientMessage), - ErrMalformedClientMessage - } - - h := l.histories[req.DirectoryAddr] - - if h == nil { + // make sure we have a history for the requested directory in the log + if !l.IsKnownDirectory(req.DirInitSTRHash) { return NewErrorResponse(ReqUnknownDirectory), ReqUnknownDirectory } - // also make sure the epoch range is well-formed - if req.StartEpoch > h.latestSTR.Epoch || req.EndEpoch > h.latestSTR.Epoch { + h := l.histories[req.DirInitSTRHash] + + // make sure the request is well-formed + if req.EndEpoch > h.latestSTR.Epoch || req.StartEpoch > req.EndEpoch { return NewErrorResponse(ErrMalformedClientMessage), ErrMalformedClientMessage } diff --git a/protocol/auditlog_test.go b/protocol/auditlog_test.go index f24f0c3..4b3eb13 100644 --- a/protocol/auditlog_test.go +++ b/protocol/auditlog_test.go @@ -1,6 +1,7 @@ package protocol import ( + "github.com/coniks-sys/coniks-go/crypto" "testing" ) @@ -26,8 +27,9 @@ func TestUpdateHistory(t *testing.T) { } // update the directory so we can update the audit log + dirInitHash := computeInitSTRHash(d.LatestSTR()) d.Update() - err = aud.Update("test-server", d.LatestSTR()) + err = aud.Update(dirInitHash, d.LatestSTR()) if err != nil { t.Fatal("Error updating the server history") @@ -81,7 +83,8 @@ func TestUpdateUnknownHistory(t *testing.T) { // let's make sure that we can't re-insert a new server // history into our log - err = aud.Update("unknown", d.LatestSTR()) + var unknown [crypto.HashSizeByte]byte + err = aud.Update(unknown, d.LatestSTR()) if err != ErrAuditLog { t.Fatal("Expected an ErrAuditLog when updating an unknown server history") } @@ -96,10 +99,13 @@ func TestGetLatestObservedSTR(t *testing.T) { t.Fatal("Error inserting new server history") } + // compute the hash of the initial STR for later lookups + dirInitHash := computeInitSTRHash(d.LatestSTR()) + res, err := aud.GetObservedSTRs(&AuditingRequest{ - DirectoryAddr: "test-server", - StartEpoch: uint64(d.LatestSTR().Epoch), - EndEpoch: uint64(d.LatestSTR().Epoch)}) + DirInitSTRHash: dirInitHash, + StartEpoch: uint64(d.LatestSTR().Epoch), + EndEpoch: uint64(d.LatestSTR().Epoch)}) if err != ReqSuccess { t.Fatal("Unable to get latest observed STR") } @@ -125,6 +131,9 @@ func TestGetObservedSTRInEpoch(t *testing.T) { d.Update() } + // compute the hash of the initial STR for later lookups + dirInitHash := computeInitSTRHash(priorSTRs[0]) + // now insert into the log err := aud.Insert("test-server", pk, priorSTRs, d.LatestSTR()) if err != nil { @@ -132,9 +141,9 @@ func TestGetObservedSTRInEpoch(t *testing.T) { } res, err := aud.GetObservedSTRs(&AuditingRequest{ - DirectoryAddr: "test-server", - StartEpoch: uint64(6), - EndEpoch: uint64(8)}) + DirInitSTRHash: dirInitHash, + StartEpoch: uint64(6), + EndEpoch: uint64(8)}) if err != ReqSuccess { t.Fatal("Unable to get latest range of STRs") @@ -170,18 +179,19 @@ func TestGetObservedSTRUnknown(t *testing.T) { t.Fatal("Error inserting new server history with prior STRs") } + var unknown [crypto.HashSizeByte]byte _, err = aud.GetObservedSTRs(&AuditingRequest{ - DirectoryAddr: "unknown", - StartEpoch: uint64(0), - EndEpoch: uint64(d.LatestSTR().Epoch)}) + DirInitSTRHash: unknown, + StartEpoch: uint64(d.LatestSTR().Epoch), + EndEpoch: uint64(d.LatestSTR().Epoch)}) if err != ReqUnknownDirectory { t.Fatal("Expect ReqUnknownDirectory for latest STR") } _, err = aud.GetObservedSTRs(&AuditingRequest{ - DirectoryAddr: "unknown", - StartEpoch: uint64(6), - EndEpoch: uint64(8)}) + DirInitSTRHash: unknown, + StartEpoch: uint64(6), + EndEpoch: uint64(8)}) if err != ReqUnknownDirectory { t.Fatal("Expect ReqUnknownDirectory for older STR") } @@ -200,47 +210,27 @@ func TestGetObservedSTRMalformed(t *testing.T) { d.Update() } + // compute the hash of the initial STR for later lookups + dirInitHash := computeInitSTRHash(priorSTRs[0]) + // now insert into the log err := aud.Insert("test-server", pk, priorSTRs, d.LatestSTR()) if err != nil { t.Fatal("Error inserting new server history with prior STRs") } - _, err = aud.GetObservedSTRs(&AuditingRequest{ - DirectoryAddr: "", - StartEpoch: uint64(0), - EndEpoch: uint64(d.LatestSTR().Epoch)}) - if err != ErrMalformedClientMessage { - t.Fatal("Expect ErrMalFormedClientMessage for latest STR") - } - - _, err = aud.GetObservedSTRs(&AuditingRequest{ - DirectoryAddr: "", - StartEpoch: uint64(4), - EndEpoch: uint64(6)}) - if err != ErrMalformedClientMessage { - t.Fatal("Expect ErrMalformedClientMessage for older STR") - } - // also test the epoch range _, err = aud.GetObservedSTRs(&AuditingRequest{ - DirectoryAddr: "test-server", - StartEpoch: uint64(6), - EndEpoch: uint64(4)}) + DirInitSTRHash: dirInitHash, + StartEpoch: uint64(6), + EndEpoch: uint64(4)}) if err != ErrMalformedClientMessage { t.Fatal("Expect ErrMalformedClientMessage for bad end epoch") } _, err = aud.GetObservedSTRs(&AuditingRequest{ - DirectoryAddr: "test-server", - StartEpoch: uint64(11), - EndEpoch: uint64(11)}) - if err != ErrMalformedClientMessage { - t.Fatal("Expect ErrMalformedClientMessage for bad start epoch") - } - _, err = aud.GetObservedSTRs(&AuditingRequest{ - DirectoryAddr: "test-server", - StartEpoch: uint64(6), - EndEpoch: uint64(11)}) + DirInitSTRHash: dirInitHash, + StartEpoch: uint64(6), + EndEpoch: uint64(11)}) if err != ErrMalformedClientMessage { t.Fatal("Expect ErrMalformedClientMessage for out-of-bounds epoch range") } diff --git a/protocol/message.go b/protocol/message.go index 1b5310f..bd3d58c 100644 --- a/protocol/message.go +++ b/protocol/message.go @@ -4,7 +4,10 @@ package protocol -import m "github.com/coniks-sys/coniks-go/merkletree" +import ( + "github.com/coniks-sys/coniks-go/crypto" + m "github.com/coniks-sys/coniks-go/merkletree" +) // The types of requests CONIKS clients send during the CONIKS protocols. const ( @@ -100,9 +103,9 @@ type MonitoringRequest struct { // The response to a successful request is an STRHistoryRange with // a list of STRs covering the epoch range [StartEpoch, EndEpoch]. type AuditingRequest struct { - DirectoryAddr string - StartEpoch uint64 - EndEpoch uint64 + DirInitSTRHash [crypto.HashSizeByte]byte + StartEpoch uint64 + EndEpoch uint64 } // A Response message indicates the result of a CONIKS client request From 9f212b16c142cfda6a6151b71323860443c6d441 Mon Sep 17 00:00:00 2001 From: Marcela M Date: Mon, 3 Jul 2017 19:07:39 -0400 Subject: [PATCH 27/43] Insert latest STR into snapshot map right away --- protocol/auditlog.go | 86 +++++++++--------- protocol/auditlog_test.go | 185 +++++++++++++++----------------------- protocol/testutil.go | 26 ++++++ 3 files changed, 142 insertions(+), 155 deletions(-) diff --git a/protocol/auditlog.go b/protocol/auditlog.go index d1d8ba1..8c8b3c7 100644 --- a/protocol/auditlog.go +++ b/protocol/auditlog.go @@ -25,9 +25,7 @@ type directoryHistory struct { // public signing key enabling the auditor to verify the corresponding // signed tree roots, and a list with all observed snapshots in // chronological order. -type ConiksAuditLog struct { - histories map[[crypto.HashSizeByte]byte]*directoryHistory -} +type ConiksAuditLog map[[crypto.HashSizeByte]byte]*directoryHistory // computeInitSTRHash is a wrapper for the digest function; // returns nil if the STR isn't an initial STR (i.e. str.Epoch != 0) @@ -35,27 +33,27 @@ func computeInitSTRHash(initSTR *DirSTR) [crypto.HashSizeByte]byte { var initSTRHash [crypto.HashSizeByte]byte if initSTR.Epoch == 0 { - // ugh HACK: need to call copy to convert slice into the array + // ugh HACK: need to call copy to convert slice + // into the array copy(initSTRHash[:], crypto.Digest(initSTR.Serialize())) } return initSTRHash } -func newDirectoryHistory(dirName string, signKey sign.PublicKey, str *DirSTR) *directoryHistory { +func newDirectoryHistory(dirName string, signKey sign.PublicKey) *directoryHistory { h := new(directoryHistory) h.dirName = dirName h.signKey = signKey h.snapshots = make(map[uint64]*DirSTR) - h.latestSTR = str + h.latestSTR = nil return h } // NewAuditLog constructs a new ConiksAuditLog. It creates an empty // 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 := new(ConiksAuditLog) - l.histories = make(map[[crypto.HashSizeByte]byte]*directoryHistory) +func NewAuditLog() ConiksAuditLog { + l := make(map[[crypto.HashSizeByte]byte]*directoryHistory) return l } @@ -64,20 +62,26 @@ func NewAuditLog() *ConiksAuditLog { // in the audit log l. IsKnownDirectory() does not // validate the entries themselves. It returns true if an entry exists, // and false otherwise. -func (l *ConiksAuditLog) IsKnownDirectory(dirInitHash [crypto.HashSizeByte]byte) bool { - h := l.histories[dirInitHash] +func (l ConiksAuditLog) IsKnownDirectory(dirInitHash [crypto.HashSizeByte]byte) bool { + h := l[dirInitHash] if h != nil { return true } return false } +// updateLatestSTR inserts a new STR into a directory history; +// assumes the STR has been validated by the caller +func (h *directoryHistory) updateLatestSTR(newLatest *DirSTR) { + h.snapshots[newLatest.Epoch] = newLatest + h.latestSTR = newLatest +} + // Insert creates a new directory history for the key directory addr // and inserts it into the audit log l. // The directory history is initialized with the key directory's -// signing key signKey, a list of STRs representing the -// directory's prior history oldSTRs, and the directory's latest STR -// latestSTR. +// signing key signKey, and a list of STRs representing the +// directory's STR history so far (as a map of epochs to STRs). // Insert() returns an ErrAuditLog if the auditor attempts to create // a new history for a known directory, an ErrMalformedDirectoryMessage // if oldSTRs is malformed, and nil otherwise. @@ -86,20 +90,16 @@ func (l *ConiksAuditLog) IsKnownDirectory(dirInitHash [crypto.HashSizeByte]byte) // 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 -func (l *ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, - oldSTRs map[uint64]*DirSTR, latestSTR *DirSTR) error { +func (l ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, + hist map[uint64]*DirSTR) error { - // make sure we have oldEpochs unless we're inserting right - // at the start of a directory's history - if oldSTRs == nil && latestSTR.Epoch > 0 { + // make sure we're getting an initial STR at the very least + if len(hist) < 1 && hist[0].Epoch != 0 { return ErrMalformedDirectoryMessage } // compute the hash of the initial STR - dirInitHash := computeInitSTRHash(latestSTR) - if oldSTRs != nil { - dirInitHash = computeInitSTRHash(oldSTRs[0]) - } + dirInitHash := computeInitSTRHash(hist[0]) // error if we want to create a new entry for a directory // we already know @@ -108,28 +108,30 @@ func (l *ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, } // create the new directory history - h := newDirectoryHistory(addr, signKey, latestSTR) + h := newDirectoryHistory(addr, signKey) startEp := uint64(0) - endEp := latestSTR.Epoch + endEp := uint64(len(hist)) - // add each old STR into the history - // This loop automatically catches if oldSTRs is malformed - // (i.e. oldSTRs starts at epoch 0 and - // len(oldSTRs) != latestSTR.Epoch-1) + // add each STR into the history + // This loop automatically catches if hist is malformed + // (i.e. hist is missing and epoch) + // FIXME: verify the consistency of each new STR before inserting + // into the audit log for ep := startEp; ep < endEp; ep++ { - str := oldSTRs[ep] + str := hist[ep] if str == nil { return ErrMalformedDirectoryMessage } h.snapshots[ep] = str } - // don't forget to add the latest STR - h.snapshots[endEp] = latestSTR - l.histories[dirInitHash] = h + // Make sure to update the latestSTR + // 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 - // FIXME: verify the consistency of each new STR return nil } @@ -141,22 +143,21 @@ func (l *ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, // addr prior to its first call and thereby expects that an entry for addr // exists in the audit log l. // FIXME: pass Response message as param -func (l *ConiksAuditLog) Update(dirInitHash [crypto.HashSizeByte]byte, newSTR *DirSTR) error { +func (l ConiksAuditLog) Update(dirInitHash [crypto.HashSizeByte]byte, newSTR *DirSTR) error { // error if we want to update the entry for an addr we don't know if !l.IsKnownDirectory(dirInitHash) { return ErrAuditLog } - h := l.histories[dirInitHash] + h := l[dirInitHash] if err := verifySTRConsistency(h.signKey, h.latestSTR, newSTR); err != nil { return err } // update the latest STR - h.snapshots[h.latestSTR.Epoch] = h.latestSTR - h.latestSTR = newSTR + h.updateLatestSTR(newSTR) return nil } @@ -178,7 +179,7 @@ func (l *ConiksAuditLog) Update(dirInitHash [crypto.HashSizeByte]byte, newSTR *D // If the auditor doesn't have any history entries for the requested CONIKS // directory, GetObservedSTRs() returns a // message.NewErrorResponse(ReqUnknownDirectory) tuple. -func (l *ConiksAuditLog) GetObservedSTRs(req *AuditingRequest) (*Response, +func (l ConiksAuditLog) GetObservedSTRs(req *AuditingRequest) (*Response, ErrorCode) { // make sure we have a history for the requested directory in the log @@ -186,7 +187,7 @@ func (l *ConiksAuditLog) GetObservedSTRs(req *AuditingRequest) (*Response, return NewErrorResponse(ReqUnknownDirectory), ReqUnknownDirectory } - h := l.histories[req.DirInitSTRHash] + h := l[req.DirInitSTRHash] // make sure the request is well-formed if req.EndEpoch > h.latestSTR.Epoch || req.StartEpoch > req.EndEpoch { @@ -195,13 +196,10 @@ func (l *ConiksAuditLog) GetObservedSTRs(req *AuditingRequest) (*Response, } var strs []*DirSTR - for ep := req.StartEpoch; ep < req.EndEpoch; ep++ { + for ep := req.StartEpoch; ep <= req.EndEpoch; ep++ { str := h.snapshots[ep] strs = append(strs, str) } - // don't forget to append the STR for EndEpoch - strs = append(strs, h.snapshots[req.EndEpoch]) - return NewSTRHistoryRange(strs) } diff --git a/protocol/auditlog_test.go b/protocol/auditlog_test.go index 4b3eb13..a59774b 100644 --- a/protocol/auditlog_test.go +++ b/protocol/auditlog_test.go @@ -6,30 +6,18 @@ import ( ) func TestInsertEmptyHistory(t *testing.T) { - // let's just create basic test directory and an empty audit log - d, pk := NewTestDirectory(t, true) - aud := NewAuditLog() - - err := aud.Insert("test-server", pk, nil, d.LatestSTR()) - if err != nil { - t.Fatal("Error inserting new server history") - } + // create basic test directory and audit log with 1 STR + _, _, _ = NewTestAuditLog(t, 0) } func TestUpdateHistory(t *testing.T) { - // let's just create basic test directory and an empty audit log - d, pk := NewTestDirectory(t, true) - aud := NewAuditLog() - - err := aud.Insert("test-server", pk, nil, d.LatestSTR()) - if err != nil { - t.Fatal("Error inserting new server history") - } + // create basic test directory and audit log with 1 STR + d, aud, hist := NewTestAuditLog(t, 0) // update the directory so we can update the audit log - dirInitHash := computeInitSTRHash(d.LatestSTR()) + dirInitHash := computeInitSTRHash(hist[0]) d.Update() - err = aud.Update(dirInitHash, d.LatestSTR()) + err := aud.Update(dirInitHash, d.LatestSTR()) if err != nil { t.Fatal("Error updating the server history") @@ -37,70 +25,59 @@ func TestUpdateHistory(t *testing.T) { } func TestInsertPriorHistory(t *testing.T) { - // let's just create basic test directory and an empty audit log - d, pk := NewTestDirectory(t, true) - aud := NewAuditLog() - - // create 10 epochs - priorSTRs := make(map[uint64]*DirSTR) - for i := 0; i < 10; i++ { - priorSTRs[d.LatestSTR().Epoch] = d.LatestSTR() - d.Update() - } - - // now insert - err := aud.Insert("test-server", pk, priorSTRs, d.LatestSTR()) - if err != nil { - t.Fatal("Error inserting new server history with prior STRs") - } + // create basic test directory and audit log with 11 STRs + _, _, _ = NewTestAuditLog(t, 10) } func TestInsertExistingHistory(t *testing.T) { - // let's just create basic test directory and an empty audit log - d, pk := NewTestDirectory(t, true) - aud := NewAuditLog() - err := aud.Insert("test-server", pk, nil, d.LatestSTR()) - if err != nil { - t.Fatal("Error inserting new server history") - } + // create basic test directory and audit log with 1 STR + _, aud, hist := NewTestAuditLog(t, 0) // let's make sure that we can't re-insert a new server // history into our log - err = aud.Insert("test-server", pk, nil, d.LatestSTR()) + err := aud.Insert("test-server", nil, hist) if err != ErrAuditLog { t.Fatal("Expected an ErrAuditLog when inserting an existing server history") } } func TestUpdateUnknownHistory(t *testing.T) { - // let's just create basic test directory and an empty audit log - d, pk := NewTestDirectory(t, true) - aud := NewAuditLog() - err := aud.Insert("test-server", pk, nil, d.LatestSTR()) - if err != nil { - t.Fatal("Error inserting new server history") - } + // create basic test directory and audit log with 1 STR + d, aud, _ := NewTestAuditLog(t, 0) - // let's make sure that we can't re-insert a new server - // history into our log + // let's make sure that we can't update a history for an unknown + // directory in our log var unknown [crypto.HashSizeByte]byte - err = aud.Update(unknown, d.LatestSTR()) + err := aud.Update(unknown, d.LatestSTR()) if err != ErrAuditLog { t.Fatal("Expected an ErrAuditLog when updating an unknown server history") } } -func TestGetLatestObservedSTR(t *testing.T) { - // let's just create basic test directory and an empty audit log - d, pk := NewTestDirectory(t, true) - aud := NewAuditLog() - err := aud.Insert("test-server", pk, nil, d.LatestSTR()) - if err != nil { - t.Fatal("Error inserting new server history") +func TestUpdateBadNewSTR(t *testing.T) { + // create basic test directory and audit log with 11 STRs + d, aud, hist := NewTestAuditLog(t, 10) + + // compute the hash of the initial STR for later lookups + dirInitHash := computeInitSTRHash(hist[0]) + + // update the directory a few more times and then try + // to update + d.Update() + d.Update() + + err := aud.Update(dirInitHash, d.LatestSTR()) + if err != CheckBadSTR { + t.Fatal("Expected a CheckBadSTR when attempting update a server history with a bad STR") } +} + +func TestGetLatestObservedSTR(t *testing.T) { + // create basic test directory and audit log with 1 STR + d, aud, hist := NewTestAuditLog(t, 0) // compute the hash of the initial STR for later lookups - dirInitHash := computeInitSTRHash(d.LatestSTR()) + dirInitHash := computeInitSTRHash(hist[0]) res, err := aud.GetObservedSTRs(&AuditingRequest{ DirInitSTRHash: dirInitHash, @@ -111,34 +88,20 @@ func TestGetLatestObservedSTR(t *testing.T) { } obs := res.DirectoryResponse.(*STRHistoryRange) - if len(obs.STR) != 1 { + if len(obs.STR) == 0 { t.Fatal("Expect returned STR to be not nil") } if obs.STR[0].Epoch != d.LatestSTR().Epoch { - t.Fatal("Unexpected epoch for returned STR") + t.Fatal("Unexpected epoch for returned latest STR") } } func TestGetObservedSTRInEpoch(t *testing.T) { - // let's just create basic test directory and an empty audit log - d, pk := NewTestDirectory(t, true) - aud := NewAuditLog() - - // create 10 epochs - priorSTRs := make(map[uint64]*DirSTR) - for i := 0; i < 10; i++ { - priorSTRs[d.LatestSTR().Epoch] = d.LatestSTR() - d.Update() - } + // create basic test directory and audit log with 11 STRs + _, aud, hist := NewTestAuditLog(t, 10) // compute the hash of the initial STR for later lookups - dirInitHash := computeInitSTRHash(priorSTRs[0]) - - // now insert into the log - err := aud.Insert("test-server", pk, priorSTRs, d.LatestSTR()) - if err != nil { - t.Fatal("Error inserting new server history with prior STRs") - } + dirInitHash := computeInitSTRHash(hist[0]) res, err := aud.GetObservedSTRs(&AuditingRequest{ DirInitSTRHash: dirInitHash, @@ -161,26 +124,40 @@ func TestGetObservedSTRInEpoch(t *testing.T) { } } -func TestGetObservedSTRUnknown(t *testing.T) { - // let's just create basic test directory and an empty audit log - d, pk := NewTestDirectory(t, true) - aud := NewAuditLog() +func TestGetEntireObservedSTRHist(t *testing.T) { + // create basic test directory and audit log with 2 STRs + d, aud, hist := NewTestAuditLog(t, 1) - // create 10 epochs - priorSTRs := make(map[uint64]*DirSTR) - for i := 0; i < 10; i++ { - priorSTRs[d.LatestSTR().Epoch] = d.LatestSTR() - d.Update() + // compute the hash of the initial STR for later lookups + dirInitHash := computeInitSTRHash(hist[0]) + + res, err := aud.GetObservedSTRs(&AuditingRequest{ + DirInitSTRHash: dirInitHash, + StartEpoch: uint64(0), + EndEpoch: d.LatestSTR().Epoch}) + + if err != ReqSuccess { + t.Fatal("Unable to get latest range of STRs") } - // now insert into the log - err := aud.Insert("test-server", pk, priorSTRs, d.LatestSTR()) - if err != nil { - t.Fatal("Error inserting new server history with prior STRs") + obs := res.DirectoryResponse.(*STRHistoryRange) + if len(obs.STR) != 2 { + t.Fatal("Unexpected number of returned STRs") + } + if obs.STR[0].Epoch != 0 { + t.Fatal("Unexpected initial epoch for returned STR range") + } + if obs.STR[1].Epoch != d.LatestSTR().Epoch { + t.Fatal("Unexpected latest STR epoch for returned STR") } +} + +func TestGetObservedSTRUnknown(t *testing.T) { + // create basic test directory and audit log with 11 STRs + d, aud, _ := NewTestAuditLog(t, 10) var unknown [crypto.HashSizeByte]byte - _, err = aud.GetObservedSTRs(&AuditingRequest{ + _, err := aud.GetObservedSTRs(&AuditingRequest{ DirInitSTRHash: unknown, StartEpoch: uint64(d.LatestSTR().Epoch), EndEpoch: uint64(d.LatestSTR().Epoch)}) @@ -199,28 +176,14 @@ func TestGetObservedSTRUnknown(t *testing.T) { } func TestGetObservedSTRMalformed(t *testing.T) { - // let's just create basic test directory and an empty audit log - d, pk := NewTestDirectory(t, true) - aud := NewAuditLog() - - // create 10 epochs - priorSTRs := make(map[uint64]*DirSTR) - for i := 0; i < 10; i++ { - priorSTRs[d.LatestSTR().Epoch] = d.LatestSTR() - d.Update() - } + // create basic test directory and audit log with 11 STRs + _, aud, hist := NewTestAuditLog(t, 10) // compute the hash of the initial STR for later lookups - dirInitHash := computeInitSTRHash(priorSTRs[0]) - - // now insert into the log - err := aud.Insert("test-server", pk, priorSTRs, d.LatestSTR()) - if err != nil { - t.Fatal("Error inserting new server history with prior STRs") - } + dirInitHash := computeInitSTRHash(hist[0]) // also test the epoch range - _, err = aud.GetObservedSTRs(&AuditingRequest{ + _, err := aud.GetObservedSTRs(&AuditingRequest{ DirInitSTRHash: dirInitHash, StartEpoch: uint64(6), EndEpoch: uint64(4)}) diff --git a/protocol/testutil.go b/protocol/testutil.go index 4e464ba..ee2bbb4 100644 --- a/protocol/testutil.go +++ b/protocol/testutil.go @@ -28,3 +28,29 @@ func NewTestDirectory(t *testing.T, useTBs bool) ( d := NewDirectory(1, vrfKey, signKey, 10, useTBs) return d, pk } + +// NewTestAuditLog creates a ConiksAuditLog and corresponding +// ConiksDirectory used for testing auditor-side CONIKS operations. +// The new audit log can be initialized with the number of epochs +// indicating the length of the directory history with which to +// initialize the log; if numEpochs > 0, the history contains numEpochs+1 +// STRs as it always includes the STR after the last directory update +func NewTestAuditLog(t *testing.T, numEpochs int) (*ConiksDirectory, ConiksAuditLog, map[uint64]*DirSTR) { + d, pk := NewTestDirectory(t, true) + aud := NewAuditLog() + + hist := make(map[uint64]*DirSTR) + for ep := 0; ep < numEpochs; ep++ { + hist[d.LatestSTR().Epoch] = d.LatestSTR() + d.Update() + } + // always include the actual latest STR + hist[d.LatestSTR().Epoch] = d.LatestSTR() + + err := aud.Insert("test-server", pk, hist) + if err != nil { + t.Fatal("Error inserting a new history with %d epochs", numEpochs) + } + + return d, aud, hist +} From fcebf89070432132456ac8b23f9fe09ae5c9d69c Mon Sep 17 00:00:00 2001 From: Marcela Melara Date: Mon, 3 Jul 2017 21:22:10 -0400 Subject: [PATCH 28/43] Fix go vet error --- protocol/testutil.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/testutil.go b/protocol/testutil.go index ee2bbb4..0f41e4f 100644 --- a/protocol/testutil.go +++ b/protocol/testutil.go @@ -49,7 +49,7 @@ func NewTestAuditLog(t *testing.T, numEpochs int) (*ConiksDirectory, ConiksAudit err := aud.Insert("test-server", pk, hist) if err != nil { - t.Fatal("Error inserting a new history with %d epochs", numEpochs) + t.Fatalf("Error inserting a new history with %d STRs", numEpochs+1) } return d, aud, hist From 7e540875d4ce60f00d18a8439baf83becb9fcf03 Mon Sep 17 00:00:00 2001 From: Marcela Melara Date: Mon, 3 Jul 2017 23:02:07 -0400 Subject: [PATCH 29/43] Change audit log index from byte array to string --- protocol/auditlog.go | 77 ++++++++++++++++++++++----------------- protocol/auditlog_test.go | 13 ++----- protocol/message.go | 20 +++++----- 3 files changed, 56 insertions(+), 54 deletions(-) diff --git a/protocol/auditlog.go b/protocol/auditlog.go index 8c8b3c7..fa7c17e 100644 --- a/protocol/auditlog.go +++ b/protocol/auditlog.go @@ -6,6 +6,8 @@ package protocol import ( + "encoding/hex" + "github.com/coniks-sys/coniks-go/crypto" "github.com/coniks-sys/coniks-go/crypto/sign" ) @@ -20,32 +22,36 @@ type directoryHistory struct { // A ConiksAuditLog maintains the histories // of all CONIKS directories known to a CONIKS auditor, // indexing the histories by the hash of a directory's initial -// STR. +// STR (specifically, the hash of the STR's signature as a string). // Each history includes the directory's domain name as a string, its // public signing key enabling the auditor to verify the corresponding -// signed tree roots, and a list with all observed snapshots in -// chronological order. -type ConiksAuditLog map[[crypto.HashSizeByte]byte]*directoryHistory +// signed tree roots, and a map with the snapshots for each observed +// epoch. +type ConiksAuditLog map[string]*directoryHistory // computeInitSTRHash is a wrapper for the digest function; -// returns nil if the STR isn't an initial STR (i.e. str.Epoch != 0) -func computeInitSTRHash(initSTR *DirSTR) [crypto.HashSizeByte]byte { - var initSTRHash [crypto.HashSizeByte]byte - - if initSTR.Epoch == 0 { - // ugh HACK: need to call copy to convert slice - // into the array - copy(initSTRHash[:], crypto.Digest(initSTR.Serialize())) +// 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 { + return "" } - return initSTRHash + return hex.EncodeToString(crypto.Digest(initSTR.Signature)) } -func newDirectoryHistory(dirName string, signKey sign.PublicKey) *directoryHistory { +// updateLatestSTR inserts a new STR into a directory history; +// assumes the STR has been validated by the caller +func (h *directoryHistory) updateLatestSTR(newLatest *DirSTR) { + h.snapshots[newLatest.Epoch] = newLatest + h.latestSTR = newLatest +} + +// caller validates that initSTR is for epoch 0 +func newDirectoryHistory(dirName string, signKey sign.PublicKey, initSTR *DirSTR) *directoryHistory { h := new(directoryHistory) h.dirName = dirName h.signKey = signKey h.snapshots = make(map[uint64]*DirSTR) - h.latestSTR = nil + h.updateLatestSTR(initSTR) return h } @@ -53,7 +59,7 @@ func newDirectoryHistory(dirName string, signKey sign.PublicKey) *directoryHisto // 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[[crypto.HashSizeByte]byte]*directoryHistory) + l := make(map[string]*directoryHistory) return l } @@ -62,7 +68,7 @@ func NewAuditLog() ConiksAuditLog { // in the audit log l. IsKnownDirectory() does not // validate the entries themselves. It returns true if an entry exists, // and false otherwise. -func (l ConiksAuditLog) IsKnownDirectory(dirInitHash [crypto.HashSizeByte]byte) bool { +func (l ConiksAuditLog) IsKnownDirectory(dirInitHash string) bool { h := l[dirInitHash] if h != nil { return true @@ -70,21 +76,16 @@ func (l ConiksAuditLog) IsKnownDirectory(dirInitHash [crypto.HashSizeByte]byte) return false } -// updateLatestSTR inserts a new STR into a directory history; -// assumes the STR has been validated by the caller -func (h *directoryHistory) updateLatestSTR(newLatest *DirSTR) { - h.snapshots[newLatest.Epoch] = newLatest - h.latestSTR = newLatest -} - -// Insert creates a new directory history for the key directory addr -// and inserts it into the audit log l. +// Insert creates a new directory history for the key directory addr, +// verifies the consistency of the STR history so far, and inserts it +// into the audit log l if the checks pass. // The directory history is initialized with the key directory's // signing key signKey, and a list of STRs representing the // directory's STR history so far (as a map of epochs to STRs). // Insert() returns an ErrAuditLog if the auditor attempts to create // a new history for a known directory, an ErrMalformedDirectoryMessage -// if oldSTRs is malformed, and nil otherwise. +// if oldSTRs is malformed, a CheckBadSignature or CheckBadSTR if there +// is an inconsistency in the history given in hist, and nil otherwise. // Insert() only creates the initial entry in the log for addr. Use Update() // to insert newly observed STRs for addr in subsequent epochs. // FIXME: pass Response message as param @@ -108,21 +109,29 @@ func (l ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, } // create the new directory history - h := newDirectoryHistory(addr, signKey) + h := newDirectoryHistory(addr, signKey, hist[0]) - startEp := uint64(0) + // add each STR into the history + // start at 1 since we've inserted the initial STR above + startEp := uint64(1) endEp := uint64(len(hist)) - // add each STR into the history // This loop automatically catches if hist is malformed - // (i.e. hist is missing and epoch) - // FIXME: verify the consistency of each new STR before inserting - // into the audit log + // (i.e. hist is missing an epoch between 0 and the latest given) for ep := startEp; ep < endEp; ep++ { str := hist[ep] if str == nil { return ErrMalformedDirectoryMessage } + + // verify the consistency of each new STR before inserting + // into the audit log + err := verifySTRConsistency(signKey, h.snapshots[ep-1], str) + + if err != nil { + return err + } + h.snapshots[ep] = str } @@ -143,7 +152,7 @@ func (l ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, // addr prior to its first call and thereby expects that an entry for addr // exists in the audit log l. // FIXME: pass Response message as param -func (l ConiksAuditLog) Update(dirInitHash [crypto.HashSizeByte]byte, newSTR *DirSTR) error { +func (l ConiksAuditLog) Update(dirInitHash string, newSTR *DirSTR) error { // error if we want to update the entry for an addr we don't know if !l.IsKnownDirectory(dirInitHash) { diff --git a/protocol/auditlog_test.go b/protocol/auditlog_test.go index a59774b..ccb58b9 100644 --- a/protocol/auditlog_test.go +++ b/protocol/auditlog_test.go @@ -1,9 +1,6 @@ package protocol -import ( - "github.com/coniks-sys/coniks-go/crypto" - "testing" -) +import "testing" func TestInsertEmptyHistory(t *testing.T) { // create basic test directory and audit log with 1 STR @@ -47,8 +44,7 @@ func TestUpdateUnknownHistory(t *testing.T) { // let's make sure that we can't update a history for an unknown // directory in our log - var unknown [crypto.HashSizeByte]byte - err := aud.Update(unknown, d.LatestSTR()) + err := aud.Update("unknown", d.LatestSTR()) if err != ErrAuditLog { t.Fatal("Expected an ErrAuditLog when updating an unknown server history") } @@ -156,9 +152,8 @@ func TestGetObservedSTRUnknown(t *testing.T) { // create basic test directory and audit log with 11 STRs d, aud, _ := NewTestAuditLog(t, 10) - var unknown [crypto.HashSizeByte]byte _, err := aud.GetObservedSTRs(&AuditingRequest{ - DirInitSTRHash: unknown, + DirInitSTRHash: "unknown", StartEpoch: uint64(d.LatestSTR().Epoch), EndEpoch: uint64(d.LatestSTR().Epoch)}) if err != ReqUnknownDirectory { @@ -166,7 +161,7 @@ func TestGetObservedSTRUnknown(t *testing.T) { } _, err = aud.GetObservedSTRs(&AuditingRequest{ - DirInitSTRHash: unknown, + DirInitSTRHash: "unknown", StartEpoch: uint64(6), EndEpoch: uint64(8)}) if err != ReqUnknownDirectory { diff --git a/protocol/message.go b/protocol/message.go index bd3d58c..20056b6 100644 --- a/protocol/message.go +++ b/protocol/message.go @@ -4,10 +4,7 @@ package protocol -import ( - "github.com/coniks-sys/coniks-go/crypto" - m "github.com/coniks-sys/coniks-go/merkletree" -) +import m "github.com/coniks-sys/coniks-go/merkletree" // The types of requests CONIKS clients send during the CONIKS protocols. const ( @@ -94,16 +91,17 @@ type MonitoringRequest struct { EndEpoch uint64 } -// An AuditingRequest is a message with a CONIKS key directory's address -// as a string, and a StartEpoch and an EndEpoch as uint64's that a CONIKS -// client sends to a CONIKS auditor to request the given directory's -// STRs for the given epoch range. To obtain a single STR, the client -// must set StartEpoch = EndEpoch in the request. +// An AuditingRequest is a message with a CONIKS key directory's initial +// STR hash as a string, and a StartEpoch and an EndEpoch as uint64's +// that a CONIKS client sends to a CONIKS auditor to request the given +// directory's STRs for the given epoch range. To obtain a single STR, +// the client must set StartEpoch = EndEpoch in the request. // // The response to a successful request is an STRHistoryRange with -// a list of STRs covering the epoch range [StartEpoch, EndEpoch]. +// a list of STRs covering the epoch range [StartEpoch, EndEpoch], +// inclusively. type AuditingRequest struct { - DirInitSTRHash [crypto.HashSizeByte]byte + DirInitSTRHash string StartEpoch uint64 EndEpoch uint64 } From 699858a1d88f9c3aa95dde0c34a1dab41daedb3f Mon Sep 17 00:00:00 2001 From: Marcela Melara Date: Mon, 3 Jul 2017 23:19:02 -0400 Subject: [PATCH 30/43] Add test case for getting an STR after an audit log update --- protocol/auditlog_test.go | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/protocol/auditlog_test.go b/protocol/auditlog_test.go index ccb58b9..50f9fa3 100644 --- a/protocol/auditlog_test.go +++ b/protocol/auditlog_test.go @@ -120,13 +120,14 @@ func TestGetObservedSTRInEpoch(t *testing.T) { } } -func TestGetEntireObservedSTRHist(t *testing.T) { +func TestGetObservedSTRMultipleEpochs(t *testing.T) { // create basic test directory and audit log with 2 STRs d, aud, hist := NewTestAuditLog(t, 1) // compute the hash of the initial STR for later lookups dirInitHash := computeInitSTRHash(hist[0]) + // first AuditingRequest res, err := aud.GetObservedSTRs(&AuditingRequest{ DirInitSTRHash: dirInitHash, StartEpoch: uint64(0), @@ -146,6 +147,32 @@ func TestGetEntireObservedSTRHist(t *testing.T) { if obs.STR[1].Epoch != d.LatestSTR().Epoch { t.Fatal("Unexpected latest STR epoch for returned STR") } + + // go to next epoch + d.Update() + err1 := aud.Update(dirInitHash, d.LatestSTR()) + if err1 != nil { + t.Fatal("Error occurred updating audit log after auditing request") + } + + // request the new latest STR + res, err = aud.GetObservedSTRs(&AuditingRequest{ + DirInitSTRHash: dirInitHash, + StartEpoch: d.LatestSTR().Epoch, + EndEpoch: d.LatestSTR().Epoch}) + + if err != ReqSuccess { + t.Fatal("Unable to get new latest STRs") + } + + obs = res.DirectoryResponse.(*STRHistoryRange) + if len(obs.STR) != 1 { + t.Fatal("Unexpected number of new latest STRs") + } + if obs.STR[0].Epoch != d.LatestSTR().Epoch { + t.Fatal("Unexpected new latest STR epoch") + } + } func TestGetObservedSTRUnknown(t *testing.T) { From 92cee27f21ab8299483b4d43712cfbd05c8308a6 Mon Sep 17 00:00:00 2001 From: Vu Quoc Huy Date: Wed, 5 Jul 2017 12:31:23 +0700 Subject: [PATCH 31/43] Refactor common functions --- protocol/auditlog.go | 20 ++++---------------- protocol/auditlog_test.go | 12 ++++++------ protocol/common.go | 18 ++++++++++++++++++ protocol/common_test.go | 39 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 67 insertions(+), 22 deletions(-) create mode 100644 protocol/common.go create mode 100644 protocol/common_test.go diff --git a/protocol/auditlog.go b/protocol/auditlog.go index fa7c17e..2f0225e 100644 --- a/protocol/auditlog.go +++ b/protocol/auditlog.go @@ -6,14 +6,11 @@ package protocol import ( - "encoding/hex" - - "github.com/coniks-sys/coniks-go/crypto" "github.com/coniks-sys/coniks-go/crypto/sign" ) type directoryHistory struct { - dirName string + name string signKey sign.PublicKey snapshots map[uint64]*DirSTR latestSTR *DirSTR @@ -29,15 +26,6 @@ type directoryHistory struct { // epoch. type ConiksAuditLog map[string]*directoryHistory -// 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 { - return "" - } - return hex.EncodeToString(crypto.Digest(initSTR.Signature)) -} - // updateLatestSTR inserts a new STR into a directory history; // assumes the STR has been validated by the caller func (h *directoryHistory) updateLatestSTR(newLatest *DirSTR) { @@ -46,9 +34,9 @@ func (h *directoryHistory) updateLatestSTR(newLatest *DirSTR) { } // caller validates that initSTR is for epoch 0 -func newDirectoryHistory(dirName string, signKey sign.PublicKey, initSTR *DirSTR) *directoryHistory { +func newDirectoryHistory(name string, signKey sign.PublicKey, initSTR *DirSTR) *directoryHistory { h := new(directoryHistory) - h.dirName = dirName + h.name = name h.signKey = signKey h.snapshots = make(map[uint64]*DirSTR) h.updateLatestSTR(initSTR) @@ -100,7 +88,7 @@ func (l ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, } // compute the hash of the initial STR - dirInitHash := computeInitSTRHash(hist[0]) + dirInitHash := ComputeDirectoryIdentity(hist[0]) // error if we want to create a new entry for a directory // we already know diff --git a/protocol/auditlog_test.go b/protocol/auditlog_test.go index 50f9fa3..b242957 100644 --- a/protocol/auditlog_test.go +++ b/protocol/auditlog_test.go @@ -12,7 +12,7 @@ func TestUpdateHistory(t *testing.T) { d, aud, hist := NewTestAuditLog(t, 0) // update the directory so we can update the audit log - dirInitHash := computeInitSTRHash(hist[0]) + dirInitHash := ComputeDirectoryIdentity(hist[0]) d.Update() err := aud.Update(dirInitHash, d.LatestSTR()) @@ -55,7 +55,7 @@ func TestUpdateBadNewSTR(t *testing.T) { d, aud, hist := NewTestAuditLog(t, 10) // compute the hash of the initial STR for later lookups - dirInitHash := computeInitSTRHash(hist[0]) + dirInitHash := ComputeDirectoryIdentity(hist[0]) // update the directory a few more times and then try // to update @@ -73,7 +73,7 @@ func TestGetLatestObservedSTR(t *testing.T) { d, aud, hist := NewTestAuditLog(t, 0) // compute the hash of the initial STR for later lookups - dirInitHash := computeInitSTRHash(hist[0]) + dirInitHash := ComputeDirectoryIdentity(hist[0]) res, err := aud.GetObservedSTRs(&AuditingRequest{ DirInitSTRHash: dirInitHash, @@ -97,7 +97,7 @@ func TestGetObservedSTRInEpoch(t *testing.T) { _, aud, hist := NewTestAuditLog(t, 10) // compute the hash of the initial STR for later lookups - dirInitHash := computeInitSTRHash(hist[0]) + dirInitHash := ComputeDirectoryIdentity(hist[0]) res, err := aud.GetObservedSTRs(&AuditingRequest{ DirInitSTRHash: dirInitHash, @@ -125,7 +125,7 @@ func TestGetObservedSTRMultipleEpochs(t *testing.T) { d, aud, hist := NewTestAuditLog(t, 1) // compute the hash of the initial STR for later lookups - dirInitHash := computeInitSTRHash(hist[0]) + dirInitHash := ComputeDirectoryIdentity(hist[0]) // first AuditingRequest res, err := aud.GetObservedSTRs(&AuditingRequest{ @@ -202,7 +202,7 @@ func TestGetObservedSTRMalformed(t *testing.T) { _, aud, hist := NewTestAuditLog(t, 10) // compute the hash of the initial STR for later lookups - dirInitHash := computeInitSTRHash(hist[0]) + dirInitHash := ComputeDirectoryIdentity(hist[0]) // also test the epoch range _, err := aud.GetObservedSTRs(&AuditingRequest{ diff --git a/protocol/common.go b/protocol/common.go new file mode 100644 index 0000000..d923cb3 --- /dev/null +++ b/protocol/common.go @@ -0,0 +1,18 @@ +package protocol + +import ( + "encoding/hex" + "fmt" + + "github.com/coniks-sys/coniks-go/crypto" +) + +// ComputeDirectoryIdentity returns the hash of +// the directory's initial STR as a string. +// It panics if the STR isn't an initial STR (i.e. str.Epoch != 0). +func ComputeDirectoryIdentity(str *DirSTR) string { + if str.Epoch != 0 { + panic(fmt.Sprintf("[coniks] Expect epoch 0, got %x", str.Epoch)) + } + return hex.EncodeToString(crypto.Digest(str.Signature)) +} diff --git a/protocol/common_test.go b/protocol/common_test.go new file mode 100644 index 0000000..35d4aae --- /dev/null +++ b/protocol/common_test.go @@ -0,0 +1,39 @@ +package protocol + +import ( + "testing" +) + +func TestComputeDirectoryIdentity(t *testing.T) { + // FIXME: NewTestDirectory should use a fixed VRF and Signing keys. + d, _ := NewTestDirectory(t, true) + // str0 := d.LatestSTR() + d.Update() + str1 := d.LatestSTR() + type args struct { + str *DirSTR + } + tests := []struct { + name string + args args + want string + }{ + // {"normal", args{str0}, ""}, + {"panic", args{str1}, ""}, + } + for _, tt := range tests { + // FIXME: Refactor testing. See #18. + t.Run(tt.name, func(t *testing.T) { + if tt.name == "panic" { + defer func() { + if r := recover(); r == nil { + t.Errorf("The code did not panic") + } + }() + } + if got := ComputeDirectoryIdentity(tt.args.str); got != tt.want { + t.Errorf("ComputeDirectoryIdentity() = %v, want %v", got, tt.want) + } + }) + } +} From c3a79e03b09c3c6e2778b688de083ce7644770f7 Mon Sep 17 00:00:00 2001 From: Marcela M Date: Wed, 5 Jul 2017 18:19:14 -0400 Subject: [PATCH 32/43] Create generic auditor interface --- protocol/auditlog.go | 40 +++++++++------ protocol/auditor.go | 97 ++++++++++------------------------- protocol/consistencychecks.go | 81 +++++++++++++++++++++++++---- protocol/message.go | 42 --------------- 4 files changed, 121 insertions(+), 139 deletions(-) diff --git a/protocol/auditlog.go b/protocol/auditlog.go index 8ef6f9f..99c4601 100644 --- a/protocol/auditlog.go +++ b/protocol/auditlog.go @@ -10,12 +10,13 @@ import ( ) type directoryHistory struct { - name string - signKey sign.PublicKey + *auditorState + name string snapshots map[uint64]*DirSTR - latestSTR *DirSTR } +var _ auditor = (*directoryHistory)(nil) + // A ConiksAuditLog maintains the histories // of all CONIKS directories known to a CONIKS auditor, // indexing the histories by the hash of a directory's initial @@ -26,6 +27,18 @@ type directoryHistory struct { // epoch. type ConiksAuditLog map[string]*directoryHistory +// caller validates that initSTR is for epoch 0 +func newDirectoryHistory(name string, signKey sign.PublicKey, initSTR *DirSTR) *directoryHistory { + a := newAuditorState(signKey, initSTR) + h := &directoryHistory{ + a, + name, + make(map[uint64]*DirSTR), + } + h.updateLatestSTR(initSTR) + return h +} + // updateLatestSTR inserts a new STR into a directory history; // assumes the STR has been validated by the caller func (h *directoryHistory) updateLatestSTR(newLatest *DirSTR) { @@ -33,16 +46,6 @@ func (h *directoryHistory) updateLatestSTR(newLatest *DirSTR) { h.latestSTR = newLatest } -// caller validates that initSTR is for epoch 0 -func newDirectoryHistory(name string, signKey sign.PublicKey, initSTR *DirSTR) *directoryHistory { - h := new(directoryHistory) - h.name = name - h.signKey = signKey - h.snapshots = make(map[uint64]*DirSTR) - h.updateLatestSTR(initSTR) - return h -} - // NewAuditLog constructs a new ConiksAuditLog. It creates an empty // log; the auditor will add an entry for each CONIKS directory // the first time it observes an STR for that directory. @@ -105,7 +108,7 @@ func (l ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, endEp := uint64(len(hist)) // This loop automatically catches if hist is malformed - // (i.e. hist is missing an epoch between 0 and the latest given) + // (i.e. hist is missing an epoch between 1 and the latest given) for ep := startEp; ep < endEp; ep++ { str := hist[ep] if str == nil { @@ -114,7 +117,7 @@ func (l ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, // verify the consistency of each new STR before inserting // into the audit log - err := verifySTRConsistency(signKey, h.snapshots[ep-1], str) + err := h.verifySTRConsistency(h.snapshots[ep-1], str) if err != nil { return err @@ -149,7 +152,7 @@ func (l ConiksAuditLog) Update(dirInitHash string, newSTR *DirSTR) error { h := l[dirInitHash] - if err := verifySTRConsistency(h.signKey, h.latestSTR, newSTR); err != nil { + if err := h.verifySTRConsistency(h.latestSTR, newSTR); err != nil { return err } @@ -200,3 +203,8 @@ func (l ConiksAuditLog) GetObservedSTRs(req *AuditingRequest) (*Response, return NewSTRHistoryRange(strs) } + +func (h *directoryHistory) HandleSTRResponse(requestType int, msg *Response, name string) ErrorCode { + // FIXME: to be implemented when server-auditor protocol is implemented + return CheckPassed +} diff --git a/protocol/auditor.go b/protocol/auditor.go index 1f30e38..2a395a0 100644 --- a/protocol/auditor.go +++ b/protocol/auditor.go @@ -6,81 +6,23 @@ package protocol import ( "github.com/coniks-sys/coniks-go/crypto/sign" - m "github.com/coniks-sys/coniks-go/merkletree" ) type auditorState struct { signKey sign.PublicKey - latestSTR *m.SignedTreeRoot + latestSTR *DirSTR } -func newAuditorState(signKey sign.PublicKey, latest *m.SignedTreeRoot) *auditorState { - a := &auditorState{ - signKey: signKey, - latestSTR: latest, - } +func newAuditorState(signKey sign.PublicKey, initSTR *DirSTR) *auditorState { + a := new (auditorState) + a.signKey = signKey + a.latestSTR = initSTR return a } -// handleDirectorySTRs is supposed to be used by CONIKS clients to -// handle auditor responses, and by CONIKS auditors to handle directory -// responses. -func (a *auditorState) HandleDirectorySTRs(requestType int, msg *Response, - e error, isClient bool) error { - if err := msg.validate(); err != nil { - return e - } - - switch requestType { - case AuditType: - if _, ok := msg.DirectoryResponse.(*ObservedSTR); !ok { - return e - } - case AuditInEpochType: - // this is the default request type for an auditor - // since the auditor conservatively assumes it may - // have missed epochs - - if _, ok := msg.DirectoryResponse.(*ObservedSTRs); !ok { - return e - } - default: - panic("[coniks] Unknown auditing request type") - } - - // clients only care about comparing the STR with the savedSTR - // TODO: if the auditor has returned a more recent STR, - // should the client update its savedSTR? Should this - // force a new round of monitoring? - if isClient { - a.compareSavedSTR(requestType, msg) - } - - // we assume the requestType is AuditInEpochType if we're here - - // verify the timeliness of the STR if we're the auditor - // check the consistency of the newly received STRs - if err := a.verifySTRConsistencyRange(strs); err != nil { - return err - } - - return nil -} - -// verifySTR checks whether the received STR is the same with -// the saved STR in the audit state using reflect.DeepEqual(). -// FIXME: check whether the STR was issued on time and whatnot. -// Maybe it has something to do w/ #81 and client transitioning between epochs. -// Try to verify w/ what's been saved -func (a *auditorState) verifySTR(str *m.SignedTreeRoot) error { - return a.compareSavedSTR(str) -} - -func (a *auditorState) compareSavedSTR(str *m.SignedTreeRoot) error { - if reflect.DeepEqual(a.latestSTR, str) { - return nil - } - return CheckBadSTR +type auditor interface { + updateLatestSTR(*DirSTR) + HandleSTRResponse(int, *Response, string) ErrorCode } // verifySTRConsistency checks the consistency between 2 snapshots. @@ -90,12 +32,12 @@ func (a *auditorState) compareSavedSTR(str *m.SignedTreeRoot) error { // in its history. // In the case of a client-side consistency check, verifySTRConsistency() // should not verify the hash chain using the STR stored in cc. -func (a *auditorState) verifySTRConsistency(str *m.SignedTreeRoot) error { +func (a *auditorState) verifySTRConsistency(prevSTR, str *DirSTR) error { // verify STR's signature if !a.signKey.Verify(str.Serialize(), str.Signature) { return CheckBadSignature } - if str.VerifyHashChain(a.latestSTR) { + if str.VerifyHashChain(prevSTR) { return nil } @@ -103,6 +45,21 @@ func (a *auditorState) verifySTRConsistency(str *m.SignedTreeRoot) error { return CheckBadSTR } -func (a *auditorState) verifySTRConsistencyRange(strs []*m.SignedTreeRoot) error { - // FIXME: implement me +func (a *auditorState) verifySTRConsistencyRange(strs []*DirSTR) error { + + prev := a.latestSTR + for i := 0; i < len(strs); i++ { + str := strs[i] + + // verify the consistency of each STR in the range + err := a.verifySTRConsistency(prev, str) + + if err != nil { + return err + } + + prev = str + } + + return nil } diff --git a/protocol/consistencychecks.go b/protocol/consistencychecks.go index 0076b93..5c0adc4 100644 --- a/protocol/consistencychecks.go +++ b/protocol/consistencychecks.go @@ -26,9 +26,9 @@ import ( // subsequent responses from the ConiksDirectory to any // client request. type ConsistencyChecks struct { - // auditState stores the latest verified signed tree root + // the auditor state stores the latest verified signed tree root // as well as the server's signing key - auditState *auditorState + *auditorState Bindings map[string][]byte // extensions settings @@ -36,6 +36,8 @@ type ConsistencyChecks struct { TBs map[string]*TemporaryBinding } +var _ auditor = (*ConsistencyChecks)(nil) + // NewCC creates an instance of ConsistencyChecks using // a CONIKS directory's pinned STR at epoch 0, or // the consistency state read from persistent storage. @@ -44,10 +46,12 @@ func NewCC(savedSTR *DirSTR, useTBs bool, signKey sign.PublicKey) *ConsistencyCh if !useTBs { panic("[coniks] Currently the server is forced to use TBs") } + a := newAuditorState(signKey, savedSTR) cc := &ConsistencyChecks{ - auditState: newAuditorState(signKey, savedSTR), - Bindings: make(map[string][]byte), - useTBs: useTBs, + a, + make(map[string][]byte), + useTBs, + nil, } if useTBs { cc.TBs = make(map[string]*TemporaryBinding) @@ -55,6 +59,48 @@ func NewCC(savedSTR *DirSTR, useTBs bool, signKey sign.PublicKey) *ConsistencyCh return cc } +func (cc *ConsistencyChecks) updateLatestSTR(newLatest *DirSTR) { + cc.latestSTR = newLatest +} + +// HandleSTRResponse verifies an auditor's response for an AuditingRequest. +func (cc *ConsistencyChecks) HandleSTRResponse(requestType int, msg *Response, name string) ErrorCode { + if err := msg.validate(); err != nil { + return err.(ErrorCode) + } + + switch requestType { + case AuditType: + if _, ok := msg.DirectoryResponse.(*STRHistoryRange); !ok { + return ErrMalformedAuditorMessage + } + default: + panic("[coniks] Unknown auditing request type") + } + + // check the consistency for each + + // first compare to the saved STR + + // clients only care about comparing the STR with the savedSTR + // TODO: if the auditor has returned a more recent STR, + // should the client update its savedSTR? Should this + // force a new round of monitoring? + //if isClient { + // a.compareSavedSTR(requestType, msg) + //} + + // we assume the requestType is AuditInEpochType if we're here + + // verify the timeliness of the STR if we're the auditor + // check the consistency of the newly received STRs + // if err := cc.verifySTRConsistencyRange(strs); err != nil { + // return err + //} + + return CheckPassed +} + // HandleResponse verifies the directory's response for a request. // It first verifies the directory's returned status code of the request. // If the status code is not in the Errors array, it means @@ -105,15 +151,16 @@ func (cc *ConsistencyChecks) updateSTR(requestType int, msg *Response) error { case RegistrationType, KeyLookupType: str = msg.DirectoryResponse.(*DirectoryProof).STR // First response - if cc.auditState.latestSTR == nil { - cc.auditState.latestSTR = str + // FIXME: the initial STR is pinned in the client + if cc.latestSTR == nil { + cc.updateLatestSTR(str) return nil } - if err := cc.auditState.verfySTR(str); err == nil { + if err := cc.verifySTR(str); err == nil { return nil } // Otherwise, expect that we've entered a new epoch - if err := cc.auditState.verifySTRConsistency(str); err != nil { + if err := cc.verifySTRConsistency(cc.latestSTR, str); err != nil { return err } @@ -122,10 +169,22 @@ func (cc *ConsistencyChecks) updateSTR(requestType int, msg *Response) error { } // And update the saved STR - cc.auditState.latestSTR = str + cc.updateLatestSTR(str) return nil } +// verifySTR checks whether the received STR is the same with +// the saved STR in the audit state using reflect.DeepEqual(). +// FIXME: check whether the STR was issued on time and whatnot. +// Maybe it has something to do w/ #81 and client transitioning between epochs. +// Try to verify w/ what's been saved +func (cc *ConsistencyChecks) verifySTR(str *DirSTR) error { + if reflect.DeepEqual(cc.latestSTR, str) { + return nil + } + return CheckBadSTR +} + func (cc *ConsistencyChecks) checkConsistency(requestType int, msg *Response, uname string, key []byte) ErrorCode { var err error @@ -290,7 +349,7 @@ func (cc *ConsistencyChecks) verifyReturnedPromise(df *DirectoryProof, } // verify TB's Signature - if !cc.auditState.signKey.Verify(tb.Serialize(str.Signature), tb.Signature) { + if !cc.signKey.Verify(tb.Serialize(str.Signature), tb.Signature) { return CheckBadSignature } diff --git a/protocol/message.go b/protocol/message.go index 4ba431c..20056b6 100644 --- a/protocol/message.go +++ b/protocol/message.go @@ -106,34 +106,6 @@ type AuditingRequest struct { EndEpoch uint64 } -// 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 -// sends to a CONIKS directory, to request the given directory's latest STR. -// If the client/auditor needs to request a directory's STR for a prior epoch, it -// must send an AuditingInEpochRequest. -// -// The response to a successful request is an ObservedSTR. -type AuditingRequest struct { - DirectoryAddr string `json:"directory_addr"` -} - -// 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, -// to retrieve the STR for the directory in -// the given epoch. The client/auditor sends this request type when it needs to -// audit a directory's STR for a prior epoch (i.e. as part of a -// key lookup in epoch check, a monitoring check, or an auditor update). -// The client/auditor can send an AuditingRequest if it needs to audit a -// directory's STR for its latest epoch. -// -// The response to a successful request is an ObservedSTRs with -// a list of STRs covering the epoch range [Epoch, d.LatestSTR().Epoch]. -type AuditingInEpochRequest struct { - DirectoryAddr string `json:"directory_addr"` - Epoch uint64 `json:"epoch"` -} - // A Response message indicates the result of a CONIKS client request // with an appropriate error code, and defines the set of cryptographic // proofs a CONIKS directory must return as part of its response. @@ -177,20 +149,6 @@ type STRHistoryRange struct { STR []*DirSTR } -// An ObservedSTR response includes a single signed tree root -// STR. A CONIKS auditor returns this DirectoryResponse type upon an -// AuditingRequest. -type ObservedSTR struct { - STR *m.SignedTreeRoot -} - -// An ObservedSTRs response includes a list of signed tree roots -// STR. A CONIKS auditor returns this DirectoryResponse type upon an -// AuditingInEpochRequest. -type ObservedSTRs struct { - STR []*m.SignedTreeRoot -} - // NewErrorResponse creates a new response message indicating the error // that occurred while a CONIKS directory or a CONIKS auditor was // processing a client request. From 69e8454efa8d7343bd48c073a5000ef387c45899 Mon Sep 17 00:00:00 2001 From: Marcela M Date: Wed, 5 Jul 2017 18:19:57 -0400 Subject: [PATCH 33/43] Fix go fmt --- protocol/auditlog.go | 4 ++-- protocol/auditlog_test.go | 2 +- protocol/auditor.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/protocol/auditlog.go b/protocol/auditlog.go index 99c4601..3321479 100644 --- a/protocol/auditlog.go +++ b/protocol/auditlog.go @@ -11,7 +11,7 @@ import ( type directoryHistory struct { *auditorState - name string + name string snapshots map[uint64]*DirSTR } @@ -194,7 +194,7 @@ func (l ConiksAuditLog) GetObservedSTRs(req *AuditingRequest) (*Response, return NewErrorResponse(ErrMalformedClientMessage), ErrMalformedClientMessage } - + var strs []*DirSTR for ep := req.StartEpoch; ep <= req.EndEpoch; ep++ { str := h.snapshots[ep] diff --git a/protocol/auditlog_test.go b/protocol/auditlog_test.go index afb4a37..b242957 100644 --- a/protocol/auditlog_test.go +++ b/protocol/auditlog_test.go @@ -186,7 +186,7 @@ func TestGetObservedSTRUnknown(t *testing.T) { if err != ReqUnknownDirectory { t.Fatal("Expect ReqUnknownDirectory for latest STR") } - + _, err = aud.GetObservedSTRs(&AuditingRequest{ DirInitSTRHash: "unknown", StartEpoch: uint64(6), diff --git a/protocol/auditor.go b/protocol/auditor.go index 2a395a0..ae76b27 100644 --- a/protocol/auditor.go +++ b/protocol/auditor.go @@ -14,7 +14,7 @@ type auditorState struct { } func newAuditorState(signKey sign.PublicKey, initSTR *DirSTR) *auditorState { - a := new (auditorState) + a := new(auditorState) a.signKey = signKey a.latestSTR = initSTR return a From 1e33f46a27b0d8380e68ff14d515dd0492090a8f Mon Sep 17 00:00:00 2001 From: Marcela M Date: Thu, 13 Jul 2017 11:55:36 -0400 Subject: [PATCH 34/43] Fix some merging issues --- protocol/auditlog.go | 6 +++--- protocol/consistencychecks.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/protocol/auditlog.go b/protocol/auditlog.go index 802ab67..6732ede 100644 --- a/protocol/auditlog.go +++ b/protocol/auditlog.go @@ -51,7 +51,7 @@ func (h *directoryHistory) updateLatestSTR(newLatest *DirSTR) { // log; the auditor will add an entry for each CONIKS directory // the first time it observes an STR for that directory. func NewAuditLog() ConiksAuditLog { - return make(map[[crypto.HashSizeByte]byte]*directoryHistory) + return make(map[[crypto.HashSizeByte]byte]*directoryHistory) } // set associates the given directoryHistory with the directory identifier @@ -115,7 +115,7 @@ func (l ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, // verify the consistency of each new STR before inserting // into the audit log - if err := verifySTRConsistency(signKey, h.latestSTR, str); err != nil { + if err := h.verifySTRConsistency(h.latestSTR, str); err != nil { return err } @@ -144,7 +144,7 @@ func (l ConiksAuditLog) Update(dirInitHash [crypto.HashSizeByte]byte, newSTR *Di return ErrAuditLog } - if err := verifySTRConsistency(h.signKey, h.latestSTR, newSTR); err != nil { + if err := h.verifySTRConsistency(h.latestSTR, newSTR); err != nil { return err } diff --git a/protocol/consistencychecks.go b/protocol/consistencychecks.go index c0654fb..5c0adc4 100644 --- a/protocol/consistencychecks.go +++ b/protocol/consistencychecks.go @@ -160,7 +160,7 @@ func (cc *ConsistencyChecks) updateSTR(requestType int, msg *Response) error { return nil } // Otherwise, expect that we've entered a new epoch - if err := verifySTRConsistency(cc.signKey, cc.SavedSTR, str); err != nil { + if err := cc.verifySTRConsistency(cc.latestSTR, str); err != nil { return err } From 187c866a8061c8e6b03a652ef36a5fb8b118151f Mon Sep 17 00:00:00 2001 From: Vu Quoc Huy Date: Thu, 15 Jun 2017 22:52:27 +0700 Subject: [PATCH 35/43] Refactor generic auditing functionality TODO: * Add tests --- protocol/auditor/auditor.go | 141 ++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 protocol/auditor/auditor.go diff --git a/protocol/auditor/auditor.go b/protocol/auditor/auditor.go new file mode 100644 index 0000000..4363721 --- /dev/null +++ b/protocol/auditor/auditor.go @@ -0,0 +1,141 @@ +// This module implements a generic CONIKS auditor, i.e. the +// functionality that clients and auditors need to verify +// a server's STR history. + +package auditor + +import ( + "reflect" + + "github.com/coniks-sys/coniks-go/crypto/sign" + p "github.com/coniks-sys/coniks-go/protocol" +) + +// Auditor verifies the hash chain of a specific directory. +type Auditor struct { + signKey sign.PublicKey + verifiedSTR *p.DirSTR + // trustedSTR is the old verified STR, which is the value + // of verifiedSTR before it gets updated. + trustedSTR *p.DirSTR +} + +// New instantiates a new auditor state from a persistance storage. +func New(signKey sign.PublicKey, trusted *p.DirSTR) *Auditor { + a := &Auditor{ + signKey: signKey, + verifiedSTR: trusted, + trustedSTR: trusted, + } + return a +} + +// VerifiedSTR returns the newly verified STR. +func (a *Auditor) VerifiedSTR() *p.DirSTR { + return a.verifiedSTR +} + +// TrustedSTR returns the old verified STR, which is +// the value of VerifiedSTR() before it gets updated. +// This could be used in other operations such as +// promise verification. +func (a *Auditor) TrustedSTR() *p.DirSTR { + return a.trustedSTR +} + +// Update is supposed to be used +// by CONIKS clients to handle directory responses, +// and by CONIKS auditors to handle directory responses. +// +// It verifies the received range of STRs and updates +// the verified STR to the latest STR. +// +// Note that this function updates the `verifiedSTR` +// even the received STR is inconsistency. +func (a *Auditor) Update(msg *p.Response) error { + if p.Errors[msg.Error] { + return msg.Error + } + + strs, ok := msg.DirectoryResponse.(*p.STRHistoryRange) + if !ok || msg.Error != p.ReqSuccess || len(strs.STR) == 0 { + return p.ErrMalformedDirectoryMessage + } + + // FIXME: we are returning the error immediately + // without saving the inconsistent STR + // see: https://github.com/coniks-sys/coniks-go/pull/74#commitcomment-19804686 + switch { + case strs.STR[0].Epoch == a.verifiedSTR.Epoch: + if err := a.verifySTR(strs.STR[0]); err != nil { + return err + } + case strs.STR[0].Epoch == a.verifiedSTR.Epoch+1: + if err := a.verifySTRConsistency(a.verifiedSTR, strs.STR[0]); err != nil { + return err + } + default: + return p.CheckBadSTR + } + + for i := 1; i < len(strs.STR); i++ { + if err := a.verifySTRConsistency(strs.STR[i-1], strs.STR[i]); err != nil { + return err + } + } + a.verifiedSTR = strs.STR[len(strs.STR)-1] + + return nil +} + +// Audit is supposed to be used +// by CONIKS clients to handle auditor responses, +// and by CONIKS auditors to handle directory responses. +// It is used to check for possible equivocation between +// the auditors' view and the client own view. +// +// TODO: if the auditor has returned a more recent STR, +// should the client update its savedSTR? Should this +// force a new round of monitoring? +func (a *Auditor) Audit(msg *p.Response) error { + if p.Errors[msg.Error] { + return msg.Error + } + + strs, ok := msg.DirectoryResponse.(*p.STRHistoryRange) + if !ok || msg.Error != p.ReqSuccess || len(strs.STR) != 1 { + return p.ErrMalformedAuditorMessage + } + + return a.verifySTR(strs.STR[0]) +} + +// verifySTR checks whether the received STR is the same with +// the saved STR in the audit state using reflect.DeepEqual(). +// FIXME: check whether the STR was issued on time and whatnot. +// Maybe it has something to do w/ #81 and client transitioning between epochs. +// Try to verify w/ what's been saved +func (a *Auditor) verifySTR(str *p.DirSTR) error { + if reflect.DeepEqual(a.verifiedSTR, str) { + return nil + } + return p.CheckBadSTR +} + +// verifySTRConsistency checks the consistency between 2 snapshots. +// 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 its consistency state, +// or an auditor's pinned signing key in its history. +func (a *Auditor) verifySTRConsistency(savedSTR, str *p.DirSTR) error { + // verify STR's signature + if !a.signKey.Verify(str.Serialize(), str.Signature) { + return p.CheckBadSignature + } + if str.VerifyHashChain(savedSTR) { + return nil + } + + // TODO: verify the directory's policies as well. See #115 + return p.CheckBadSTR +} From 0f71b064ef70631b4955aa8781e1137cca014265 Mon Sep 17 00:00:00 2001 From: Marcela M Date: Thu, 13 Jul 2017 19:10:37 -0400 Subject: [PATCH 36/43] Renaming STR verification functions, use generic auditor functionality in auditlog and consistencychecks --- protocol/auditlog.go | 43 +++--- protocol/auditor.go | 114 ++++++++++++--- protocol/auditor/auditor.go | 141 ------------------- protocol/{common_test.go => auditor_test.go} | 0 protocol/common.go | 20 --- protocol/consistencychecks.go | 85 ++++------- 6 files changed, 145 insertions(+), 258 deletions(-) delete mode 100644 protocol/auditor/auditor.go rename protocol/{common_test.go => auditor_test.go} (100%) delete mode 100644 protocol/common.go diff --git a/protocol/auditlog.go b/protocol/auditlog.go index 6732ede..d3fe17a 100644 --- a/protocol/auditlog.go +++ b/protocol/auditlog.go @@ -11,22 +11,22 @@ import ( ) type directoryHistory struct { - *auditorState + *AudState addr string snapshots map[uint64]*DirSTR } -var _ auditor = (*directoryHistory)(nil) +var _ Auditor = (*directoryHistory)(nil) // caller validates that initSTR is for epoch 0 func newDirectoryHistory(addr string, signKey sign.PublicKey, initSTR *DirSTR) *directoryHistory { - a := newAuditorState(signKey, initSTR) + a := NewAuditor(signKey, initSTR) h := &directoryHistory{ a, addr, make(map[uint64]*DirSTR), } - h.updateLatestSTR(initSTR) + h.updateVerifiedSTR(initSTR) return h } @@ -42,9 +42,23 @@ type ConiksAuditLog map[[crypto.HashSizeByte]byte]*directoryHistory // updateLatestSTR inserts a new STR into a directory history; // assumes the STR has been validated by the caller -func (h *directoryHistory) updateLatestSTR(newLatest *DirSTR) { - h.snapshots[newLatest.Epoch] = newLatest - h.latestSTR = newLatest +func (h *directoryHistory) updateVerifiedSTR(newVerified *DirSTR) { + h.snapshots[newVerified.Epoch] = newVerified + h.verifiedSTR = newVerified +} + +// Audit checks that a directory's STR history +// is linear and updates the audtor's state +// if the checks pass. +// Audit() first checks the oldest STR in the +// STR range received in message against the h.verfiedSTR, +// and then verifies the remaining STRs in msg, and +// finally updates the snapshots if the checks pass. +// Audit() is called when an auditor receives new STRs +// from a directory. +func (h *directoryHistory) Audit(msg *Response) error { + // TODO: Implement as part of the auditor-server protocol + return CheckPassed } // NewAuditLog constructs a new ConiksAuditLog. It creates an empty @@ -115,11 +129,11 @@ func (l ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, // verify the consistency of each new STR before inserting // into the audit log - if err := h.verifySTRConsistency(h.latestSTR, str); err != nil { + if err := h.VerifySTRConsistency(h.verifiedSTR, str); err != nil { return err } - h.updateLatestSTR(snaps[i]) + h.updateVerifiedSTR(snaps[i]) } // Finally, add the new history to the log @@ -144,12 +158,12 @@ func (l ConiksAuditLog) Update(dirInitHash [crypto.HashSizeByte]byte, newSTR *Di return ErrAuditLog } - if err := h.verifySTRConsistency(h.latestSTR, newSTR); err != nil { + if err := h.VerifySTRConsistency(h.verifiedSTR, newSTR); err != nil { return err } // update the latest STR - h.updateLatestSTR(newSTR) + h.updateVerifiedSTR(newSTR) return nil } @@ -181,7 +195,7 @@ func (l ConiksAuditLog) GetObservedSTRs(req *AuditingRequest) (*Response, } // make sure the request is well-formed - if req.EndEpoch > h.latestSTR.Epoch || req.StartEpoch > req.EndEpoch { + if req.EndEpoch > h.verifiedSTR.Epoch || req.StartEpoch > req.EndEpoch { return NewErrorResponse(ErrMalformedClientMessage), ErrMalformedClientMessage } @@ -194,8 +208,3 @@ func (l ConiksAuditLog) GetObservedSTRs(req *AuditingRequest) (*Response, return NewSTRHistoryRange(strs) } - -func (h *directoryHistory) HandleSTRResponse(requestType int, msg *Response, name string) ErrorCode { - // FIXME: to be implemented when server-auditor protocol is implemented - return CheckPassed -} diff --git a/protocol/auditor.go b/protocol/auditor.go index ae76b27..e666a5f 100644 --- a/protocol/auditor.go +++ b/protocol/auditor.go @@ -5,34 +5,86 @@ package protocol import ( + "fmt" + "reflect" + + "github.com/coniks-sys/coniks-go/crypto" "github.com/coniks-sys/coniks-go/crypto/sign" ) -type auditorState struct { - signKey sign.PublicKey - latestSTR *DirSTR +// AudState verifies the hash chain of a specific directory. +type AudState struct { + signKey sign.PublicKey + verifiedSTR *DirSTR +} + +// Auditor provides a generic interface allowing different +// auditor types to implement specific auditing functionality. +type Auditor interface { + Audit(*Response) error } -func newAuditorState(signKey sign.PublicKey, initSTR *DirSTR) *auditorState { - a := new(auditorState) - a.signKey = signKey - a.latestSTR = initSTR +// New instantiates a new auditor state from a persistance storage. +func NewAuditor(signKey sign.PublicKey, verified *DirSTR) *AudState { + a := &AudState{ + signKey: signKey, + verifiedSTR: verified, + } return a } -type auditor interface { - updateLatestSTR(*DirSTR) - HandleSTRResponse(int, *Response, string) ErrorCode +// VerifiedSTR returns the newly verified STR. +func (a *AudState) VerifiedSTR() *DirSTR { + return a.verifiedSTR } -// verifySTRConsistency checks the consistency between 2 snapshots. +// CompareWithVerified checks whether the received STR is the same as +// the verified STR in the AudState using reflect.DeepEqual(). +func (a *AudState) CompareWithVerified(str *DirSTR) error { + if reflect.DeepEqual(a.verifiedSTR, str) { + return nil + } + return CheckBadSTR +} + +// CheckSTRAgainstVerified checks an STR str against the a.verifiedSTR. +// If str's Epoch is the same as the verified, CheckSTRAgainstVerified() +// compares the two STRs directly. If str is one epoch ahead of the +// a.verifiedSTR, CheckSTRAgainstVerified() checks the consistency between +// the two STRs. +// CheckSTRAgainstVerified() returns nil if the check passes, +// or the appropriate consistency check error if any of the checks fail, +// or str's epoch is anything other than the same or one ahead of +// a.verifiedSTR. +func (a *AudState) CheckSTRAgainstVerified(str *DirSTR) error { + // FIXME: check whether the STR was issued on time and whatnot. + // Maybe it has something to do w/ #81 and client transitioning between epochs. + // Try to verify w/ what's been saved + + switch { + case str.Epoch == a.verifiedSTR.Epoch: + // Checking an STR in the same epoch + if err := a.CompareWithVerified(str); err != nil { + return err + } + case str.Epoch == a.verifiedSTR.Epoch+1: + // Otherwise, expect that we've entered a new epoch + if err := a.VerifySTRConsistency(a.verifiedSTR, str); err != nil { + return err + } + default: + return CheckBadSTR + } + + return nil +} + +// VerifySTRConsistency checks the consistency between 2 snapshots. // 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 -// in its history. -// In the case of a client-side consistency check, verifySTRConsistency() -// should not verify the hash chain using the STR stored in cc. -func (a *auditorState) verifySTRConsistency(prevSTR, str *DirSTR) error { +// pinned signing key in its consistency state, +// or an auditor's pinned signing key in its history. +func (a *AudState) VerifySTRConsistency(prevSTR, str *DirSTR) error { // verify STR's signature if !a.signKey.Verify(str.Serialize(), str.Signature) { return CheckBadSignature @@ -45,16 +97,23 @@ func (a *auditorState) verifySTRConsistency(prevSTR, str *DirSTR) error { return CheckBadSTR } -func (a *auditorState) verifySTRConsistencyRange(strs []*DirSTR) error { +// VerifySTRHistory checks the consistency of a range +// of STRs. It begins by verifying the STR consistency between +// the given prevSTR and the first STR in the given range, and +// then verifies the consistency between each subsequent STR pair. +func (a *AudState) VerifySTRHistory(prevSTR *DirSTR, strs []*DirSTR) error { - prev := a.latestSTR + prev := prevSTR for i := 0; i < len(strs); i++ { str := strs[i] + if str == nil { + // FIXME: if this comes from the auditor, this + // should really be an ErrMalformedAuditorMessage + return ErrMalformedDirectoryMessage + } // verify the consistency of each STR in the range - err := a.verifySTRConsistency(prev, str) - - if err != nil { + if err := a.VerifySTRConsistency(prev, str); err != nil { return err } @@ -63,3 +122,16 @@ func (a *auditorState) verifySTRConsistencyRange(strs []*DirSTR) error { return nil } + +// ComputeDirectoryIdentity returns the hash of +// the directory's initial STR as a byte array. +// It panics if the STR isn't an initial STR (i.e. str.Epoch != 0). +func ComputeDirectoryIdentity(str *DirSTR) [crypto.HashSizeByte]byte { + if str.Epoch != 0 { + panic(fmt.Sprintf("[coniks] Expect epoch 0, got %x", str.Epoch)) + } + + var initSTRHash [crypto.HashSizeByte]byte + copy(initSTRHash[:], crypto.Digest(str.Signature)) + return initSTRHash +} diff --git a/protocol/auditor/auditor.go b/protocol/auditor/auditor.go deleted file mode 100644 index 4363721..0000000 --- a/protocol/auditor/auditor.go +++ /dev/null @@ -1,141 +0,0 @@ -// This module implements a generic CONIKS auditor, i.e. the -// functionality that clients and auditors need to verify -// a server's STR history. - -package auditor - -import ( - "reflect" - - "github.com/coniks-sys/coniks-go/crypto/sign" - p "github.com/coniks-sys/coniks-go/protocol" -) - -// Auditor verifies the hash chain of a specific directory. -type Auditor struct { - signKey sign.PublicKey - verifiedSTR *p.DirSTR - // trustedSTR is the old verified STR, which is the value - // of verifiedSTR before it gets updated. - trustedSTR *p.DirSTR -} - -// New instantiates a new auditor state from a persistance storage. -func New(signKey sign.PublicKey, trusted *p.DirSTR) *Auditor { - a := &Auditor{ - signKey: signKey, - verifiedSTR: trusted, - trustedSTR: trusted, - } - return a -} - -// VerifiedSTR returns the newly verified STR. -func (a *Auditor) VerifiedSTR() *p.DirSTR { - return a.verifiedSTR -} - -// TrustedSTR returns the old verified STR, which is -// the value of VerifiedSTR() before it gets updated. -// This could be used in other operations such as -// promise verification. -func (a *Auditor) TrustedSTR() *p.DirSTR { - return a.trustedSTR -} - -// Update is supposed to be used -// by CONIKS clients to handle directory responses, -// and by CONIKS auditors to handle directory responses. -// -// It verifies the received range of STRs and updates -// the verified STR to the latest STR. -// -// Note that this function updates the `verifiedSTR` -// even the received STR is inconsistency. -func (a *Auditor) Update(msg *p.Response) error { - if p.Errors[msg.Error] { - return msg.Error - } - - strs, ok := msg.DirectoryResponse.(*p.STRHistoryRange) - if !ok || msg.Error != p.ReqSuccess || len(strs.STR) == 0 { - return p.ErrMalformedDirectoryMessage - } - - // FIXME: we are returning the error immediately - // without saving the inconsistent STR - // see: https://github.com/coniks-sys/coniks-go/pull/74#commitcomment-19804686 - switch { - case strs.STR[0].Epoch == a.verifiedSTR.Epoch: - if err := a.verifySTR(strs.STR[0]); err != nil { - return err - } - case strs.STR[0].Epoch == a.verifiedSTR.Epoch+1: - if err := a.verifySTRConsistency(a.verifiedSTR, strs.STR[0]); err != nil { - return err - } - default: - return p.CheckBadSTR - } - - for i := 1; i < len(strs.STR); i++ { - if err := a.verifySTRConsistency(strs.STR[i-1], strs.STR[i]); err != nil { - return err - } - } - a.verifiedSTR = strs.STR[len(strs.STR)-1] - - return nil -} - -// Audit is supposed to be used -// by CONIKS clients to handle auditor responses, -// and by CONIKS auditors to handle directory responses. -// It is used to check for possible equivocation between -// the auditors' view and the client own view. -// -// TODO: if the auditor has returned a more recent STR, -// should the client update its savedSTR? Should this -// force a new round of monitoring? -func (a *Auditor) Audit(msg *p.Response) error { - if p.Errors[msg.Error] { - return msg.Error - } - - strs, ok := msg.DirectoryResponse.(*p.STRHistoryRange) - if !ok || msg.Error != p.ReqSuccess || len(strs.STR) != 1 { - return p.ErrMalformedAuditorMessage - } - - return a.verifySTR(strs.STR[0]) -} - -// verifySTR checks whether the received STR is the same with -// the saved STR in the audit state using reflect.DeepEqual(). -// FIXME: check whether the STR was issued on time and whatnot. -// Maybe it has something to do w/ #81 and client transitioning between epochs. -// Try to verify w/ what's been saved -func (a *Auditor) verifySTR(str *p.DirSTR) error { - if reflect.DeepEqual(a.verifiedSTR, str) { - return nil - } - return p.CheckBadSTR -} - -// verifySTRConsistency checks the consistency between 2 snapshots. -// 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 its consistency state, -// or an auditor's pinned signing key in its history. -func (a *Auditor) verifySTRConsistency(savedSTR, str *p.DirSTR) error { - // verify STR's signature - if !a.signKey.Verify(str.Serialize(), str.Signature) { - return p.CheckBadSignature - } - if str.VerifyHashChain(savedSTR) { - return nil - } - - // TODO: verify the directory's policies as well. See #115 - return p.CheckBadSTR -} diff --git a/protocol/common_test.go b/protocol/auditor_test.go similarity index 100% rename from protocol/common_test.go rename to protocol/auditor_test.go diff --git a/protocol/common.go b/protocol/common.go deleted file mode 100644 index deb68a9..0000000 --- a/protocol/common.go +++ /dev/null @@ -1,20 +0,0 @@ -package protocol - -import ( - "fmt" - - "github.com/coniks-sys/coniks-go/crypto" -) - -// ComputeDirectoryIdentity returns the hash of -// the directory's initial STR as a byte array. -// It panics if the STR isn't an initial STR (i.e. str.Epoch != 0). -func ComputeDirectoryIdentity(str *DirSTR) [crypto.HashSizeByte]byte { - if str.Epoch != 0 { - panic(fmt.Sprintf("[coniks] Expect epoch 0, got %x", str.Epoch)) - } - - var initSTRHash [crypto.HashSizeByte]byte - copy(initSTRHash[:], crypto.Digest(str.Signature)) - return initSTRHash -} diff --git a/protocol/consistencychecks.go b/protocol/consistencychecks.go index 5c0adc4..78fe714 100644 --- a/protocol/consistencychecks.go +++ b/protocol/consistencychecks.go @@ -8,7 +8,6 @@ package protocol import ( "bytes" - "reflect" "github.com/coniks-sys/coniks-go/crypto/sign" m "github.com/coniks-sys/coniks-go/merkletree" @@ -28,7 +27,7 @@ import ( type ConsistencyChecks struct { // the auditor state stores the latest verified signed tree root // as well as the server's signing key - *auditorState + *AudState Bindings map[string][]byte // extensions settings @@ -36,7 +35,7 @@ type ConsistencyChecks struct { TBs map[string]*TemporaryBinding } -var _ auditor = (*ConsistencyChecks)(nil) +var _ Auditor = (*ConsistencyChecks)(nil) // NewCC creates an instance of ConsistencyChecks using // a CONIKS directory's pinned STR at epoch 0, or @@ -46,9 +45,9 @@ func NewCC(savedSTR *DirSTR, useTBs bool, signKey sign.PublicKey) *ConsistencyCh if !useTBs { panic("[coniks] Currently the server is forced to use TBs") } - a := newAuditorState(signKey, savedSTR) + aud := NewAuditor(signKey, savedSTR) cc := &ConsistencyChecks{ - a, + aud, make(map[string][]byte), useTBs, nil, @@ -59,46 +58,33 @@ func NewCC(savedSTR *DirSTR, useTBs bool, signKey sign.PublicKey) *ConsistencyCh return cc } -func (cc *ConsistencyChecks) updateLatestSTR(newLatest *DirSTR) { - cc.latestSTR = newLatest -} - -// HandleSTRResponse verifies an auditor's response for an AuditingRequest. -func (cc *ConsistencyChecks) HandleSTRResponse(requestType int, msg *Response, name string) ErrorCode { +// Audit checks for possible equivocation between +// an auditors' observed STRs and the client's own view. +// Audit() first verifies the STR range received +// in msg if msg contains more than 1 STR, and +// then checks the most recent STR in msg against +// the cc.verifiedSTR. +// Audit() is called when a client receives a response to a +// message.AuditingRequest from an auditor +func (cc *ConsistencyChecks) Audit(msg *Response) error { if err := msg.validate(); err != nil { return err.(ErrorCode) } - switch requestType { - case AuditType: - if _, ok := msg.DirectoryResponse.(*STRHistoryRange); !ok { - return ErrMalformedAuditorMessage + strs := msg.DirectoryResponse.(*STRHistoryRange) + + // verify the hashchain of the received STRs + // if we get more than 1 in our range + if len(strs.STR) > 1 { + if err := cc.VerifySTRHistory(strs.STR[0], strs.STR[1:]); err != nil { + return err } - default: - panic("[coniks] Unknown auditing request type") } - // check the consistency for each - - // first compare to the saved STR - - // clients only care about comparing the STR with the savedSTR // TODO: if the auditor has returned a more recent STR, // should the client update its savedSTR? Should this // force a new round of monitoring? - //if isClient { - // a.compareSavedSTR(requestType, msg) - //} - - // we assume the requestType is AuditInEpochType if we're here - - // verify the timeliness of the STR if we're the auditor - // check the consistency of the newly received STRs - // if err := cc.verifySTRConsistencyRange(strs); err != nil { - // return err - //} - - return CheckPassed + return cc.CheckSTRAgainstVerified(strs.STR[len(strs.STR)-1]) } // HandleResponse verifies the directory's response for a request. @@ -150,17 +136,9 @@ func (cc *ConsistencyChecks) updateSTR(requestType int, msg *Response) error { switch requestType { case RegistrationType, KeyLookupType: str = msg.DirectoryResponse.(*DirectoryProof).STR - // First response - // FIXME: the initial STR is pinned in the client - if cc.latestSTR == nil { - cc.updateLatestSTR(str) - return nil - } - if err := cc.verifySTR(str); err == nil { - return nil - } - // Otherwise, expect that we've entered a new epoch - if err := cc.verifySTRConsistency(cc.latestSTR, str); err != nil { + // The initial STR is pinned in the client + // so cc.verifiedSTR should never be nil + if err := cc.CheckSTRAgainstVerified(str); err != nil { return err } @@ -169,20 +147,9 @@ func (cc *ConsistencyChecks) updateSTR(requestType int, msg *Response) error { } // And update the saved STR - cc.updateLatestSTR(str) - return nil -} + cc.verifiedSTR = str -// verifySTR checks whether the received STR is the same with -// the saved STR in the audit state using reflect.DeepEqual(). -// FIXME: check whether the STR was issued on time and whatnot. -// Maybe it has something to do w/ #81 and client transitioning between epochs. -// Try to verify w/ what's been saved -func (cc *ConsistencyChecks) verifySTR(str *DirSTR) error { - if reflect.DeepEqual(cc.latestSTR, str) { - return nil - } - return CheckBadSTR + return nil } func (cc *ConsistencyChecks) checkConsistency(requestType int, msg *Response, From d9a95482e712b4eab75fa1ddf4850065176f3072 Mon Sep 17 00:00:00 2001 From: Marcela Melara Date: Tue, 18 Jul 2017 09:54:37 -0500 Subject: [PATCH 37/43] h.verifiedSTR -> h.VerifiedSTR() --- protocol/auditlog.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/protocol/auditlog.go b/protocol/auditlog.go index d3fe17a..f603e6d 100644 --- a/protocol/auditlog.go +++ b/protocol/auditlog.go @@ -40,7 +40,7 @@ func newDirectoryHistory(addr string, signKey sign.PublicKey, initSTR *DirSTR) * // chronological order. type ConiksAuditLog map[[crypto.HashSizeByte]byte]*directoryHistory -// updateLatestSTR inserts a new STR into a directory history; +// updateVerifiedSTR inserts a new STR into a directory history; // assumes the STR has been validated by the caller func (h *directoryHistory) updateVerifiedSTR(newVerified *DirSTR) { h.snapshots[newVerified.Epoch] = newVerified @@ -129,7 +129,7 @@ func (l ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, // verify the consistency of each new STR before inserting // into the audit log - if err := h.VerifySTRConsistency(h.verifiedSTR, str); err != nil { + if err := h.VerifySTRConsistency(h.VerifiedSTR(), str); err != nil { return err } @@ -158,7 +158,7 @@ func (l ConiksAuditLog) Update(dirInitHash [crypto.HashSizeByte]byte, newSTR *Di return ErrAuditLog } - if err := h.VerifySTRConsistency(h.verifiedSTR, newSTR); err != nil { + if err := h.VerifySTRConsistency(h.VerifiedSTR(), newSTR); err != nil { return err } @@ -195,7 +195,7 @@ func (l ConiksAuditLog) GetObservedSTRs(req *AuditingRequest) (*Response, } // make sure the request is well-formed - if req.EndEpoch > h.verifiedSTR.Epoch || req.StartEpoch > req.EndEpoch { + if req.EndEpoch > h.VerifiedSTR().Epoch || req.StartEpoch > req.EndEpoch { return NewErrorResponse(ErrMalformedClientMessage), ErrMalformedClientMessage } From 88d021f343aebfeb6189f3d6e3fd38e8063bc1a9 Mon Sep 17 00:00:00 2001 From: Marcela Melara Date: Wed, 19 Jul 2017 12:11:34 -0500 Subject: [PATCH 38/43] Refactor common STR verification for STR ranges from server --- protocol/auditlog.go | 53 ++++++++++++++++------------- protocol/auditor.go | 63 +++++++++++++++++++++++++++-------- protocol/consistencychecks.go | 21 ++++++------ 3 files changed, 89 insertions(+), 48 deletions(-) diff --git a/protocol/auditlog.go b/protocol/auditlog.go index f603e6d..217b526 100644 --- a/protocol/auditlog.go +++ b/protocol/auditlog.go @@ -13,20 +13,18 @@ import ( type directoryHistory struct { *AudState addr string - snapshots map[uint64]*DirSTR + snapshots []*DirSTR } -var _ Auditor = (*directoryHistory)(nil) - // caller validates that initSTR is for epoch 0 func newDirectoryHistory(addr string, signKey sign.PublicKey, initSTR *DirSTR) *directoryHistory { a := NewAuditor(signKey, initSTR) h := &directoryHistory{ a, addr, - make(map[uint64]*DirSTR), + make([]*DirSTR, 0), } - h.updateVerifiedSTR(initSTR) + h.updateVerifiedSTR([]*DirSTR{initSTR}) return h } @@ -40,11 +38,12 @@ func newDirectoryHistory(addr string, signKey sign.PublicKey, initSTR *DirSTR) * // chronological order. type ConiksAuditLog map[[crypto.HashSizeByte]byte]*directoryHistory -// updateVerifiedSTR inserts a new STR into a directory history; -// assumes the STR has been validated by the caller -func (h *directoryHistory) updateVerifiedSTR(newVerified *DirSTR) { - h.snapshots[newVerified.Epoch] = newVerified - h.verifiedSTR = newVerified +// updateVerifiedSTR inserts a new range of STRs into a directory history; +// assumes the STRs have been validated by the caller +func (h *directoryHistory) updateVerifiedSTR(newVerified []*DirSTR) { + h.Update(LatestSTRInRange(newVerified)) + h.snapshots = append(h.snapshots, newVerified...) + } // Audit checks that a directory's STR history @@ -89,10 +88,11 @@ func (l ConiksAuditLog) get(dirInitHash [crypto.HashSizeByte]byte) (*directoryHi // signing key signKey, and a list of snapshots snaps representing the // directory's STR history so far, in chronological order. // Insert() returns an ErrAuditLog if the auditor attempts to create -// a new history for a known directory, an ErrMalformedDirectoryMessage -// if oldSTRs is malformed, and nil otherwise. +// a new history for a known directory, and nil otherwise. // Insert() only creates the initial entry in the log for addr. Use Update() // to insert newly observed STRs for addr in subsequent epochs. +// Insert() assumes that the caller +// has called Audit() on snaps before calling Insert(). // 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 @@ -117,6 +117,7 @@ func (l ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, // create the new directory history h = newDirectoryHistory(addr, signKey, snaps[0]) + // FIXME: remove this check --> caller calls Audit() before this function // add each STR into the history // start at 1 since we've inserted the initial STR above // This loop automatically catches if snaps is malformed @@ -129,26 +130,28 @@ func (l ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, // verify the consistency of each new STR before inserting // into the audit log - if err := h.VerifySTRConsistency(h.VerifiedSTR(), str); err != nil { + if err := h.verifySTRConsistency(snaps[i-1], str); err != nil { return err } - - h.updateVerifiedSTR(snaps[i]) } // Finally, add the new history to the log + if len(snaps) > 1 { + h.updateVerifiedSTR(snaps[1:]) + } l.set(dirInitHash, h) return nil } -// Update verifies the consistency of a newly observed STR newSTR for -// the directory addr, and inserts the newSTR into addr's directory history -// if the checks (i.e. STR signature and hash chain verifications) pass. -// Update() returns nil if the checks pass, and the appropriate consistency -// check error otherwise. Update() assumes that Insert() has been called for -// addr prior to its first call and thereby expects that an entry for addr -// exists in the audit log l. +// Update inserts a newly observed STR newSTR into the log entry for the +// directory history given by dirInitHash (hash of direcotry's initial STR). +// Update() assumes that Insert() has been called for +// dirInitHash prior to its first call and thereby expects that an +// entry for addr exists in the audit log l, and that the caller +// has called Audit() on newSTR before calling Update(). +// Update() returns ErrAuditLog if the audit log doesn't contain an +// entry for dirInitHash // FIXME: pass Response message as param func (l ConiksAuditLog) Update(dirInitHash [crypto.HashSizeByte]byte, newSTR *DirSTR) error { @@ -158,12 +161,14 @@ func (l ConiksAuditLog) Update(dirInitHash [crypto.HashSizeByte]byte, newSTR *Di return ErrAuditLog } - if err := h.VerifySTRConsistency(h.VerifiedSTR(), newSTR); err != nil { + // FIXME: remove this check --> caller calls Audit() before this function + if err := h.verifySTRConsistency(h.VerifiedSTR(), newSTR); err != nil { return err } // update the latest STR - h.updateVerifiedSTR(newSTR) + // FIXME: use STR slice from Response msg + h.updateVerifiedSTR([]*DirSTR{newSTR}) return nil } diff --git a/protocol/auditor.go b/protocol/auditor.go index e666a5f..34752e5 100644 --- a/protocol/auditor.go +++ b/protocol/auditor.go @@ -12,17 +12,19 @@ import ( "github.com/coniks-sys/coniks-go/crypto/sign" ) +// Auditor provides a generic interface allowing different +// auditor types to implement specific auditing functionality. +type Auditor interface { + AuditDirectory([]*DirSTR) error +} + // AudState verifies the hash chain of a specific directory. type AudState struct { signKey sign.PublicKey verifiedSTR *DirSTR } -// Auditor provides a generic interface allowing different -// auditor types to implement specific auditing functionality. -type Auditor interface { - Audit(*Response) error -} +var _ Auditor = (*AudState)(nil) // New instantiates a new auditor state from a persistance storage. func NewAuditor(signKey sign.PublicKey, verified *DirSTR) *AudState { @@ -38,9 +40,14 @@ func (a *AudState) VerifiedSTR() *DirSTR { return a.verifiedSTR } +// Update updates the auditor's verifiedSTR to newSTR +func (a *AudState) Update(newSTR *DirSTR) { + a.verifiedSTR = newSTR +} + // CompareWithVerified checks whether the received STR is the same as // the verified STR in the AudState using reflect.DeepEqual(). -func (a *AudState) CompareWithVerified(str *DirSTR) error { +func (a *AudState) compareWithVerified(str *DirSTR) error { if reflect.DeepEqual(a.verifiedSTR, str) { return nil } @@ -56,7 +63,7 @@ func (a *AudState) CompareWithVerified(str *DirSTR) error { // or the appropriate consistency check error if any of the checks fail, // or str's epoch is anything other than the same or one ahead of // a.verifiedSTR. -func (a *AudState) CheckSTRAgainstVerified(str *DirSTR) error { +func (a *AudState) checkSTRAgainstVerified(str *DirSTR) error { // FIXME: check whether the STR was issued on time and whatnot. // Maybe it has something to do w/ #81 and client transitioning between epochs. // Try to verify w/ what's been saved @@ -64,12 +71,12 @@ func (a *AudState) CheckSTRAgainstVerified(str *DirSTR) error { switch { case str.Epoch == a.verifiedSTR.Epoch: // Checking an STR in the same epoch - if err := a.CompareWithVerified(str); err != nil { + if err := a.compareWithVerified(str); err != nil { return err } case str.Epoch == a.verifiedSTR.Epoch+1: // Otherwise, expect that we've entered a new epoch - if err := a.VerifySTRConsistency(a.verifiedSTR, str); err != nil { + if err := a.verifySTRConsistency(a.verifiedSTR, str); err != nil { return err } default: @@ -84,7 +91,7 @@ func (a *AudState) CheckSTRAgainstVerified(str *DirSTR) error { // The signKey param either comes from a client's // pinned signing key in its consistency state, // or an auditor's pinned signing key in its history. -func (a *AudState) VerifySTRConsistency(prevSTR, str *DirSTR) error { +func (a *AudState) verifySTRConsistency(prevSTR, str *DirSTR) error { // verify STR's signature if !a.signKey.Verify(str.Serialize(), str.Signature) { return CheckBadSignature @@ -98,10 +105,10 @@ func (a *AudState) VerifySTRConsistency(prevSTR, str *DirSTR) error { } // VerifySTRHistory checks the consistency of a range -// of STRs. It begins by verifying the STR consistency between +// of a directory's STRs. It begins by verifying the STR consistency between // the given prevSTR and the first STR in the given range, and // then verifies the consistency between each subsequent STR pair. -func (a *AudState) VerifySTRHistory(prevSTR *DirSTR, strs []*DirSTR) error { +func (a *AudState) verifySTRRange(prevSTR *DirSTR, strs []*DirSTR) error { prev := prevSTR for i := 0; i < len(strs); i++ { @@ -113,7 +120,7 @@ func (a *AudState) VerifySTRHistory(prevSTR *DirSTR, strs []*DirSTR) error { } // verify the consistency of each STR in the range - if err := a.VerifySTRConsistency(prev, str); err != nil { + if err := a.verifySTRConsistency(prev, str); err != nil { return err } @@ -123,6 +130,29 @@ func (a *AudState) VerifySTRHistory(prevSTR *DirSTR, strs []*DirSTR) error { return nil } +// AuditDirectory validates a range of STRs received from a CONIKS directory. +// AuditDirectory() checks the consistency of the oldest STR in the range +// against the verifiedSTR, and verifies the remaining +// range if the message contains more than one STR. +// AuditDirectory() returns the appropriate consistency check error +// if any of the checks fail, or nil if the checks pass. +func (a *AudState) AuditDirectory(strs []*DirSTR) error { + + // check STR against the latest verified STR + if err := a.checkSTRAgainstVerified(strs[0]); err != nil { + return err + } + + // verify the entire range if we have received more than one STR + if len(strs) > 1 { + if err := a.verifySTRRange(strs[0], strs[1:]); err != nil { + return err + } + } + + return nil +} + // ComputeDirectoryIdentity returns the hash of // the directory's initial STR as a byte array. // It panics if the STR isn't an initial STR (i.e. str.Epoch != 0). @@ -135,3 +165,10 @@ func ComputeDirectoryIdentity(str *DirSTR) [crypto.HashSizeByte]byte { copy(initSTRHash[:], crypto.Digest(str.Signature)) return initSTRHash } + +// LatestSTRInRange returns the STR in the last element of a +// range of STRs. The range is assumed to be sorted +// by increasing order of STR epoch. +func LatestSTRInRange(strs []*DirSTR) *DirSTR { + return strs[len(strs)-1] +} diff --git a/protocol/consistencychecks.go b/protocol/consistencychecks.go index 78fe714..38d08a3 100644 --- a/protocol/consistencychecks.go +++ b/protocol/consistencychecks.go @@ -35,8 +35,6 @@ type ConsistencyChecks struct { TBs map[string]*TemporaryBinding } -var _ Auditor = (*ConsistencyChecks)(nil) - // NewCC creates an instance of ConsistencyChecks using // a CONIKS directory's pinned STR at epoch 0, or // the consistency state read from persistent storage. @@ -58,15 +56,15 @@ func NewCC(savedSTR *DirSTR, useTBs bool, signKey sign.PublicKey) *ConsistencyCh return cc } -// Audit checks for possible equivocation between +// CheckEquivocation checks for possible equivocation between // an auditors' observed STRs and the client's own view. -// Audit() first verifies the STR range received +// CheckEquivocation() first verifies the STR range received // in msg if msg contains more than 1 STR, and // then checks the most recent STR in msg against // the cc.verifiedSTR. -// Audit() is called when a client receives a response to a -// message.AuditingRequest from an auditor -func (cc *ConsistencyChecks) Audit(msg *Response) error { +// CheckEquivocation() is called when a client receives a response to a +// message.AuditingRequest from an auditor. +func (cc *ConsistencyChecks) CheckEquivocation(msg *Response) error { if err := msg.validate(); err != nil { return err.(ErrorCode) } @@ -76,7 +74,7 @@ func (cc *ConsistencyChecks) Audit(msg *Response) error { // verify the hashchain of the received STRs // if we get more than 1 in our range if len(strs.STR) > 1 { - if err := cc.VerifySTRHistory(strs.STR[0], strs.STR[1:]); err != nil { + if err := cc.verifySTRRange(strs.STR[0], strs.STR[1:]); err != nil { return err } } @@ -84,7 +82,7 @@ func (cc *ConsistencyChecks) Audit(msg *Response) error { // TODO: if the auditor has returned a more recent STR, // should the client update its savedSTR? Should this // force a new round of monitoring? - return cc.CheckSTRAgainstVerified(strs.STR[len(strs.STR)-1]) + return cc.checkSTRAgainstVerified(LatestSTRInRange(strs.STR)) } // HandleResponse verifies the directory's response for a request. @@ -138,7 +136,8 @@ func (cc *ConsistencyChecks) updateSTR(requestType int, msg *Response) error { str = msg.DirectoryResponse.(*DirectoryProof).STR // The initial STR is pinned in the client // so cc.verifiedSTR should never be nil - if err := cc.CheckSTRAgainstVerified(str); err != nil { + // FIXME: use STR slice from Response msg + if err := cc.AuditDirectory([]*DirSTR{str}); err != nil { return err } @@ -147,7 +146,7 @@ func (cc *ConsistencyChecks) updateSTR(requestType int, msg *Response) error { } // And update the saved STR - cc.verifiedSTR = str + cc.Update(str) return nil } From 4865670d4ed73c9e45639042fa895c349ff47ac1 Mon Sep 17 00:00:00 2001 From: Marcela Melara Date: Wed, 19 Jul 2017 12:31:05 -0500 Subject: [PATCH 39/43] Fix --- protocol/auditlog.go | 1 - 1 file changed, 1 deletion(-) diff --git a/protocol/auditlog.go b/protocol/auditlog.go index 217b526..5afb051 100644 --- a/protocol/auditlog.go +++ b/protocol/auditlog.go @@ -43,7 +43,6 @@ type ConiksAuditLog map[[crypto.HashSizeByte]byte]*directoryHistory func (h *directoryHistory) updateVerifiedSTR(newVerified []*DirSTR) { h.Update(LatestSTRInRange(newVerified)) h.snapshots = append(h.snapshots, newVerified...) - } // Audit checks that a directory's STR history From 2a494b5eab01c01737668bdc214f0afe6e9824e2 Mon Sep 17 00:00:00 2001 From: Marcela Melara Date: Wed, 2 Aug 2017 16:52:11 -0400 Subject: [PATCH 40/43] Fix documentation and formatting, revert to use map for auditlog snapshots --- protocol/auditlog.go | 35 ++++++++++----------- protocol/auditor.go | 58 ++++++++++++++++++----------------- protocol/consistencychecks.go | 10 +++--- 3 files changed, 51 insertions(+), 52 deletions(-) diff --git a/protocol/auditlog.go b/protocol/auditlog.go index 5afb051..5d86897 100644 --- a/protocol/auditlog.go +++ b/protocol/auditlog.go @@ -13,18 +13,18 @@ import ( type directoryHistory struct { *AudState addr string - snapshots []*DirSTR + snapshots map[uint64]*DirSTR } // caller validates that initSTR is for epoch 0 func newDirectoryHistory(addr string, signKey sign.PublicKey, initSTR *DirSTR) *directoryHistory { a := NewAuditor(signKey, initSTR) h := &directoryHistory{ - a, - addr, - make([]*DirSTR, 0), + AudState: a, + addr: addr, + snapshots: make(map[uint64]*DirSTR), } - h.updateVerifiedSTR([]*DirSTR{initSTR}) + h.updateVerifiedSTR(initSTR) return h } @@ -40,9 +40,9 @@ type ConiksAuditLog map[[crypto.HashSizeByte]byte]*directoryHistory // updateVerifiedSTR inserts a new range of STRs into a directory history; // assumes the STRs have been validated by the caller -func (h *directoryHistory) updateVerifiedSTR(newVerified []*DirSTR) { - h.Update(LatestSTRInRange(newVerified)) - h.snapshots = append(h.snapshots, newVerified...) +func (h *directoryHistory) updateVerifiedSTR(newVerified *DirSTR) { + h.Update(newVerified) + h.snapshots[newVerified.Epoch] = newVerified } // Audit checks that a directory's STR history @@ -97,7 +97,6 @@ func (l ConiksAuditLog) get(dirInitHash [crypto.HashSizeByte]byte) (*directoryHi // for "catching up" on a history in case an auditor misses epochs func (l ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, snaps []*DirSTR) error { - // make sure we're getting an initial STR at the very least if len(snaps) < 1 || snaps[0].Epoch != 0 { return ErrMalformedDirectoryMessage @@ -116,7 +115,8 @@ func (l ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, // create the new directory history h = newDirectoryHistory(addr, signKey, snaps[0]) - // FIXME: remove this check --> caller calls Audit() before this function + // FIXME: remove this check --> caller calls Audit() before + // this function // add each STR into the history // start at 1 since we've inserted the initial STR above // This loop automatically catches if snaps is malformed @@ -129,15 +129,13 @@ func (l ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, // verify the consistency of each new STR before inserting // into the audit log - if err := h.verifySTRConsistency(snaps[i-1], str); err != nil { + if err := h.verifySTRConsistency(h.VerifiedSTR(), str); err != nil { return err } - } - // Finally, add the new history to the log - if len(snaps) > 1 { - h.updateVerifiedSTR(snaps[1:]) + h.updateVerifiedSTR(snaps[i]) } + l.set(dirInitHash, h) return nil @@ -153,21 +151,21 @@ func (l ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, // entry for dirInitHash // FIXME: pass Response message as param func (l ConiksAuditLog) Update(dirInitHash [crypto.HashSizeByte]byte, newSTR *DirSTR) error { - // error if we want to update the entry for an addr we don't know h, ok := l.get(dirInitHash) if !ok { return ErrAuditLog } - // FIXME: remove this check --> caller calls Audit() before this function + // FIXME: remove this check --> caller calls Audit() before this + // function if err := h.verifySTRConsistency(h.VerifiedSTR(), newSTR); err != nil { return err } // update the latest STR // FIXME: use STR slice from Response msg - h.updateVerifiedSTR([]*DirSTR{newSTR}) + h.updateVerifiedSTR(newSTR) return nil } @@ -191,7 +189,6 @@ func (l ConiksAuditLog) Update(dirInitHash [crypto.HashSizeByte]byte, newSTR *Di // message.NewErrorResponse(ReqUnknownDirectory) tuple. func (l ConiksAuditLog) GetObservedSTRs(req *AuditingRequest) (*Response, ErrorCode) { - // make sure we have a history for the requested directory in the log h, ok := l.get(req.DirInitSTRHash) if !ok { diff --git a/protocol/auditor.go b/protocol/auditor.go index 34752e5..9fb0409 100644 --- a/protocol/auditor.go +++ b/protocol/auditor.go @@ -26,7 +26,7 @@ type AudState struct { var _ Auditor = (*AudState)(nil) -// New instantiates a new auditor state from a persistance storage. +// NewAuditor instantiates a new auditor state from a persistance storage. func NewAuditor(signKey sign.PublicKey, verified *DirSTR) *AudState { a := &AudState{ signKey: signKey, @@ -45,7 +45,7 @@ func (a *AudState) Update(newSTR *DirSTR) { a.verifiedSTR = newSTR } -// CompareWithVerified checks whether the received STR is the same as +// compareWithVerified checks whether the received STR is the same as // the verified STR in the AudState using reflect.DeepEqual(). func (a *AudState) compareWithVerified(str *DirSTR) error { if reflect.DeepEqual(a.verifiedSTR, str) { @@ -54,20 +54,42 @@ func (a *AudState) compareWithVerified(str *DirSTR) error { return CheckBadSTR } -// CheckSTRAgainstVerified checks an STR str against the a.verifiedSTR. -// If str's Epoch is the same as the verified, CheckSTRAgainstVerified() +// verifySTRConsistency checks the consistency between 2 snapshots. +// 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 its consistency state, +// or an auditor's pinned signing key in its history. +func (a *AudState) verifySTRConsistency(prevSTR, str *DirSTR) error { + // verify STR's signature + if !a.signKey.Verify(str.Serialize(), str.Signature) { + return CheckBadSignature + } + if str.VerifyHashChain(prevSTR) { + return nil + } + + // TODO: verify the directory's policies as well. See #115 + return CheckBadSTR +} + +// checkSTRAgainstVerified checks an STR str against the a.verifiedSTR. +// If str's Epoch is the same as the verified, checkSTRAgainstVerified() // compares the two STRs directly. If str is one epoch ahead of the -// a.verifiedSTR, CheckSTRAgainstVerified() checks the consistency between +// a.verifiedSTR, checkSTRAgainstVerified() checks the consistency between // the two STRs. -// CheckSTRAgainstVerified() returns nil if the check passes, +// checkSTRAgainstVerified() returns nil if the check passes, // or the appropriate consistency check error if any of the checks fail, // or str's epoch is anything other than the same or one ahead of // a.verifiedSTR. func (a *AudState) checkSTRAgainstVerified(str *DirSTR) error { // FIXME: check whether the STR was issued on time and whatnot. - // Maybe it has something to do w/ #81 and client transitioning between epochs. + // Maybe it has something to do w/ #81 and client + // transitioning between epochs. // Try to verify w/ what's been saved + // FIXME: we are returning the error immediately + // without saving the inconsistent STR + // see: https://github.com/coniks-sys/coniks-go/pull/74#commitcomment-19804686 switch { case str.Epoch == a.verifiedSTR.Epoch: // Checking an STR in the same epoch @@ -86,30 +108,11 @@ func (a *AudState) checkSTRAgainstVerified(str *DirSTR) error { return nil } -// VerifySTRConsistency checks the consistency between 2 snapshots. -// 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 its consistency state, -// or an auditor's pinned signing key in its history. -func (a *AudState) verifySTRConsistency(prevSTR, str *DirSTR) error { - // verify STR's signature - if !a.signKey.Verify(str.Serialize(), str.Signature) { - return CheckBadSignature - } - if str.VerifyHashChain(prevSTR) { - return nil - } - - // TODO: verify the directory's policies as well. See #115 - return CheckBadSTR -} - -// VerifySTRHistory checks the consistency of a range +// verifySTRRange checks the consistency of a range // of a directory's STRs. It begins by verifying the STR consistency between // the given prevSTR and the first STR in the given range, and // then verifies the consistency between each subsequent STR pair. func (a *AudState) verifySTRRange(prevSTR *DirSTR, strs []*DirSTR) error { - prev := prevSTR for i := 0; i < len(strs); i++ { str := strs[i] @@ -137,7 +140,6 @@ func (a *AudState) verifySTRRange(prevSTR *DirSTR, strs []*DirSTR) error { // AuditDirectory() returns the appropriate consistency check error // if any of the checks fail, or nil if the checks pass. func (a *AudState) AuditDirectory(strs []*DirSTR) error { - // check STR against the latest verified STR if err := a.checkSTRAgainstVerified(strs[0]); err != nil { return err diff --git a/protocol/consistencychecks.go b/protocol/consistencychecks.go index 38d08a3..481ba56 100644 --- a/protocol/consistencychecks.go +++ b/protocol/consistencychecks.go @@ -43,12 +43,12 @@ func NewCC(savedSTR *DirSTR, useTBs bool, signKey sign.PublicKey) *ConsistencyCh if !useTBs { panic("[coniks] Currently the server is forced to use TBs") } - aud := NewAuditor(signKey, savedSTR) + a := NewAuditor(signKey, savedSTR) cc := &ConsistencyChecks{ - aud, - make(map[string][]byte), - useTBs, - nil, + AudState: a, + Bindings: make(map[string][]byte), + useTBs: useTBs, + TBs: nil, } if useTBs { cc.TBs = make(map[string]*TemporaryBinding) From 8fbc629719070b92a87a93f3acf7cae00bb5f9cb Mon Sep 17 00:00:00 2001 From: Marcela Melara Date: Fri, 18 Aug 2017 10:51:06 -0400 Subject: [PATCH 41/43] Minor fixes --- protocol/auditor.go | 13 ++++++------- protocol/consistencychecks.go | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/protocol/auditor.go b/protocol/auditor.go index 9fb0409..a5ff938 100644 --- a/protocol/auditor.go +++ b/protocol/auditor.go @@ -140,6 +140,12 @@ func (a *AudState) verifySTRRange(prevSTR *DirSTR, strs []*DirSTR) error { // AuditDirectory() returns the appropriate consistency check error // if any of the checks fail, or nil if the checks pass. func (a *AudState) AuditDirectory(strs []*DirSTR) error { + + // validate strs + if strs == nil { + return ErrMalformedDirectoryMessage + } + // check STR against the latest verified STR if err := a.checkSTRAgainstVerified(strs[0]); err != nil { return err @@ -167,10 +173,3 @@ func ComputeDirectoryIdentity(str *DirSTR) [crypto.HashSizeByte]byte { copy(initSTRHash[:], crypto.Digest(str.Signature)) return initSTRHash } - -// LatestSTRInRange returns the STR in the last element of a -// range of STRs. The range is assumed to be sorted -// by increasing order of STR epoch. -func LatestSTRInRange(strs []*DirSTR) *DirSTR { - return strs[len(strs)-1] -} diff --git a/protocol/consistencychecks.go b/protocol/consistencychecks.go index 5ff0fcd..b0ee106 100644 --- a/protocol/consistencychecks.go +++ b/protocol/consistencychecks.go @@ -82,7 +82,7 @@ func (cc *ConsistencyChecks) CheckEquivocation(msg *Response) error { // TODO: if the auditor has returned a more recent STR, // should the client update its savedSTR? Should this // force a new round of monitoring? - return cc.checkSTRAgainstVerified(LatestSTRInRange(strs.STR)) + return cc.checkSTRAgainstVerified(strs.STR[len(strs.STR)-1]) } // HandleResponse verifies the directory's response for a request. From e27b75037e49f9e8d440a37749b5c710437a4c12 Mon Sep 17 00:00:00 2001 From: huyvq Date: Sat, 19 Aug 2017 10:07:11 +0700 Subject: [PATCH 42/43] Minor fix --- protocol/auditor.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/protocol/auditor.go b/protocol/auditor.go index a5ff938..4d10dde 100644 --- a/protocol/auditor.go +++ b/protocol/auditor.go @@ -140,9 +140,8 @@ func (a *AudState) verifySTRRange(prevSTR *DirSTR, strs []*DirSTR) error { // AuditDirectory() returns the appropriate consistency check error // if any of the checks fail, or nil if the checks pass. func (a *AudState) AuditDirectory(strs []*DirSTR) error { - // validate strs - if strs == nil { + if len(strs) == 0 { return ErrMalformedDirectoryMessage } From c59b570551cd14d0f2004b81eb96d30501f57c56 Mon Sep 17 00:00:00 2001 From: Vu Quoc Huy Date: Fri, 25 Aug 2017 09:14:36 +0700 Subject: [PATCH 43/43] Typo --- protocol/auditlog.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/protocol/auditlog.go b/protocol/auditlog.go index 5d86897..b7454d0 100644 --- a/protocol/auditlog.go +++ b/protocol/auditlog.go @@ -16,7 +16,7 @@ type directoryHistory struct { snapshots map[uint64]*DirSTR } -// caller validates that initSTR is for epoch 0 +// caller validates that initSTR is for epoch 0. func newDirectoryHistory(addr string, signKey sign.PublicKey, initSTR *DirSTR) *directoryHistory { a := NewAuditor(signKey, initSTR) h := &directoryHistory{ @@ -38,15 +38,15 @@ func newDirectoryHistory(addr string, signKey sign.PublicKey, initSTR *DirSTR) * // chronological order. type ConiksAuditLog map[[crypto.HashSizeByte]byte]*directoryHistory -// updateVerifiedSTR inserts a new range of STRs into a directory history; -// assumes the STRs have been validated by the caller +// updateVerifiedSTR inserts the latest verified STR into a directory history; +// assumes the STRs have been validated by the caller. func (h *directoryHistory) updateVerifiedSTR(newVerified *DirSTR) { h.Update(newVerified) h.snapshots[newVerified.Epoch] = newVerified } // Audit checks that a directory's STR history -// is linear and updates the audtor's state +// is linear and updates the auditor's state // if the checks pass. // Audit() first checks the oldest STR in the // STR range received in message against the h.verfiedSTR, @@ -148,7 +148,7 @@ func (l ConiksAuditLog) Insert(addr string, signKey sign.PublicKey, // entry for addr exists in the audit log l, and that the caller // has called Audit() on newSTR before calling Update(). // Update() returns ErrAuditLog if the audit log doesn't contain an -// entry for dirInitHash +// entry for dirInitHash. // FIXME: pass Response message as param func (l ConiksAuditLog) Update(dirInitHash [crypto.HashSizeByte]byte, newSTR *DirSTR) error { // error if we want to update the entry for an addr we don't know