Skip to content

Commit 955d2e8

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

File tree

4 files changed

+360
-8
lines changed

4 files changed

+360
-8
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/namer.go

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

44
import (
5+
"bytes"
6+
"errors"
7+
"fmt"
8+
"strings"
9+
10+
"github.com/tarantool/go-storage/crypto"
11+
"github.com/tarantool/go-storage/hasher"
512
"github.com/tarantool/go-storage/kv"
13+
"github.com/tarantool/go-storage/marshaller"
614
)
715

816
// KeyType represents key types.
@@ -17,6 +25,19 @@ const (
1725
KeyTypeSignature
1826
)
1927

28+
const (
29+
hashName = "hash"
30+
sigName = "sig"
31+
namesNumber = 3
32+
)
33+
34+
var (
35+
// ErrInvalidKey is returned when missing key, hash or signature.
36+
ErrInvalidKey = errors.New("missing key, hash or signature")
37+
// ErrHashMismatch is returned when hash mismatch.
38+
ErrHashMismatch = errors.New("hash mismatch")
39+
)
40+
2041
// Key implements internal realization.
2142
type Key struct {
2243
Name string // Object identificator.
@@ -30,6 +51,65 @@ type Namer interface {
3051
ParseNames(names []string) []Key // Convert names into keys.
3152
}
3253

54+
// DefaultNamer represents default namer.
55+
type DefaultNamer struct {
56+
prefix string
57+
}
58+
59+
// NewDefaultNamer returns new DefaultNamer object.
60+
func NewDefaultNamer(prefix string) *DefaultNamer {
61+
return &DefaultNamer{
62+
prefix: prefix,
63+
}
64+
}
65+
66+
// GenerateNames generates set of names from basic name.
67+
func (n *DefaultNamer) GenerateNames(name string) []string {
68+
return []string{
69+
n.prefix + "/" + name,
70+
n.prefix + "/" + hashName + "/" + name,
71+
n.prefix + "/" + sigName + "/" + name,
72+
}
73+
}
74+
75+
// ParseNames returns set of Keys with different types.
76+
func (n *DefaultNamer) ParseNames(names []string) []Key {
77+
keys := make([]Key, 0, namesNumber)
78+
79+
for _, name := range names {
80+
var key Key
81+
82+
// Remove prefix.
83+
result := strings.ReplaceAll(name, n.prefix, "")
84+
85+
parts := strings.Split(result, "/")
86+
87+
key.Name = name
88+
89+
switch parts[1] {
90+
case "hash":
91+
{
92+
key.Property = ""
93+
key.Type = KeyTypeHash
94+
}
95+
case "sig":
96+
{
97+
key.Property = ""
98+
key.Type = KeyTypeSignature
99+
}
100+
default:
101+
{
102+
key.Property = ""
103+
key.Type = KeyTypeValue
104+
}
105+
}
106+
107+
keys = append(keys, key)
108+
}
109+
110+
return keys
111+
}
112+
33113
// Generator generates signer K/V pairs.
34114
// Implementation should use `generic` and will used for strong typing of the solution.
35115
type Generator[T any] interface {
@@ -40,3 +120,121 @@ type Generator[T any] interface {
40120
type Validator[T any] interface {
41121
Validate(pairs []kv.KeyValue) (T, error)
42122
}
123+
124+
// DefaultGeneratorValidator represent default generator-validator.
125+
type DefaultGeneratorValidator[T any] struct {
126+
Namer Namer
127+
Hasher hasher.Hasher
128+
SignerVerifier crypto.SignerVerifier
129+
Marshaller marshaller.Marshallable
130+
}
131+
132+
// NewDefaultGeneratorValidator returns new object.
133+
func NewDefaultGeneratorValidator[T any](
134+
namer Namer,
135+
hasher hasher.Hasher,
136+
signverifier crypto.SignerVerifier,
137+
marshaller marshaller.Marshallable,
138+
) DefaultGeneratorValidator[T] {
139+
return DefaultGeneratorValidator[T]{
140+
Namer: namer,
141+
Hasher: hasher,
142+
SignerVerifier: signverifier,
143+
Marshaller: marshaller,
144+
}
145+
}
146+
147+
// Generate create KV pairs with value, hash and signature.
148+
func (gv DefaultGeneratorValidator[T]) Generate(name string, value T) ([]kv.KeyValue, error) {
149+
var kvList []kv.KeyValue
150+
151+
blob, err := gv.Marshaller.Marshal(value)
152+
if err != nil {
153+
return nil, fmt.Errorf("failed to marshal: %w", err)
154+
}
155+
156+
hash, err := gv.Hasher.Hash(blob)
157+
if err != nil {
158+
return nil, fmt.Errorf("failed to hash: %w", err)
159+
}
160+
161+
signature, err := gv.SignerVerifier.Sign(hash)
162+
if err != nil {
163+
return nil, fmt.Errorf("failed to sign: %w", err)
164+
}
165+
166+
names := gv.Namer.GenerateNames(name)
167+
keys := gv.Namer.ParseNames(names)
168+
169+
for _, key := range keys {
170+
switch key.Type {
171+
case KeyTypeValue:
172+
{
173+
kvList = append(kvList, kv.KeyValue{
174+
Key: []byte(key.Name),
175+
Value: blob,
176+
ModRevision: 1,
177+
})
178+
}
179+
case KeyTypeHash:
180+
{
181+
kvList = append(kvList, kv.KeyValue{
182+
Key: []byte(key.Name),
183+
Value: hash,
184+
ModRevision: 1,
185+
})
186+
}
187+
case KeyTypeSignature:
188+
{
189+
kvList = append(kvList, kv.KeyValue{
190+
Key: []byte(key.Name),
191+
Value: signature,
192+
ModRevision: 1,
193+
})
194+
}
195+
}
196+
}
197+
198+
return kvList, nil
199+
}
200+
201+
// Validate checks hash match, verify signature, unmarshall object and return it.
202+
func (gv DefaultGeneratorValidator[T]) Validate(pairs []kv.KeyValue) (T, error) {
203+
var value T
204+
205+
var blob []byte
206+
207+
var hash, signature []byte
208+
209+
for _, keyvalue := range pairs {
210+
switch {
211+
case strings.Contains(string(keyvalue.Key), hashName):
212+
hash = keyvalue.Value
213+
case strings.Contains(string(keyvalue.Key), sigName):
214+
signature = keyvalue.Value
215+
default:
216+
blob = keyvalue.Value
217+
}
218+
}
219+
220+
if blob == nil || hash == nil || signature == nil {
221+
return value, ErrInvalidKey
222+
}
223+
224+
err := gv.SignerVerifier.Verify(hash, signature)
225+
if err != nil {
226+
return value, fmt.Errorf("signature verification failed: %w", err)
227+
}
228+
229+
computedHash, err := gv.Hasher.Hash(blob)
230+
if !bytes.Equal(computedHash, hash) || err != nil {
231+
return value, ErrHashMismatch
232+
}
233+
234+
err = gv.Marshaller.Unmarshal(blob, &value)
235+
if err != nil {
236+
return value, fmt.Errorf("failed to unmarshal: %w", err)
237+
}
238+
239+
return value, nil
240+
}

0 commit comments

Comments
 (0)