@@ -21,18 +21,22 @@ type hkdfTestVectorSet struct {
2121}
2222
2323type hkdfTestGroup struct {
24- ID uint64 `json:"tgId"`
25- Type string `json:"testType"` // AFT or VAL
26- Config hkdfConfiguration `json:"kdfConfiguration"`
27- Tests []hkdfTest `json:"tests"`
24+ ID uint64 `json:"tgId"`
25+ Type string `json:"testType"` // AFT or VAL
26+ Config hkdfConfiguration `json:"kdfConfiguration"`
27+ MultiExpansion bool `json:"multiExpansion"`
28+ MultiConfig hkdfMultiExpansionConfiguration `json:"kdfMultiExpansionConfiguration"`
29+ Tests []hkdfTest `json:"tests"`
2830}
2931
3032type hkdfTest struct {
31- ID uint64 `json:"tcId"`
32- Params hkdfParameters `json:"kdfParameter"`
33- PartyU hkdfPartyInfo `json:"fixedInfoPartyU"`
34- PartyV hkdfPartyInfo `json:"fixedInfoPartyV"`
35- ExpectedHex string `json:"dkm"`
33+ ID uint64 `json:"tcId"`
34+ Params hkdfParameters `json:"kdfParameter"`
35+ MultiParams hkdfMultiExpansionParameters `json:"kdfMultiExpansionParameter"`
36+ PartyU hkdfPartyInfo `json:"fixedInfoPartyU"`
37+ PartyV hkdfPartyInfo `json:"fixedInfoPartyV"`
38+ ExpectedHex string `json:"dkm"`
39+ ExpectedDkmsHex []string `json:"dkms"`
3640}
3741
3842type hkdfConfiguration struct {
@@ -57,6 +61,35 @@ func (c *hkdfConfiguration) extract() (outBytes uint32, hashName string, err err
5761 return c .OutputBits / 8 , c .HmacAlg , nil
5862}
5963
64+ type hkdfMultiExpansionConfiguration struct {
65+ Type string `json:"kdfType"`
66+ SaltMethod string `json:"saltMethod"`
67+ SaltLength uint64 `json:"saltLen"`
68+ HmacAlg string `json:"hmacAlg"`
69+ OutputBits uint32 `json:"l"`
70+ }
71+
72+ func (c * hkdfMultiExpansionConfiguration ) extract () (hashName string , err error ) {
73+ if c .Type != "hkdf" ||
74+ (c .SaltMethod != "default" && c .SaltMethod != "random" ) {
75+ return "" , fmt .Errorf ("Test group not configured for KDA HKDF multi-expansion" )
76+ }
77+ return c .HmacAlg , nil
78+ }
79+
80+ type hkdfMultiExpansionParameters struct {
81+ KdfType string `json:"kdfType"`
82+ KeyHex string `json:"z"`
83+ HmacAlg string `json:"hmacAlg"`
84+ SaltHex string `json:"salt"`
85+ IterationParameters []hkdfMultiExpansionIteration `json:"iterationParameters"`
86+ }
87+
88+ type hkdfMultiExpansionIteration struct {
89+ OutputBits uint32 `json:"l"`
90+ FixedInfoHex string `json:"fixedInfo"`
91+ }
92+
6093type hkdfParameters struct {
6194 KdfType string `json:"kdfType"`
6295 SaltHex string `json:"salt"`
@@ -117,9 +150,10 @@ type hkdfTestGroupResponse struct {
117150}
118151
119152type hkdfTestResponse struct {
120- ID uint64 `json:"tcId"`
121- KeyOut string `json:"dkm,omitempty"`
122- Passed * bool `json:"testPassed,omitempty"`
153+ ID uint64 `json:"tcId"`
154+ KeyOut string `json:"dkm,omitempty"`
155+ KeyOuts []string `json:"dkms,omitempty"`
156+ Passed * bool `json:"testPassed,omitempty"`
123157}
124158
125159type kdaHkdfMode struct {}
@@ -135,70 +169,166 @@ func (k *kdaHkdfMode) ProcessKDA(vectorSet []byte, m Transactable) (interface{},
135169 group := group
136170 groupResp := hkdfTestGroupResponse {ID : group .ID }
137171
138- // determine the test type
139- var isValidationTest bool
140- switch group .Type {
141- case "VAL" :
142- isValidationTest = true
143- case "AFT" :
144- isValidationTest = false
145- default :
146- return nil , fmt .Errorf ("unknown test type %q" , group .Type )
172+ if group .MultiExpansion {
173+ if err := processMultiExpansionGroup (& group , & groupResp , m ); err != nil {
174+ return nil , err
175+ }
176+ } else {
177+ if err := processSingleExpansionGroup (& group , & groupResp , m ); err != nil {
178+ return nil , err
179+ }
147180 }
148181
149- // get the number of bytes to output and the hmac alg we're using
150- outBytes , hashName , err := group .Config .extract ()
182+ respGroups = append (respGroups , groupResp )
183+ }
184+
185+ return respGroups , nil
186+ }
187+
188+ func processSingleExpansionGroup (group * hkdfTestGroup , groupResp * hkdfTestGroupResponse , m Transactable ) error {
189+ // determine the test type
190+ var isValidationTest bool
191+ switch group .Type {
192+ case "VAL" :
193+ isValidationTest = true
194+ case "AFT" :
195+ isValidationTest = false
196+ default :
197+ return fmt .Errorf ("unknown test type %q" , group .Type )
198+ }
199+
200+ // get the number of bytes to output and the hmac alg we're using
201+ outBytes , hashName , err := group .Config .extract ()
202+ if err != nil {
203+ return err
204+ }
205+
206+ for _ , test := range group .Tests {
207+ test := test
208+ testResp := hkdfTestResponse {ID : test .ID }
209+
210+ key , salt , err := test .Params .extract ()
151211 if err != nil {
152- return nil , err
212+ return err
153213 }
214+ uData , err := test .PartyU .data ()
215+ if err != nil {
216+ return err
217+ }
218+ vData , err := test .PartyV .data ()
219+ if err != nil {
220+ return err
221+ }
222+ lenData := test .Params .data ()
154223
155- for _ , test := range group .Tests {
156- test := test
157- testResp := hkdfTestResponse {ID : test .ID }
158-
159- key , salt , err := test .Params .extract ()
160- if err != nil {
161- return nil , err
162- }
163- uData , err := test .PartyU .data ()
164- if err != nil {
165- return nil , err
166- }
167- vData , err := test .PartyV .data ()
224+ var expected []byte
225+ if isValidationTest {
226+ expected , err = hex .DecodeString (test .ExpectedHex )
168227 if err != nil {
169- return nil , err
228+ return err
170229 }
171- lenData := test . Params . data ()
230+ }
172231
173- var expected []byte
174- if isValidationTest {
175- expected , err = hex .DecodeString (test .ExpectedHex )
176- if err != nil {
177- return nil , err
178- }
179- }
232+ info := make ([]byte , 0 , len (uData )+ len (vData )+ len (lenData ))
233+ info = append (info , uData ... )
234+ info = append (info , vData ... )
235+ info = append (info , lenData ... )
236+
237+ resp , err := m .Transact ("KDA/HKDF/" + hashName , 1 , key , salt , info , uint32le (outBytes ))
238+ if err != nil {
239+ return fmt .Errorf ("KDA_HKDF operation failed: %s" , err )
240+ }
241+
242+ if isValidationTest {
243+ passed := bytes .Equal (expected , resp [0 ])
244+ testResp .Passed = & passed
245+ } else {
246+ testResp .KeyOut = hex .EncodeToString (resp [0 ])
247+ }
248+
249+ groupResp .Tests = append (groupResp .Tests , testResp )
250+ }
251+
252+ return nil
253+ }
254+
255+ func processMultiExpansionGroup (group * hkdfTestGroup , groupResp * hkdfTestGroupResponse , m Transactable ) error {
256+ // Multi-expansion supports both AFT and VAL
257+ var isValidationTest bool
258+ switch group .Type {
259+ case "VAL" :
260+ isValidationTest = true
261+ case "AFT" :
262+ isValidationTest = false
263+ default :
264+ return fmt .Errorf ("unsupported test type %q for multi-expansion" , group .Type )
265+ }
266+
267+ hashName , err := group .MultiConfig .extract ()
268+ if err != nil {
269+ return err
270+ }
180271
181- info := make ([]byte , 0 , len (uData )+ len (vData )+ len (lenData ))
182- info = append (info , uData ... )
183- info = append (info , vData ... )
184- info = append (info , lenData ... )
272+ for _ , test := range group .Tests {
273+ test := test
274+ testResp := hkdfTestResponse {ID : test .ID }
185275
186- resp , err := m .Transact ("KDA/HKDF/" + hashName , 1 , key , salt , info , uint32le (outBytes ))
276+ // Decode z (shared secret) and salt
277+ z , err := hex .DecodeString (test .MultiParams .KeyHex )
278+ if err != nil {
279+ return fmt .Errorf ("tcId %d: failed to decode z: %s" , test .ID , err )
280+ }
281+ salt , err := hex .DecodeString (test .MultiParams .SaltHex )
282+ if err != nil {
283+ return fmt .Errorf ("tcId %d: failed to decode salt: %s" , test .ID , err )
284+ }
285+
286+ // Step 1: Extract — PRK = HKDF-Extract(salt, z)
287+ extractResp , err := m .Transact ("HKDF/" + hashName + "/extract" , 1 , z , salt )
288+ if err != nil {
289+ return fmt .Errorf ("tcId %d: HKDF extract failed: %s" , test .ID , err )
290+ }
291+ prk := extractResp [0 ]
292+
293+ // Step 2: Expand — for each iteration, derive a key
294+ dkms := make ([]string , 0 , len (test .MultiParams .IterationParameters ))
295+ for i , iter := range test .MultiParams .IterationParameters {
296+ if iter .OutputBits % 8 != 0 {
297+ return fmt .Errorf ("tcId %d, iteration %d: output bits %d not a multiple of 8" , test .ID , i , iter .OutputBits )
298+ }
299+ outBytes := iter .OutputBits / 8
300+
301+ fixedInfo , err := hex .DecodeString (iter .FixedInfoHex )
187302 if err != nil {
188- return nil , fmt .Errorf ("KDA_HKDF operation failed: %s" , err )
303+ return fmt .Errorf ("tcId %d, iteration %d: failed to decode fixedInfo : %s" , test . ID , i , err )
189304 }
190305
191- if isValidationTest {
192- passed := bytes .Equal (expected , resp [0 ])
193- testResp .Passed = & passed
194- } else {
195- testResp .KeyOut = hex .EncodeToString (resp [0 ])
306+ expandResp , err := m .Transact ("HKDF/" + hashName + "/expand" , 1 , uint32le (outBytes ), prk , fixedInfo )
307+ if err != nil {
308+ return fmt .Errorf ("tcId %d, iteration %d: HKDF expand failed: %s" , test .ID , i , err )
196309 }
197310
198- groupResp . Tests = append (groupResp . Tests , testResp )
311+ dkms = append (dkms , strings . ToUpper ( hex . EncodeToString ( expandResp [ 0 ])) )
199312 }
200- respGroups = append (respGroups , groupResp )
313+
314+ if isValidationTest {
315+ // Compare computed dkms against expected
316+ passed := len (dkms ) == len (test .ExpectedDkmsHex )
317+ if passed {
318+ for i := range dkms {
319+ if ! strings .EqualFold (dkms [i ], test .ExpectedDkmsHex [i ]) {
320+ passed = false
321+ break
322+ }
323+ }
324+ }
325+ testResp .Passed = & passed
326+ } else {
327+ testResp .KeyOuts = dkms
328+ }
329+
330+ groupResp .Tests = append (groupResp .Tests , testResp )
201331 }
202332
203- return respGroups , nil
333+ return nil
204334}
0 commit comments