• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (c) 2021, Google Inc.
2//
3// Permission to use, copy, modify, and/or distribute this software for any
4// purpose with or without fee is hereby granted, provided that the above
5// copyright notice and this permission notice appear in all copies.
6//
7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
15package subprocess
16
17import (
18	"bytes"
19	"encoding/hex"
20	"encoding/json"
21	"fmt"
22	"strings"
23)
24
25// The following structures reflect the JSON of ACVP KAS KDF tests. See
26// https://pages.nist.gov/ACVP/draft-hammett-acvp-kas-kdf-twostep.html
27
28type hkdfTestVectorSet struct {
29	Groups []hkdfTestGroup `json:"testGroups"`
30}
31
32type hkdfTestGroup struct {
33	ID     uint64            `json:"tgId"`
34	Type   string            `json:"testType"` // AFT or VAL
35	Config hkdfConfiguration `json:"kdfConfiguration"`
36	Tests  []hkdfTest        `json:"tests"`
37}
38
39type hkdfTest struct {
40	ID          uint64         `json:"tcId"`
41	Params      hkdfParameters `json:"kdfParameter"`
42	PartyU      hkdfPartyInfo  `json:"fixedInfoPartyU"`
43	PartyV      hkdfPartyInfo  `json:"fixedInfoPartyV"`
44	ExpectedHex string         `json:"dkm"`
45}
46
47type hkdfConfiguration struct {
48	Type               string `json:"kdfType"`
49	AdditionalNonce    bool   `json:"requiresAdditionalNoncePair"`
50	OutputBits         uint32 `json:"l"`
51	FixedInfoPattern   string `json:"fixedInfoPattern"`
52	FixedInputEncoding string `json:"fixedInfoEncoding"`
53	KDFMode            string `json:"kdfMode"`
54	MACMode            string `json:"macMode"`
55	CounterLocation    string `json:"counterLocation"`
56	CounterBits        uint   `json:"counterLen"`
57}
58
59func (c *hkdfConfiguration) extract() (outBytes uint32, hashName string, err error) {
60	if c.Type != "twoStep" ||
61		c.AdditionalNonce ||
62		c.FixedInfoPattern != "uPartyInfo||vPartyInfo" ||
63		c.FixedInputEncoding != "concatenation" ||
64		c.KDFMode != "feedback" ||
65		c.CounterLocation != "after fixed data" ||
66		c.CounterBits != 8 ||
67		c.OutputBits%8 != 0 {
68		return 0, "", fmt.Errorf("KAS-KDF not configured for HKDF: %#v", c)
69	}
70
71	if !strings.HasPrefix(c.MACMode, "HMAC-") {
72		return 0, "", fmt.Errorf("MAC mode %q does't start with 'HMAC-'", c.MACMode)
73	}
74
75	return c.OutputBits / 8, c.MACMode[5:], nil
76}
77
78type hkdfParameters struct {
79	SaltHex string `json:"salt"`
80	KeyHex  string `json:"z"`
81}
82
83func (p *hkdfParameters) extract() (key, salt []byte, err error) {
84	salt, err = hex.DecodeString(p.SaltHex)
85	if err != nil {
86		return nil, nil, err
87	}
88
89	key, err = hex.DecodeString(p.KeyHex)
90	if err != nil {
91		return nil, nil, err
92	}
93
94	return key, salt, nil
95}
96
97type hkdfPartyInfo struct {
98	IDHex    string `json:"partyId"`
99	ExtraHex string `json:"ephemeralData"`
100}
101
102func (p *hkdfPartyInfo) data() ([]byte, error) {
103	ret, err := hex.DecodeString(p.IDHex)
104	if err != nil {
105		return nil, err
106	}
107
108	if len(p.ExtraHex) > 0 {
109		extra, err := hex.DecodeString(p.ExtraHex)
110		if err != nil {
111			return nil, err
112		}
113		ret = append(ret, extra...)
114	}
115
116	return ret, nil
117}
118
119type hkdfTestGroupResponse struct {
120	ID    uint64             `json:"tgId"`
121	Tests []hkdfTestResponse `json:"tests"`
122}
123
124type hkdfTestResponse struct {
125	ID     uint64 `json:"tcId"`
126	KeyOut string `json:"dkm,omitempty"`
127	Passed *bool  `json:"testPassed,omitempty"`
128}
129
130type hkdf struct{}
131
132func (k *hkdf) Process(vectorSet []byte, m Transactable) (interface{}, error) {
133	var parsed hkdfTestVectorSet
134	if err := json.Unmarshal(vectorSet, &parsed); err != nil {
135		return nil, err
136	}
137
138	var respGroups []hkdfTestGroupResponse
139	for _, group := range parsed.Groups {
140		groupResp := hkdfTestGroupResponse{ID: group.ID}
141
142		var isValidationTest bool
143		switch group.Type {
144		case "VAL":
145			isValidationTest = true
146		case "AFT":
147			isValidationTest = false
148		default:
149			return nil, fmt.Errorf("unknown test type %q", group.Type)
150		}
151
152		outBytes, hashName, err := group.Config.extract()
153		if err != nil {
154			return nil, err
155		}
156
157		for _, test := range group.Tests {
158			testResp := hkdfTestResponse{ID: test.ID}
159
160			key, salt, err := test.Params.extract()
161			if err != nil {
162				return nil, err
163			}
164			uData, err := test.PartyU.data()
165			if err != nil {
166				return nil, err
167			}
168			vData, err := test.PartyV.data()
169			if err != nil {
170				return nil, err
171			}
172
173			var expected []byte
174			if isValidationTest {
175				expected, err = hex.DecodeString(test.ExpectedHex)
176				if err != nil {
177					return nil, err
178				}
179			}
180
181			info := make([]byte, 0, len(uData)+len(vData))
182			info = append(info, uData...)
183			info = append(info, vData...)
184
185			resp, err := m.Transact("HKDF/"+hashName, 1, key, salt, info, uint32le(outBytes))
186			if err != nil {
187				return nil, fmt.Errorf("HKDF operation failed: %s", err)
188			}
189
190			if isValidationTest {
191				passed := bytes.Equal(expected, resp[0])
192				testResp.Passed = &passed
193			} else {
194				testResp.KeyOut = hex.EncodeToString(resp[0])
195			}
196
197			groupResp.Tests = append(groupResp.Tests, testResp)
198		}
199		respGroups = append(respGroups, groupResp)
200	}
201
202	return respGroups, nil
203}
204