Skip to content

Commit

Permalink
Implement monitoring verification
Browse files Browse the repository at this point in the history
* Refactor common STR verification functionality

* Close #144
  • Loading branch information
vqhuy committed Apr 6, 2017
1 parent 77350b6 commit 1c07a42
Show file tree
Hide file tree
Showing 5 changed files with 273 additions and 52 deletions.
2 changes: 1 addition & 1 deletion client/coniksclient/internal/cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func init() {
func run(cmd *cobra.Command) {
isDebugging, _ := strconv.ParseBool(cmd.Flag("debug").Value.String())
conf := loadConfigOrExit(cmd)
cc := p.NewCC(nil, true, conf.SigningPubKey)
cc := p.NewCC(nil, conf.SigningPubKey, make(map[string]uint64), true)

state, err := terminal.MakeRaw(int(os.Stdin.Fd()))
if err != nil {
Expand Down
55 changes: 55 additions & 0 deletions protocol/auditor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// 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 (
"reflect"

"github.com/coniks-sys/coniks-go/crypto/sign"
)

type auditorState struct {
// SavedSTR stores the latest verified signed tree root.
SavedSTR *DirSTR
signKey sign.PublicKey
}

// NewAuditorState creates an auditor state for a specific directory.
func newAuditorState(signKey sign.PublicKey, saved *DirSTR) *auditorState {
return &auditorState{
SavedSTR: saved,
signKey: signKey,
}
}

// 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 *DirSTR) error {
if reflect.DeepEqual(a.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 its consistency state,
// or an auditor's pinned signing key in its history.
func (a *auditorState) verifySTRConsistency(savedSTR, str *DirSTR) error {
// verify STR's signature
if !a.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
}
124 changes: 80 additions & 44 deletions protocol/consistencychecks.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ package protocol

import (
"bytes"
"reflect"

"github.com/coniks-sys/coniks-go/crypto/sign"
m "github.com/coniks-sys/coniks-go/merkletree"
Expand All @@ -25,30 +24,31 @@ import (
// subsequent responses from the ConiksDirectory to any
// client request.
type ConsistencyChecks struct {
// SavedSTR stores the latest verified signed tree root.
SavedSTR *DirSTR
*auditorState
// Bindings stores all the verified name-to-key bindings.
Bindings map[string][]byte
// RegEpoch keeps the registration epoch of each user.
// Entries are kept even the binding was inserted into the directory.
RegEpoch map[string]uint64

// extensions settings
useTBs bool
TBs map[string]*TemporaryBinding

signKey sign.PublicKey
}

// NewCC creates an instance of ConsistencyChecks using
// a CONIKS directory's pinned STR at epoch 0, or
// the consistency state read from persistent storage.
func NewCC(savedSTR *DirSTR, useTBs bool, signKey sign.PublicKey) *ConsistencyChecks {
func NewCC(savedSTR *DirSTR, signKey sign.PublicKey, regs map[string]uint64, useTBs bool) *ConsistencyChecks {
// TODO: see #110
if !useTBs {
panic("[coniks] Currently the server is forced to use TBs")
}
cc := &ConsistencyChecks{
SavedSTR: savedSTR,
Bindings: make(map[string][]byte),
useTBs: useTBs,
signKey: signKey,
auditorState: newAuditorState(signKey, savedSTR),
RegEpoch: regs,
Bindings: make(map[string][]byte),
useTBs: useTBs,
}
if useTBs {
cc.TBs = make(map[string]*TemporaryBinding)
Expand Down Expand Up @@ -79,8 +79,8 @@ func (cc *ConsistencyChecks) HandleResponse(requestType int, msg *Response,
if _, ok := msg.DirectoryResponse.(*DirectoryProof); !ok {
return ErrMalformedDirectoryMessage
}
case MonitoringType, KeyLookupInEpochType:
if _, ok := msg.DirectoryResponse.(*DirectoryProofs); !ok {
case MonitoringType:
if _, ok := msg.DirectoryResponse.(*DirectoryProofs); !ok || msg.Error != ReqSuccess {
return ErrMalformedDirectoryMessage
}
default:
Expand Down Expand Up @@ -118,9 +118,33 @@ func (cc *ConsistencyChecks) updateSTR(requestType int, msg *Response) error {
}
// Otherwise, expect that we've entered a new epoch
if err := cc.verifySTRConsistency(cc.SavedSTR, str); err != nil {
// FIXME: we are returning the error immediately
// without saving the inconsistent STR
// see: https://github.com/coniks-sys/coniks-go/pull/74#commitcomment-19804686
return err
}

case MonitoringType:
strs := msg.DirectoryResponse.(*DirectoryProofs).STR
switch {
case strs[0].Epoch == cc.SavedSTR.Epoch:
if err := cc.verifySTR(strs[0]); err != nil {
return err
}
case strs[0].Epoch == cc.SavedSTR.Epoch+1:
if err := cc.verifySTRConsistency(cc.SavedSTR, strs[0]); err != nil {
return err
}
default:
panic("[coniks] Unexpected monitoring epoch")
}

for i := 1; i < len(strs); i++ {
if err := cc.verifySTRConsistency(strs[i-1], strs[i]); err != nil {
return err
}
}
str = strs[len(strs)-1]
default:
panic("[coniks] Unknown request type")
}
Expand All @@ -130,31 +154,6 @@ func (cc *ConsistencyChecks) updateSTR(requestType int, msg *Response) error {
return nil
}

// verifySTR checks whether the received STR is the same with
// the SavedSTR using reflect.DeepEqual().
func (cc *ConsistencyChecks) verifySTR(str *DirSTR) error {
if reflect.DeepEqual(cc.SavedSTR, str) {
return nil
}
return CheckBadSTR
}

// 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 {
// verify STR's signature
if !cc.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
Expand All @@ -163,6 +162,8 @@ func (cc *ConsistencyChecks) checkConsistency(requestType int, msg *Response,
err = cc.verifyRegistration(msg, uname, key)
case KeyLookupType:
err = cc.verifyKeyLookup(msg, uname, key)
case MonitoringType:
err = cc.verifyMonitoring(msg, uname, key)
default:
panic("[coniks] Unknown request type")
}
Expand Down Expand Up @@ -214,6 +215,28 @@ func (cc *ConsistencyChecks) verifyKeyLookup(msg *Response,
return CheckPassed
}

func (cc *ConsistencyChecks) verifyMonitoring(msg *Response,
uname string, key []byte) error {
if _, ok := cc.RegEpoch[uname]; !ok {
// TODO: panic for now since we haven't supported #106 yet
panic("[coniks] Cannot perform monitoring for unregistered user name ")
}

dfs := msg.DirectoryResponse.(*DirectoryProofs)
for i := 0; i < len(dfs.STR); i++ {
str := dfs.STR[i]
ap := dfs.AP[i]
if ap.ProofType() != m.ProofOfInclusion {
return CheckBadAuthPath
}
if err := verifyAuthPath(uname, key, ap, str); err != nil {
return err
}
}

return CheckPassed
}

func verifyAuthPath(uname string, key []byte, ap *m.AuthenticationPath, str *DirSTR) error {
// verify VRF Index
vrfKey := str.Policies.VrfPublicKey
Expand Down Expand Up @@ -255,7 +278,7 @@ func (cc *ConsistencyChecks) updateTBs(requestType int, msg *Response,
if err := cc.verifyReturnedPromise(df, key); err != nil {
return err
}
cc.TBs[uname] = df.TB
cc.savePromise(uname, df.TB)
}
return nil

Expand All @@ -275,24 +298,37 @@ func (cc *ConsistencyChecks) updateTBs(requestType int, msg *Response,
if err := cc.verifyReturnedPromise(df, key); err != nil {
return err
}
cc.TBs[uname] = df.TB
cc.savePromise(uname, df.TB)
}

case MonitoringType:
dfs := msg.DirectoryResponse.(*DirectoryProofs)
if err := cc.verifyFulfilledPromise(uname, dfs.STR[0], dfs.AP[0]); err != nil {
return err
}
delete(cc.TBs, uname)

default:
panic("[coniks] Unknown request type")
}
return nil
}

func (cc *ConsistencyChecks) savePromise(uname string, tb *TemporaryBinding) {
cc.TBs[uname] = tb
cc.RegEpoch[uname] = cc.SavedSTR.Epoch
}

// verifyFulfilledPromise verifies issued TBs were inserted
// in the directory as promised.
func (cc *ConsistencyChecks) verifyFulfilledPromise(uname string, str *DirSTR,
ap *m.AuthenticationPath) error {
// FIXME: Which epoch did this lookup happen in?
if tb, ok := cc.TBs[uname]; ok {
if !bytes.Equal(ap.LookupIndex, tb.Index) ||
!bytes.Equal(ap.Leaf.Value, tb.Value) {
return CheckBrokenPromise
if regEp, ok := cc.RegEpoch[uname]; ok && str.Epoch == regEp+1 {
if tb, ok := cc.TBs[uname]; ok {
if !bytes.Equal(ap.LookupIndex, tb.Index) ||
!bytes.Equal(ap.Leaf.Value, tb.Value) {
return CheckBrokenPromise
}
}
}
return nil
Expand Down
Loading

0 comments on commit 1c07a42

Please sign in to comment.