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