Browse Source

Add first pass of ecc checksuming

pull/1782/head
Ethan Frey 7 years ago
parent
commit
55a25f0f62
2 changed files with 108 additions and 0 deletions
  1. +56
    -0
      keys/ecc.go
  2. +52
    -0
      keys/ecc_test.go

+ 56
- 0
keys/ecc.go View File

@ -0,0 +1,56 @@
package keys
import (
"encoding/binary"
"errors"
"hash/crc32"
)
// ECC is used for anything that calculates an error-correcting code
type ECC interface {
// AddECC calculates an error-correcting code for the input
// returns an output with the code appended
AddECC([]byte) []byte
// CheckECC verifies if the ECC is proper on the input and returns
// the data with the code removed, or an error
CheckECC([]byte) ([]byte, error)
}
// NoECC is a no-op placeholder, kind of useless... except for tests
type NoECC struct{}
var _ ECC = NoECC{}
func (_ NoECC) AddECC(input []byte) []byte { return input }
func (_ NoECC) CheckECC(input []byte) ([]byte, error) { return input, nil }
// CRC32 does the ieee crc32 polynomial check
type CRC32 struct{}
var _ ECC = CRC32{}
func (_ CRC32) AddECC(input []byte) []byte {
// get crc and convert to some bytes...
crc := crc32.ChecksumIEEE(input)
check := make([]byte, 4)
binary.BigEndian.PutUint32(check, crc)
// append it to the input
output := append(input, check...)
return output
}
func (_ CRC32) CheckECC(input []byte) ([]byte, error) {
if len(input) <= 4 {
return nil, errors.New("input too short, no checksum present")
}
cut := len(input) - 4
data, check := input[:cut], input[cut:]
crc := binary.BigEndian.Uint32(check)
calc := crc32.ChecksumIEEE(data)
if crc != calc {
return nil, errors.New("Checksum does not match")
}
return data, nil
}

+ 52
- 0
keys/ecc_test.go View File

@ -0,0 +1,52 @@
package keys
import (
"testing"
"github.com/stretchr/testify/assert"
cmn "github.com/tendermint/tmlibs/common"
)
// TestECCPasses makes sure that the AddECC/CheckECC methods are symetric
func TestECCPasses(t *testing.T) {
assert := assert.New(t)
checks := []ECC{NoECC{}, CRC32{}}
for _, check := range checks {
for i := 0; i < 2000; i++ {
numBytes := cmn.RandInt()%60 + 1
data := cmn.RandBytes(numBytes)
checked := check.AddECC(data)
res, err := check.CheckECC(checked)
if assert.Nil(err, "%v: %+v", check, err) {
assert.Equal(data, res, "%v", check)
}
}
}
}
// TestECCFails makes sure random data will (usually) fail the checksum
func TestECCFails(t *testing.T) {
assert := assert.New(t)
checks := []ECC{CRC32{}}
attempts := 2000
for _, check := range checks {
failed := 0
for i := 0; i < attempts; i++ {
numBytes := cmn.RandInt()%60 + 1
data := cmn.RandBytes(numBytes)
_, err := check.CheckECC(data)
if err != nil {
failed += 1
}
}
// we allow up to 1 falsely accepted checksums, as there are random matches
assert.InDelta(attempts, failed, 1, "%v", check)
}
}

Loading…
Cancel
Save