-
Notifications
You must be signed in to change notification settings - Fork 2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use a hash to detect changes to Certificate resource.
Signed-off-by: Tim Ramlot <42113979+inteon@users.noreply.github.com>
- Loading branch information
Showing
22 changed files
with
1,380 additions
and
360 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
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
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,35 @@ | ||
# infohash | ||
|
||
Am extendable hash & comparator that tells you why the hash did not match. | ||
|
||
For example if you edit the Name field in a struct, this library will tell | ||
you that the hash did not match because the Name field was changed. | ||
|
||
The hash is extendable, so you can add fields to the struct without breaking | ||
the hash. | ||
|
||
## Space Complexity | ||
|
||
Per struct, we store a "global" hash and a "local" hash for each field. | ||
The global hash is 64 bits long, and the local hashes are 32 bits long. | ||
We use hamming codes to detect which "local" hash has changed. This requires | ||
us to store log2(nr_fields+1) 32 bit parity hashes instead of nr_fields 32 bit | ||
hashes. To store the number of fields, we use 16 bits. The total space complexity | ||
is therefore: | ||
|
||
``` | ||
(16 + 64 + log2(nr_fields+1) * 32) bits | ||
= (10 + log2(nr_fields+1) * 4) bytes | ||
= (20 + log2(nr_fields+1) * 8) hex characters | ||
``` | ||
|
||
As a rule of thumb, this library is only useful if you have at least +-7 fields, | ||
otherwise you can just store the hashes of each field individually. | ||
|
||
## Safety | ||
|
||
This library will detect any changes to the struct (with collision chance of 1/2^32, accounting for birthday attack). | ||
However, accurately detecting which field changed is only possible if only one field changed. | ||
If multiple fields changed, we can only detect that at least one field changed, but not which one. | ||
This is because we use hamming codes to detect which field changed, and hamming codes can only detect | ||
single field errors. |
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,30 @@ | ||
package infohash | ||
|
||
import "fmt" | ||
|
||
// ErrInvalidHash is returned by Compare if the hash is invalid. | ||
var ErrInvalidHash = fmt.Errorf("invalid hash") | ||
|
||
// FieldChangedError is returned by Compare if the hashes indicate | ||
// that the fields have changed. | ||
type FieldChangedError struct { | ||
// Change contains the field that changed and its new value. | ||
// If we do know which field changed (eg. because there were | ||
// multiple changes), Change will be nil. | ||
Change *FieldChange | ||
} | ||
|
||
type FieldChange struct { | ||
FieldName string | ||
NewValue interface{} | ||
} | ||
|
||
func (e FieldChangedError) Error() string { | ||
if e.Change == nil { | ||
return "field changed" | ||
} | ||
|
||
newValue := prettyPrintConfigForHash.Sprintf("%#v", e.Change.NewValue) | ||
|
||
return "field changed: " + e.Change.FieldName + " to " + newValue | ||
} |
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,47 @@ | ||
package infohash | ||
|
||
func calculateParityCodes(values []uint32) []uint32 { | ||
log2NumberOfFieldsPlusOne := log2OfXPlusOne(uint32(len(values))) | ||
parityCodes := make([]uint32, log2NumberOfFieldsPlusOne) | ||
|
||
for id, field := range values { | ||
for i := range parityCodes { | ||
if (id+1)&(1<<i) != 0 { | ||
parityCodes[i] ^= field | ||
} | ||
} | ||
} | ||
|
||
return parityCodes | ||
} | ||
|
||
func findErrorLocation(values []uint32, parityCodes []uint32) (int, error) { | ||
expectedCode := calculateParityCodes(values) | ||
var errorLocation uint32 | ||
|
||
if len(parityCodes) != len(expectedCode) { | ||
return 0, ErrInvalidHash | ||
} | ||
|
||
for i := range parityCodes { | ||
if parityCodes[i] != expectedCode[i] { | ||
errorLocation |= 1 << i | ||
} | ||
} | ||
|
||
if errorLocation == 0 || errorLocation > uint32(len(values)) { | ||
// Could not find the error location (there is probably more than one error) | ||
return -1, nil | ||
} | ||
|
||
return int(errorLocation - 1), nil | ||
} | ||
|
||
func log2OfXPlusOne(x uint32) uint32 { | ||
var r uint32 | ||
for x > 0 { | ||
x >>= 1 | ||
r += 1 | ||
} | ||
return r | ||
} |
Oops, something went wrong.