-
Notifications
You must be signed in to change notification settings - Fork 2
Add support for evidence #16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 |
|---|---|---|
|
|
@@ -21,11 +21,8 @@ import ( | |
| "golang.org/x/crypto/cryptobyte" | ||
| ) | ||
|
|
||
| const csLen = 32 | ||
|
|
||
| var ( | ||
| ErrChecksumInvalid = errors.New("Invalid checksum") | ||
| ErrClosed = errors.New("Handle is closed") | ||
| ErrClosed = errors.New("Handle is closed") | ||
| ) | ||
|
|
||
| type NewOpts struct { | ||
|
|
@@ -50,76 +47,12 @@ type Handle struct { | |
|
|
||
| indices map[uint32]*Index | ||
| aas map[uint32]*os.File | ||
| evs map[uint32]*os.File | ||
| trees map[uint32]*Tree | ||
|
|
||
| batchNumbersCache []uint32 // cache for existing batches | ||
| } | ||
|
|
||
| type QueuedAssertion struct { | ||
| Checksum []byte | ||
| Assertion mtc.Assertion | ||
| } | ||
|
|
||
| func (a *QueuedAssertion) UnmarshalBinary(data []byte) error { | ||
| var ( | ||
| s cryptobyte.String = cryptobyte.String(data) | ||
| checksum [csLen]byte | ||
| ) | ||
| if !s.CopyBytes(checksum[:]) { | ||
| return mtc.ErrTruncated | ||
| } | ||
|
|
||
| a.Checksum = make([]byte, csLen) | ||
| copy(a.Checksum, checksum[:]) | ||
|
|
||
| checksum2 := sha256.Sum256([]byte(s)) | ||
| if !bytes.Equal(checksum2[:], checksum[:]) { | ||
| return ErrChecksumInvalid | ||
| } | ||
|
|
||
| if err := a.Assertion.UnmarshalBinary([]byte(s)); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| func (a *QueuedAssertion) marshalAndCheckAssertion() ([]byte, error) { | ||
| buf, err := a.Assertion.MarshalBinary() | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| checksum2 := sha256.Sum256([]byte(buf)) | ||
| if a.Checksum == nil { | ||
| a.Checksum = checksum2[:] | ||
| } else if !bytes.Equal(checksum2[:], a.Checksum) { | ||
| return nil, ErrChecksumInvalid | ||
| } | ||
|
|
||
| return buf, nil | ||
| } | ||
|
|
||
| // If set, checks whether the Checksum is correct. If not set, sets the | ||
| // Checksum to the correct value. | ||
| func (a *QueuedAssertion) Check() error { | ||
| _, err := a.marshalAndCheckAssertion() | ||
| return err | ||
| } | ||
|
|
||
| func (a *QueuedAssertion) MarshalBinary() ([]byte, error) { | ||
| var b cryptobyte.Builder | ||
|
|
||
| buf, err := a.marshalAndCheckAssertion() | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| b.AddBytes(a.Checksum) | ||
| b.AddBytes(buf) | ||
|
|
||
| return b.Bytes() | ||
| } | ||
|
|
||
| func (ca *Handle) Params() mtc.CAParams { | ||
| return ca.params | ||
| } | ||
|
|
@@ -137,6 +70,10 @@ func (ca *Handle) Close() error { | |
| r.Close() | ||
| } | ||
|
|
||
| for _, r := range ca.evs { | ||
| r.Close() | ||
| } | ||
|
|
||
| for _, t := range ca.trees { | ||
| t.Close() | ||
| } | ||
|
|
@@ -165,7 +102,7 @@ func (h *Handle) dropQueue() error { | |
| // | ||
| // For each entry, if checksum is not nil, makes sure the assertion | ||
| // matches the checksum | ||
| func (h *Handle) QueueMultiple(it func(yield func(qa QueuedAssertion) error) error) error { | ||
| func (h *Handle) QueueMultiple(it func(yield func(ar mtc.AssertionRequest) error) error) error { | ||
| if h.closed { | ||
| return ErrClosed | ||
| } | ||
|
|
@@ -177,8 +114,8 @@ func (h *Handle) QueueMultiple(it func(yield func(qa QueuedAssertion) error) err | |
| defer w.Close() | ||
| bw := bufio.NewWriter(w) | ||
|
|
||
| if err := it(func(qa QueuedAssertion) error { | ||
| buf, err := qa.MarshalBinary() | ||
| if err := it(func(ar mtc.AssertionRequest) error { | ||
| buf, err := ar.MarshalBinary() | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
@@ -208,14 +145,9 @@ func (h *Handle) QueueMultiple(it func(yield func(qa QueuedAssertion) error) err | |
| // Queue assertion for publication. | ||
| // | ||
| // If checksum is not nil, makes sure assertion matches the checksum. | ||
| func (h *Handle) Queue(a mtc.Assertion, checksum []byte) error { | ||
| return h.QueueMultiple(func(yield func(qa QueuedAssertion) error) error { | ||
| return yield( | ||
| QueuedAssertion{ | ||
| Checksum: checksum, | ||
| Assertion: a, | ||
| }, | ||
| ) | ||
| func (h *Handle) Queue(ar mtc.AssertionRequest) error { | ||
| return h.QueueMultiple(func(yield func(ar mtc.AssertionRequest) error) error { | ||
| return yield(ar) | ||
| }) | ||
| } | ||
|
|
||
|
|
@@ -227,6 +159,7 @@ func Open(path string) (*Handle, error) { | |
| path: path, | ||
| indices: make(map[uint32]*Index), | ||
| aas: make(map[uint32]*os.File), | ||
| evs: make(map[uint32]*os.File), | ||
| trees: make(map[uint32]*Tree), | ||
| } | ||
| if err := h.lock(); err != nil { | ||
|
|
@@ -282,6 +215,10 @@ func (h Handle) aaPath(number uint32) string { | |
| return gopath.Join(h.batchPath(number), "abridged-assertions") | ||
| } | ||
|
|
||
| func (h Handle) evPath(number uint32) string { | ||
| return gopath.Join(h.batchPath(number), "evidence") | ||
| } | ||
|
|
||
| func (h Handle) batchPath(number uint32) string { | ||
| return gopath.Join(h.batchesPath(), fmt.Sprintf("%d", number)) | ||
| } | ||
|
|
@@ -387,7 +324,7 @@ func (h *Handle) listBatchRange() (mtc.BatchRange, error) { | |
| } | ||
|
|
||
| // Calls f on each assertion queued to be published. | ||
| func (h *Handle) WalkQueue(f func(QueuedAssertion) error) error { | ||
| func (h *Handle) WalkQueue(f func(mtc.AssertionRequest) error) error { | ||
| r, err := os.OpenFile(h.queuePath(), os.O_RDONLY, 0) | ||
| if err != nil { | ||
| return fmt.Errorf("Opening queue: %w", err) | ||
|
|
@@ -400,7 +337,7 @@ func (h *Handle) WalkQueue(f func(QueuedAssertion) error) error { | |
| var ( | ||
| prefix [2]byte | ||
| aLen uint16 | ||
| qa QueuedAssertion | ||
| ar mtc.AssertionRequest | ||
| ) | ||
| _, err := io.ReadFull(br, prefix[:]) | ||
| if err == io.EOF { | ||
|
|
@@ -418,12 +355,12 @@ func (h *Handle) WalkQueue(f func(QueuedAssertion) error) error { | |
| return fmt.Errorf("Reading queue: %w", err) | ||
| } | ||
|
|
||
| err = qa.UnmarshalBinary(buf) | ||
| err = ar.UnmarshalBinary(buf) | ||
| if err != nil { | ||
| return fmt.Errorf("Parsing queue: %w", err) | ||
| } | ||
|
|
||
| err = f(qa) | ||
| err = f(ar) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
@@ -489,6 +426,14 @@ func (h *Handle) closeBatch(batch uint32) error { | |
| delete(h.aas, batch) | ||
| } | ||
|
|
||
| if r, ok := h.evs[batch]; ok { | ||
| err := r.Close() | ||
| if err != nil { | ||
| return fmt.Errorf("closing evidence for %d: %w", batch, err) | ||
| } | ||
| delete(h.evs, batch) | ||
| } | ||
|
|
||
| if r, ok := h.trees[batch]; ok { | ||
| err := r.Close() | ||
| if err != nil { | ||
|
|
@@ -545,10 +490,25 @@ func (ca *Handle) aaFileFor(batch uint32) (*os.File, error) { | |
| return r, nil | ||
| } | ||
|
|
||
| // Returns file handle to evidence file for the given batch. | ||
| func (ca *Handle) evFileFor(batch uint32) (*os.File, error) { | ||
| if r, ok := ca.evs[batch]; ok { | ||
| return r, nil | ||
| } | ||
|
|
||
| r, err := os.Open(ca.evPath(batch)) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| return r, nil | ||
| } | ||
|
|
||
| type keySearchResult struct { | ||
| Batch uint32 | ||
| SequenceNumber uint64 | ||
| Offset uint64 | ||
| EvidenceOffset uint64 | ||
| } | ||
|
|
||
| // Returns the certificate for an issued assertion | ||
|
|
@@ -589,7 +549,46 @@ func (ca *Handle) CertificateFor(a mtc.Assertion) (*mtc.BikeshedCertificate, err | |
| }, nil | ||
| } | ||
|
|
||
| // Search for AbridgedAssertions's batch/seqno/offset by key. | ||
| var errShortCircuit = errors.New("short circuit") | ||
|
|
||
| // Returns the evidence for an issued assertion | ||
| func (ca *Handle) EvidenceFor(a mtc.Assertion) (*mtc.Evidence, error) { | ||
| aa := a.Abridge() | ||
| var key [mtc.HashLen]byte | ||
| err := aa.Key(key[:]) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| res, err := ca.aaByKey(key[:]) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("searching by key: %w", err) | ||
| } | ||
|
|
||
| if res == nil { | ||
| return nil, fmt.Errorf("no assertion with key %x on record", key) | ||
| } | ||
|
|
||
| var ev *mtc.Evidence | ||
| evFile, err := ca.evFileFor(res.Batch) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| _, err = evFile.Seek(int64(res.EvidenceOffset), 0) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| err = mtc.UnmarshalEvidenceEntries(evFile, func(_ int, ev2 *mtc.Evidence) error { | ||
| ev = ev2 | ||
| return errShortCircuit | ||
| }) | ||
| if err != errShortCircuit { | ||
| return nil, err | ||
| } | ||
|
|
||
| return ev, nil | ||
| } | ||
|
|
||
| // Search for AbridgedAssertions's batch/seqno/offset/evidence_offset by key. | ||
| func (ca *Handle) aaByKey(key []byte) (*keySearchResult, error) { | ||
| batches, err := ca.listBatchRange() | ||
| if err != nil { | ||
|
|
@@ -600,7 +599,7 @@ func (ca *Handle) aaByKey(key []byte) (*keySearchResult, error) { | |
| return nil, nil | ||
| } | ||
|
|
||
| for batch := batches.End - 1; batch <= batches.End; batch-- { | ||
| for batch := batches.End - 1; batch >= batches.Begin && batch <= batches.End; batch-- { | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. bugfix here. Say we have the batches 14-18 on disk. Previously, we'd go the way down to 13 and then throw an error since the files don't exist. |
||
| res, err := ca.aaByKeyIn(batch, key) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("Searching in batch %d: %w", batch, err) | ||
|
|
@@ -610,6 +609,7 @@ func (ca *Handle) aaByKey(key []byte) (*keySearchResult, error) { | |
| Batch: batch, | ||
| SequenceNumber: res.SequenceNumber, | ||
| Offset: res.Offset, | ||
| EvidenceOffset: res.EvidenceOffset, | ||
| }, nil | ||
| } | ||
| } | ||
|
|
@@ -760,6 +760,7 @@ func (h *Handle) issueBatch(number uint32, empty bool) error { | |
| "tree", | ||
| "signed-validity-window", | ||
| "abridged-assertions", | ||
| "evidence", | ||
| "index", | ||
| }, | ||
| ) | ||
|
|
@@ -884,7 +885,7 @@ func (h *Handle) issueBatchTo(dir string, batch mtc.Batch, empty bool) error { | |
| prevHeads = w.ValidityWindow.TreeHeads | ||
| } | ||
|
|
||
| // Read queue and write abridged-assertions | ||
| // Read queue and write abridged-assertions and evidence | ||
| aasPath := gopath.Join(dir, "abridged-assertions") | ||
| aasW, err := os.Create(aasPath) | ||
| if err != nil { | ||
|
|
@@ -893,23 +894,48 @@ func (h *Handle) issueBatchTo(dir string, batch mtc.Batch, empty bool) error { | |
| defer aasW.Close() | ||
| aasBW := bufio.NewWriter(aasW) | ||
|
|
||
| evPath := gopath.Join(dir, "evidence") | ||
| evW, err := os.Create(evPath) | ||
| if err != nil { | ||
| return fmt.Errorf("creating %s: %w", evPath, err) | ||
| } | ||
| defer evW.Close() | ||
| evBW := bufio.NewWriter(evW) | ||
|
|
||
| if !empty { | ||
| err = h.WalkQueue(func(qa QueuedAssertion) error { | ||
| aa := qa.Assertion.Abridge() | ||
| err = h.WalkQueue(func(ar mtc.AssertionRequest) error { | ||
| aa := ar.Assertion.Abridge() | ||
| buf, err := aa.MarshalBinary() | ||
| if err != nil { | ||
| return fmt.Errorf("Marshalling assertion %x: %w", qa.Checksum, err) | ||
| return fmt.Errorf("Marshalling assertion %x: %w", ar.Checksum, err) | ||
| } | ||
|
|
||
| _, err = aasBW.Write(buf) | ||
| if err != nil { | ||
| return fmt.Errorf( | ||
| "Writing assertion %x to %s: %w", | ||
| qa.Checksum, | ||
| ar.Checksum, | ||
| aasPath, | ||
| err, | ||
| ) | ||
| } | ||
|
|
||
| ev := ar.Evidence | ||
| buf, err = ev.MarshalBinary() | ||
| if err != nil { | ||
| return fmt.Errorf("Marshalling evidence %x: %w", ar.Checksum, err) | ||
| } | ||
|
|
||
| _, err = evBW.Write(buf) | ||
| if err != nil { | ||
| return fmt.Errorf( | ||
| "Writing evidence %x to %s: %w", | ||
| ar.Checksum, | ||
| evPath, | ||
| err, | ||
| ) | ||
| } | ||
|
|
||
| return nil | ||
| }) | ||
| if err != nil { | ||
|
|
@@ -932,6 +958,21 @@ func (h *Handle) issueBatchTo(dir string, batch mtc.Batch, empty bool) error { | |
| } | ||
| defer aasR.Close() | ||
|
|
||
| err = evBW.Flush() | ||
| if err != nil { | ||
| return fmt.Errorf("flushing %s: %w", evPath, err) | ||
| } | ||
|
|
||
| err = evW.Close() | ||
| if err != nil { | ||
| return fmt.Errorf("closing %s: %w", evPath, err) | ||
| } | ||
| evR, err := os.OpenFile(evPath, os.O_RDONLY, 0) | ||
| if err != nil { | ||
| return fmt.Errorf("opening %s: %w", evPath, err) | ||
| } | ||
| defer evR.Close() | ||
|
|
||
| // Compute tree | ||
| tree, err := batch.ComputeTree(bufio.NewReader(aasR)) | ||
| if err != nil { | ||
|
|
@@ -970,7 +1011,7 @@ func (h *Handle) issueBatchTo(dir string, batch mtc.Batch, empty bool) error { | |
|
|
||
| defer indexW.Close() | ||
|
|
||
| err = ComputeIndex(aasR, indexW) | ||
| err = ComputeIndex(aasR, evR, indexW) | ||
| if err != nil { | ||
| return fmt.Errorf("computing %s to start: %w", indexPath, err) | ||
| } | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: I added this bit to mirror how the
aasfiles are handled, but it doesn't look like these maps are ever populated.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, yeah, that's a bug in
aaFileForwhich should set an entry.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
aaFileForis currently unused since we just recalculate the abridged assertion key from the passed-in assertion inCertificateFor😁 . We could do an extra check there and make sure we find the abridged assertion where we expect it to be, and that the bytes are equal.