[#44] checksum: move package from neofs-api-go

Also remove `Checksum` from functions and constants names.

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
Evgenii Stratonikov 2021-10-27 10:44:59 +03:00 committed by Alex Vanin
parent ecc3d0fcf1
commit bd554c94d8
2 changed files with 356 additions and 0 deletions

181
checksum/checksum.go Normal file
View file

@ -0,0 +1,181 @@
package checksum
import (
"bytes"
"crypto/sha256"
"encoding/hex"
"fmt"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
)
// Checksum represents v2-compatible checksum.
type Checksum refs.Checksum
// Type represents the enumeration
// of checksum types.
type Type uint8
const (
// Unknown is an undefined checksum type.
Unknown Type = iota
// SHA256 is a SHA256 checksum type.
SHA256
// TZ is a Tillich-Zemor checksum type.
TZ
)
// NewFromV2 wraps v2 Checksum message to Checksum.
//
// Nil refs.Checksum converts to nil.
func NewFromV2(cV2 *refs.Checksum) *Checksum {
return (*Checksum)(cV2)
}
// New creates and initializes blank Checksum.
//
// Defaults:
// - sum: nil;
// - type: Unknown.
func New() *Checksum {
return NewFromV2(new(refs.Checksum))
}
// Type returns checksum type.
func (c *Checksum) Type() Type {
switch (*refs.Checksum)(c).GetType() {
case refs.SHA256:
return SHA256
case refs.TillichZemor:
return TZ
default:
return Unknown
}
}
// Sum returns checksum bytes.
func (c *Checksum) Sum() []byte {
return (*refs.Checksum)(c).GetSum()
}
// SetSHA256 sets checksum to SHA256 hash.
func (c *Checksum) SetSHA256(v [sha256.Size]byte) {
checksum := (*refs.Checksum)(c)
checksum.SetType(refs.SHA256)
checksum.SetSum(v[:])
}
// SetTillichZemor sets checksum to Tillich-Zemor hash.
func (c *Checksum) SetTillichZemor(v [64]byte) {
checksum := (*refs.Checksum)(c)
checksum.SetType(refs.TillichZemor)
checksum.SetSum(v[:])
}
// ToV2 converts Checksum to v2 Checksum message.
//
// Nil Checksum converts to nil.
func (c *Checksum) ToV2() *refs.Checksum {
return (*refs.Checksum)(c)
}
func Equal(cs1, cs2 *Checksum) bool {
return cs1.Type() == cs2.Type() && bytes.Equal(cs1.Sum(), cs2.Sum())
}
// Marshal marshals Checksum into a protobuf binary form.
func (c *Checksum) Marshal() ([]byte, error) {
return (*refs.Checksum)(c).StableMarshal(nil)
}
// Unmarshal unmarshals protobuf binary representation of Checksum.
func (c *Checksum) Unmarshal(data []byte) error {
return (*refs.Checksum)(c).Unmarshal(data)
}
// MarshalJSON encodes Checksum to protobuf JSON format.
func (c *Checksum) MarshalJSON() ([]byte, error) {
return (*refs.Checksum)(c).MarshalJSON()
}
// UnmarshalJSON decodes Checksum from protobuf JSON format.
func (c *Checksum) UnmarshalJSON(data []byte) error {
return (*refs.Checksum)(c).UnmarshalJSON(data)
}
func (c *Checksum) String() string {
return hex.EncodeToString((*refs.Checksum)(c).GetSum())
}
// Parse parses Checksum from its string representation.
func (c *Checksum) Parse(s string) error {
data, err := hex.DecodeString(s)
if err != nil {
return err
}
var typ refs.ChecksumType
switch ln := len(data); ln {
default:
return fmt.Errorf("unsupported checksum length %d", ln)
case sha256.Size:
typ = refs.SHA256
case 64:
typ = refs.TillichZemor
}
cV2 := (*refs.Checksum)(c)
cV2.SetType(typ)
cV2.SetSum(data)
return nil
}
// String returns string representation of Type.
//
// String mapping:
// * TZ: TZ;
// * SHA256: SHA256;
// * Unknown, default: CHECKSUM_TYPE_UNSPECIFIED.
func (m Type) String() string {
var m2 refs.ChecksumType
switch m {
default:
m2 = refs.UnknownChecksum
case TZ:
m2 = refs.TillichZemor
case SHA256:
m2 = refs.SHA256
}
return m2.String()
}
// FromString parses Type from a string representation.
// It is a reverse action to String().
//
// Returns true if s was parsed successfully.
func (m *Type) FromString(s string) bool {
var g refs.ChecksumType
ok := g.FromString(s)
if ok {
switch g {
default:
*m = Unknown
case refs.TillichZemor:
*m = TZ
case refs.SHA256:
*m = SHA256
}
}
return ok
}

175
checksum/checksum_test.go Normal file
View file

@ -0,0 +1,175 @@
package checksum
import (
"crypto/rand"
"crypto/sha256"
"testing"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
"github.com/stretchr/testify/require"
)
func randSHA256(t *testing.T) [sha256.Size]byte {
cSHA256 := [sha256.Size]byte{}
_, err := rand.Read(cSHA256[:])
require.NoError(t, err)
return cSHA256
}
func TestChecksum(t *testing.T) {
c := New()
cSHA256 := [sha256.Size]byte{}
_, _ = rand.Read(cSHA256[:])
c.SetSHA256(cSHA256)
require.Equal(t, SHA256, c.Type())
require.Equal(t, cSHA256[:], c.Sum())
cV2 := c.ToV2()
require.Equal(t, refs.SHA256, cV2.GetType())
require.Equal(t, cSHA256[:], cV2.GetSum())
cTZ := [64]byte{}
_, _ = rand.Read(cSHA256[:])
c.SetTillichZemor(cTZ)
require.Equal(t, TZ, c.Type())
require.Equal(t, cTZ[:], c.Sum())
cV2 = c.ToV2()
require.Equal(t, refs.TillichZemor, cV2.GetType())
require.Equal(t, cTZ[:], cV2.GetSum())
}
func TestEqualChecksums(t *testing.T) {
require.True(t, Equal(nil, nil))
csSHA := [sha256.Size]byte{}
_, _ = rand.Read(csSHA[:])
cs1 := New()
cs1.SetSHA256(csSHA)
cs2 := New()
cs2.SetSHA256(csSHA)
require.True(t, Equal(cs1, cs2))
csSHA[0]++
cs2.SetSHA256(csSHA)
require.False(t, Equal(cs1, cs2))
}
func TestChecksumEncoding(t *testing.T) {
cs := New()
cs.SetSHA256(randSHA256(t))
t.Run("binary", func(t *testing.T) {
data, err := cs.Marshal()
require.NoError(t, err)
c2 := New()
require.NoError(t, c2.Unmarshal(data))
require.Equal(t, cs, c2)
})
t.Run("json", func(t *testing.T) {
data, err := cs.MarshalJSON()
require.NoError(t, err)
cs2 := New()
require.NoError(t, cs2.UnmarshalJSON(data))
require.Equal(t, cs, cs2)
})
t.Run("string", func(t *testing.T) {
cs2 := New()
require.NoError(t, cs2.Parse(cs.String()))
require.Equal(t, cs, cs2)
})
}
func TestNewChecksumFromV2(t *testing.T) {
t.Run("from nil", func(t *testing.T) {
var x *refs.Checksum
require.Nil(t, NewFromV2(x))
})
}
func TestChecksum_ToV2(t *testing.T) {
t.Run("nil", func(t *testing.T) {
var x *Checksum
require.Nil(t, x.ToV2())
})
}
func TestNewChecksum(t *testing.T) {
t.Run("default values", func(t *testing.T) {
chs := New()
// check initial values
require.Equal(t, Unknown, chs.Type())
require.Nil(t, chs.Sum())
// convert to v2 message
chsV2 := chs.ToV2()
require.Equal(t, refs.UnknownChecksum, chsV2.GetType())
require.Nil(t, chsV2.GetSum())
})
}
type enumIface interface {
FromString(string) bool
String() string
}
type enumStringItem struct {
val enumIface
str string
}
func testEnumStrings(t *testing.T, e enumIface, items []enumStringItem) {
for _, item := range items {
require.Equal(t, item.str, item.val.String())
s := item.val.String()
require.True(t, e.FromString(s), s)
require.EqualValues(t, item.val, e, item.val)
}
// incorrect strings
for _, str := range []string{
"some string",
"undefined",
} {
require.False(t, e.FromString(str))
}
}
func TestChecksumType_String(t *testing.T) {
toPtr := func(v Type) *Type {
return &v
}
testEnumStrings(t, new(Type), []enumStringItem{
{val: toPtr(TZ), str: "TZ"},
{val: toPtr(SHA256), str: "SHA256"},
{val: toPtr(Unknown), str: "CHECKSUM_TYPE_UNSPECIFIED"},
})
}