-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor common STR verification functionality (#170)
* Add client-auditor messages * Add client-auditor messages * Create audit log structure, query API finished * Add/Update docs to include auditor, add ReqUnknownDirectory auditor error * Use single generic verifySTRConsistency to be used by client and auditor * Add tests for audit log, debug audit log * Add assertions to validate auditor messages on client * Add generic STR response handler * Add TODO to move all generic STR auditing code to a separate module * [WIP] Refactor generic auditing functionality * Add client-auditor messages * Create audit log structure, query API finished * Add/Update docs to include auditor, add ReqUnknownDirectory auditor error * Use single generic verifySTRConsistency to be used by client and auditor * Add tests for audit log, debug audit log * Add assertions to validate auditor messages on client * Add generic STR response handler * # 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 * Fix documentation * Use DirSTR instead of merkletree.SignedTreeRoot in auditlog * Remove all references to auditor-directory communication, make auditor response message generic * STRList -> STRHistoryRange * Fail sooner in GetObservedSTRs * Revert changes to protocol/str.go * Always request epoch range in AuditingRequest, fix Insert() bug * Index audit log by hash of directory's initial STR * Insert latest STR into snapshot map right away * Fix go vet error * Change audit log index from byte array to string * Add test case for getting an STR after an audit log update * Refactor common functions * Create generic auditor interface * Fix go fmt * Fix some merging issues * Refactor generic auditing functionality TODO: * Add tests * Renaming STR verification functions, use generic auditor functionality in auditlog and consistencychecks * h.verifiedSTR -> h.VerifiedSTR() * Refactor common STR verification for STR ranges from server * Fix * Fix documentation and formatting, revert to use map for auditlog snapshots * Minor fixes * Minor fix * Typo * Part of #151
- Loading branch information
Showing
5 changed files
with
266 additions
and
103 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
// 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 ( | ||
"fmt" | ||
"reflect" | ||
|
||
"github.com/coniks-sys/coniks-go/crypto" | ||
"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 | ||
} | ||
|
||
var _ Auditor = (*AudState)(nil) | ||
|
||
// NewAuditor 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 | ||
} | ||
|
||
// VerifiedSTR returns the newly verified STR. | ||
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 { | ||
if reflect.DeepEqual(a.verifiedSTR, 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 *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 | ||
// 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 | ||
|
||
// 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 | ||
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 | ||
} | ||
|
||
// 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] | ||
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 | ||
if err := a.verifySTRConsistency(prev, str); err != nil { | ||
return err | ||
} | ||
|
||
prev = str | ||
} | ||
|
||
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 { | ||
// validate strs | ||
if len(strs) == 0 { | ||
return ErrMalformedDirectoryMessage | ||
} | ||
|
||
// 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). | ||
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 | ||
} |
File renamed without changes.
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.