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