Skip to content

Commit 48ffc1c

Browse files
api: implement namer, generator, validator
Closes TNTP-4190
1 parent c36b4a8 commit 48ffc1c

File tree

6 files changed

+388
-16
lines changed

6 files changed

+388
-16
lines changed

hasher/hasher_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,9 @@ func TestSHA256Hasher(t *testing.T) {
6161
t.Parallel()
6262

6363
tests := []struct {
64-
name string
65-
in []byte
66-
out string
64+
name string
65+
in []byte
66+
expected string
6767
}{
6868
{"empty", []byte(""), "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"},
6969
{"abc", []byte("abc"), "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"},
@@ -77,7 +77,7 @@ func TestSHA256Hasher(t *testing.T) {
7777

7878
result, _ := h.Hash(test.in)
7979

80-
assert.Equal(t, test.out, hex.EncodeToString(result))
80+
assert.Equal(t, test.expected, hex.EncodeToString(result))
8181
})
8282
}
8383
}

marshaller/marshaller.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,24 @@ var ErrUnmarshall = errors.New("failed to unmarshal")
1717
// implements one time for all objects.
1818
// Required for `integrity.Storage` to set marshalling format for any type object
1919
// and as recommendation for developers of `Storage` wrappers.
20-
type DefaultMarshaller interface {
20+
type DefaultMarshaller interface { //nolint:iface
2121
Marshal(data any) ([]byte, error)
2222
Unmarshal(data []byte, out any) error
2323
}
2424

2525
// Marshallable - custom object serialization, implements for each object.
2626
// Required for `integrity.Storage` type to set marshalling format to specific object
2727
// and as recommendation for developers of `Storage` wrappers.
28-
type Marshallable interface {
29-
Marshal() ([]byte, error)
28+
type Marshallable interface { //nolint:iface
29+
Marshal(data any) ([]byte, error)
3030
Unmarshal(data []byte, out any) error
3131
}
3232

3333
// YAMLMarshaller struct represent realization.
3434
type YAMLMarshaller struct{}
3535

3636
// NewYamlMarshaller creates new NewYamlMarshaller object.
37-
func NewYamlMarshaller() YAMLMarshaller {
37+
func NewYamlMarshaller() Marshallable {
3838
return YAMLMarshaller{}
3939
}
4040

namer/generatorvalidator.go

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
package namer
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"strings"
7+
8+
"github.com/tarantool/go-storage/crypto"
9+
"github.com/tarantool/go-storage/hasher"
10+
"github.com/tarantool/go-storage/kv"
11+
"github.com/tarantool/go-storage/marshaller"
12+
)
13+
14+
// Generator generates signer K/V pairs.
15+
// Implementation should use `generic` and will used for strong typing of the solution.
16+
type Generator[T any] interface {
17+
Generate(name string, value T) ([]kv.KeyValue, error)
18+
}
19+
20+
// Validator validates and build the object from K/V.
21+
type Validator[T any] interface {
22+
Validate(pairs []kv.KeyValue) (T, error)
23+
}
24+
25+
// DefaultGeneratorValidator represent default generator-validator.
26+
type DefaultGeneratorValidator[T any] struct {
27+
Namer Namer
28+
Hasher hasher.Hasher
29+
SignerVerifier crypto.SignerVerifier
30+
Marshaller marshaller.Marshallable
31+
}
32+
33+
// NewDefaultGeneratorValidator returns new object.
34+
func NewDefaultGeneratorValidator[T any](
35+
namer Namer,
36+
hasher hasher.Hasher,
37+
signverifier crypto.SignerVerifier,
38+
marshaller marshaller.Marshallable,
39+
) DefaultGeneratorValidator[T] {
40+
return DefaultGeneratorValidator[T]{
41+
Namer: namer,
42+
Hasher: hasher,
43+
SignerVerifier: signverifier,
44+
Marshaller: marshaller,
45+
}
46+
}
47+
48+
// Generate create KV pairs with value, hash and signature.
49+
func (gv DefaultGeneratorValidator[T]) Generate(name string, value T) ([]kv.KeyValue, error) {
50+
if name == "" {
51+
return []kv.KeyValue{}, ErrInvalidInput
52+
}
53+
54+
var kvList []kv.KeyValue
55+
56+
blob, err := gv.Marshaller.Marshal(value)
57+
if err != nil {
58+
return nil, fmt.Errorf("failed to marshal: %w", err)
59+
}
60+
61+
hash, err := gv.Hasher.Hash(blob)
62+
if err != nil {
63+
return nil, fmt.Errorf("failed to hash: %w", err)
64+
}
65+
66+
signature, err := gv.SignerVerifier.Sign(hash)
67+
if err != nil {
68+
return nil, fmt.Errorf("failed to sign: %w", err)
69+
}
70+
71+
names := gv.Namer.GenerateNames(name)
72+
keys := gv.Namer.ParseNames(names)
73+
74+
for _, key := range keys {
75+
switch key.Type {
76+
case KeyTypeValue:
77+
{
78+
kvList = append(kvList, kv.KeyValue{
79+
Key: []byte(key.Name),
80+
Value: blob,
81+
ModRevision: 1,
82+
})
83+
}
84+
case KeyTypeHash:
85+
{
86+
kvList = append(kvList, kv.KeyValue{
87+
Key: []byte(key.Name),
88+
Value: hash,
89+
ModRevision: 1,
90+
})
91+
}
92+
case KeyTypeSignature:
93+
{
94+
kvList = append(kvList, kv.KeyValue{
95+
Key: []byte(key.Name),
96+
Value: signature,
97+
ModRevision: 1,
98+
})
99+
}
100+
}
101+
}
102+
103+
return kvList, nil
104+
}
105+
106+
// Validate checks hash match, verify signature, unmarshall object and return it.
107+
func (gv DefaultGeneratorValidator[T]) Validate(pairs []kv.KeyValue) (T, error) {
108+
var value T
109+
110+
var blob []byte
111+
112+
var hash, signature []byte
113+
114+
for _, keyvalue := range pairs {
115+
switch {
116+
case strings.Contains(string(keyvalue.Key), hashName):
117+
hash = keyvalue.Value
118+
case strings.Contains(string(keyvalue.Key), sigName):
119+
signature = keyvalue.Value
120+
default:
121+
blob = keyvalue.Value
122+
}
123+
}
124+
125+
if blob == nil || hash == nil || signature == nil {
126+
return value, ErrInvalidKey
127+
}
128+
129+
err := gv.SignerVerifier.Verify(hash, signature)
130+
if err != nil {
131+
return value, fmt.Errorf("signature verification failed: %w", err)
132+
}
133+
134+
computedHash, err := gv.Hasher.Hash(blob)
135+
if !bytes.Equal(computedHash, hash) || err != nil {
136+
return value, ErrHashMismatch
137+
}
138+
139+
err = gv.Marshaller.Unmarshal(blob, &value)
140+
if err != nil {
141+
return value, fmt.Errorf("failed to unmarshal: %w", err)
142+
}
143+
144+
return value, nil
145+
}

namer/generatorvalidator_test.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package namer_test
2+
3+
import (
4+
"crypto/rand"
5+
"crypto/rsa"
6+
"testing"
7+
8+
"github.com/stretchr/testify/require"
9+
"github.com/tarantool/go-storage/hasher"
10+
"github.com/tarantool/go-storage/kv"
11+
"github.com/tarantool/go-storage/marshaller"
12+
"github.com/tarantool/go-storage/namer"
13+
14+
gscrypto "github.com/tarantool/go-storage/crypto"
15+
)
16+
17+
func TestGeneratorValidator(t *testing.T) {
18+
t.Parallel()
19+
20+
tests := []struct {
21+
prefix string
22+
name string
23+
expected []kv.KeyValue
24+
}{
25+
{
26+
"/tt/config",
27+
"all",
28+
[]kv.KeyValue{
29+
{
30+
Key: []byte("/tt/config/all"),
31+
Value: []byte("value"),
32+
ModRevision: 1,
33+
},
34+
{
35+
Key: []byte("/tt/config/hash/all"),
36+
Value: []byte{
37+
0x1E, 0x1F, 0x2C, 0x88, 0x1A, 0xE0, 0x60, 0x8E, 0xC7, 0x7E, 0xBF, 0x88, 0xA7, 0x5C, 0x66, 0xD3,
38+
0x09, 0x91, 0x13, 0xA7, 0x34, 0x32, 0x38, 0xF2, 0xF7, 0xA0, 0xEB, 0xB9, 0x1A, 0x4E, 0xD3, 0x35,
39+
},
40+
ModRevision: 1,
41+
},
42+
{
43+
Key: []byte("/tt/config/sig/all"),
44+
Value: []byte{
45+
0x2c, 0xd0, 0x10, 0x8d, 0x3b, 0x43, 0x3b, 0x8e, 0x60, 0x22, 0x09, 0x78, 0x86, 0xf2, 0xe1, 0xd1,
46+
0xcd, 0xea, 0xa0, 0x9b, 0xd0, 0x92, 0xb1, 0xc5, 0x90, 0x3a, 0x85, 0xeb, 0xd2, 0xe8, 0x3f, 0x43,
47+
0x6d, 0xe2, 0x08, 0x1d, 0xfb, 0xc6, 0xd9, 0x04, 0x6e, 0x2c, 0x11, 0xa7, 0x6f, 0xcb, 0x27, 0x66,
48+
0x0e, 0x6b, 0xc8, 0xbb, 0xe1, 0x77, 0x33, 0xa3, 0x65, 0x49, 0xbf, 0xc7, 0x6e, 0x18, 0x0e, 0x4d,
49+
0x0f, 0x20, 0x0b, 0x51, 0xf9, 0x50, 0xdf, 0x76, 0x2d, 0x22, 0x0e, 0xa2, 0x1c, 0xa5, 0xdf, 0x49,
50+
0xbd, 0x2b, 0xbd, 0xc7, 0xa6, 0x99, 0x8d, 0x55, 0x3b, 0xf8, 0xf4, 0xa2, 0xbf, 0xda, 0xf8, 0xe9,
51+
0xf7, 0x41, 0xa8, 0xc4, 0xa9, 0x34, 0xef, 0x0b, 0xa4, 0xb5, 0x96, 0x08, 0x60, 0x8c, 0xa1, 0xdb,
52+
0x4e, 0xd0, 0x5a, 0xed, 0x78, 0x3f, 0x0f, 0x9e, 0x74, 0x09, 0xf9, 0x75, 0x07, 0xbb, 0x5b, 0xe8,
53+
0xbe, 0x44, 0xbf, 0x07, 0xf7, 0x9f, 0x26, 0x12, 0xb2, 0x02, 0xbf, 0x96, 0xdf, 0x18, 0x35, 0x46,
54+
0x67, 0x9f, 0x7b, 0xe8, 0xdc, 0xfe, 0x7f, 0xac, 0xad, 0x5b, 0x94, 0x63, 0x0f, 0x1b, 0x03, 0xe6,
55+
0x3f, 0x80, 0xb8, 0x9a, 0x7f, 0x5a, 0x4e, 0x1c, 0x7b, 0x51, 0x9b, 0x76, 0x3b, 0x50, 0xcd, 0x22,
56+
0x95, 0xca, 0xf0, 0x0a, 0xac, 0xf8, 0x61, 0xbb, 0x9f, 0xe6, 0x36, 0xe5, 0xde, 0x6c, 0xb8, 0xfb,
57+
0x3e, 0xb7, 0x94, 0x53, 0x20, 0x1e, 0xbc, 0xfa, 0x0a, 0x6e, 0x29, 0xcd, 0x91, 0xe5, 0xf3, 0xa9,
58+
0x1a, 0xb2, 0x87, 0x97, 0x6f, 0x93, 0x77, 0x48, 0x08, 0xe3, 0xf9, 0x9d, 0xd3, 0x44, 0x18, 0x82,
59+
0x58, 0x66, 0xf7, 0x41, 0x64, 0x41, 0x1d, 0xae, 0xef, 0x30, 0x3e, 0x6e, 0xf9, 0xeb, 0x0d, 0xbe,
60+
0xb8, 0x90, 0x76, 0x79, 0x36, 0x44, 0x60, 0x0d, 0xa2, 0x15, 0xcc, 0xc8, 0x28, 0x0f, 0x5d, 0x36,
61+
},
62+
ModRevision: 1,
63+
},
64+
},
65+
},
66+
}
67+
68+
for _, tt := range tests {
69+
t.Run(tt.name, func(t *testing.T) {
70+
t.Parallel()
71+
72+
defnamer := namer.NewDefaultNamer(tt.prefix)
73+
74+
hasher := hasher.NewSHA256Hasher()
75+
76+
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
77+
require.NoError(t, err)
78+
79+
sv := gscrypto.NewRSAPSS(*privateKey, privateKey.PublicKey)
80+
81+
mr := marshaller.NewYamlMarshaller()
82+
83+
dgv := namer.NewDefaultGeneratorValidator[string](defnamer, hasher, &sv, mr)
84+
85+
resultKV, err := dgv.Generate(tt.name, "value")
86+
require.NoError(t, err)
87+
88+
require.Len(t, tt.expected, len(resultKV))
89+
90+
for i := range resultKV {
91+
require.Equal(t, tt.expected[i].Key, resultKV[i].Key)
92+
}
93+
})
94+
}
95+
}

namer/namer.go

Lines changed: 72 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
package namer
33

44
import (
5-
"github.com/tarantool/go-storage/kv"
5+
"errors"
6+
"strings"
67
)
78

89
// KeyType represents key types.
@@ -17,6 +18,21 @@ const (
1718
KeyTypeSignature
1819
)
1920

21+
const (
22+
hashName = "hash"
23+
sigName = "sig"
24+
namesNumber = 3
25+
)
26+
27+
var (
28+
// ErrInvalidKey is returned when missing key, hash or signature.
29+
ErrInvalidKey = errors.New("missing key, hash or signature")
30+
// ErrHashMismatch is returned when hash mismatch.
31+
ErrHashMismatch = errors.New("hash mismatch")
32+
// ErrInvalidInput is returned when input data is invalid.
33+
ErrInvalidInput = errors.New("failed to generate: invalid input data")
34+
)
35+
2036
// Key implements internal realization.
2137
type Key struct {
2238
Name string // Object identificator.
@@ -30,13 +46,61 @@ type Namer interface {
3046
ParseNames(names []string) []Key // Convert names into keys.
3147
}
3248

33-
// Generator generates signer K/V pairs.
34-
// Implementation should use `generic` and will used for strong typing of the solution.
35-
type Generator[T any] interface {
36-
Generate(name string, value T) ([]kv.KeyValue, error)
49+
// DefaultNamer represents default namer.
50+
type DefaultNamer struct {
51+
prefix string
52+
}
53+
54+
// NewDefaultNamer returns new DefaultNamer object.
55+
func NewDefaultNamer(prefix string) *DefaultNamer {
56+
return &DefaultNamer{
57+
prefix: prefix,
58+
}
59+
}
60+
61+
// GenerateNames generates set of names from basic name.
62+
func (n *DefaultNamer) GenerateNames(name string) []string {
63+
return []string{
64+
n.prefix + "/" + name,
65+
n.prefix + "/" + hashName + "/" + name,
66+
n.prefix + "/" + sigName + "/" + name,
67+
}
3768
}
3869

39-
// Validator validates and build the object from K/V.
40-
type Validator[T any] interface {
41-
Validate(pairs []kv.KeyValue) (T, error)
70+
// ParseNames returns set of Keys with different types.
71+
func (n *DefaultNamer) ParseNames(names []string) []Key {
72+
keys := make([]Key, 0, namesNumber)
73+
74+
for _, name := range names {
75+
var key Key
76+
77+
// Remove prefix.
78+
result := strings.ReplaceAll(name, n.prefix, "")
79+
80+
parts := strings.Split(result, "/")
81+
82+
key.Name = name
83+
84+
switch parts[1] {
85+
case "hash":
86+
{
87+
key.Property = ""
88+
key.Type = KeyTypeHash
89+
}
90+
case "sig":
91+
{
92+
key.Property = ""
93+
key.Type = KeyTypeSignature
94+
}
95+
default:
96+
{
97+
key.Property = ""
98+
key.Type = KeyTypeValue
99+
}
100+
}
101+
102+
keys = append(keys, key)
103+
}
104+
105+
return keys
42106
}

0 commit comments

Comments
 (0)