1// Copyright (c) 2019, 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) 23 24// The following structures reflect the JSON of ACVP hash tests. See 25// https://pages.nist.gov/ACVP/draft-fussell-acvp-ecdsa.html#name-test-vectors 26 27type ecdsaTestVectorSet struct { 28 Groups []ecdsaTestGroup `json:"testGroups"` 29 Mode string `json:"mode"` 30} 31 32type ecdsaTestGroup struct { 33 ID uint64 `json:"tgId"` 34 Curve string `json:"curve"` 35 SecretGenerationMode string `json:"secretGenerationMode,omitempty"` 36 HashAlgo string `json:"hashAlg,omitEmpty"` 37 ComponentTest bool `json:"componentTest"` 38 Tests []struct { 39 ID uint64 `json:"tcId"` 40 QxHex string `json:"qx,omitempty"` 41 QyHex string `json:"qy,omitempty"` 42 RHex string `json:"r,omitempty"` 43 SHex string `json:"s,omitempty"` 44 MsgHex string `json:"message,omitempty"` 45 } `json:"tests"` 46} 47 48type ecdsaTestGroupResponse struct { 49 ID uint64 `json:"tgId"` 50 Tests []ecdsaTestResponse `json:"tests"` 51 QxHex string `json:"qx,omitempty"` 52 QyHex string `json:"qy,omitempty"` 53} 54 55type ecdsaTestResponse struct { 56 ID uint64 `json:"tcId"` 57 DHex string `json:"d,omitempty"` 58 QxHex string `json:"qx,omitempty"` 59 QyHex string `json:"qy,omitempty"` 60 RHex string `json:"r,omitempty"` 61 SHex string `json:"s,omitempty"` 62 Passed *bool `json:"testPassed,omitempty"` // using pointer so value is not omitted when it is false 63} 64 65// ecdsa implements an ACVP algorithm by making requests to the 66// subprocess to generate and verify ECDSA keys and signatures. 67type ecdsa struct { 68 // algo is the ACVP name for this algorithm and also the command name 69 // given to the subprocess to hash with this hash function. 70 algo string 71 curves map[string]bool // supported curve names 72 primitives map[string]primitive 73} 74 75func (e *ecdsa) Process(vectorSet []byte, m Transactable) (any, error) { 76 var parsed ecdsaTestVectorSet 77 if err := json.Unmarshal(vectorSet, &parsed); err != nil { 78 return nil, err 79 } 80 81 var ret []ecdsaTestGroupResponse 82 // See 83 // https://pages.nist.gov/ACVP/draft-fussell-acvp-ecdsa.html#name-test-vectors 84 // for details about the tests. 85 for _, group := range parsed.Groups { 86 group := group 87 88 if _, ok := e.curves[group.Curve]; !ok { 89 return nil, fmt.Errorf("curve %q in test group %d not supported", group.Curve, group.ID) 90 } 91 92 response := ecdsaTestGroupResponse{ 93 ID: group.ID, 94 } 95 var sigGenPrivateKey []byte 96 97 for _, test := range group.Tests { 98 test := test 99 100 var testResp ecdsaTestResponse 101 testResp.ID = test.ID 102 103 switch parsed.Mode { 104 case "keyGen": 105 if group.SecretGenerationMode != "testing candidates" { 106 return nil, fmt.Errorf("invalid secret generation mode in test group %d: %q", group.ID, group.SecretGenerationMode) 107 } 108 m.TransactAsync(e.algo+"/"+"keyGen", 3, [][]byte{[]byte(group.Curve)}, func(result [][]byte) error { 109 testResp.DHex = hex.EncodeToString(result[0]) 110 testResp.QxHex = hex.EncodeToString(result[1]) 111 testResp.QyHex = hex.EncodeToString(result[2]) 112 response.Tests = append(response.Tests, testResp) 113 return nil 114 }) 115 116 case "keyVer": 117 qx, err := hex.DecodeString(test.QxHex) 118 if err != nil { 119 return nil, fmt.Errorf("failed to decode qx in test case %d/%d: %s", group.ID, test.ID, err) 120 } 121 qy, err := hex.DecodeString(test.QyHex) 122 if err != nil { 123 return nil, fmt.Errorf("failed to decode qy in test case %d/%d: %s", group.ID, test.ID, err) 124 } 125 m.TransactAsync(e.algo+"/"+"keyVer", 1, [][]byte{[]byte(group.Curve), qx, qy}, func(result [][]byte) error { 126 // result[0] should be a single byte: zero if false, one if true 127 switch { 128 case bytes.Equal(result[0], []byte{00}): 129 f := false 130 testResp.Passed = &f 131 case bytes.Equal(result[0], []byte{01}): 132 t := true 133 testResp.Passed = &t 134 default: 135 return fmt.Errorf("key verification returned unexpected result: %q", result[0]) 136 } 137 response.Tests = append(response.Tests, testResp) 138 return nil 139 }) 140 141 case "sigGen": 142 p := e.primitives[group.HashAlgo] 143 h, ok := p.(*hashPrimitive) 144 if !ok { 145 return nil, fmt.Errorf("unsupported hash algorithm %q in test group %d", group.HashAlgo, group.ID) 146 } 147 148 if len(sigGenPrivateKey) == 0 { 149 // Ask the subprocess to generate a key for this test group. 150 result, err := m.Transact(e.algo+"/"+"keyGen", 3, []byte(group.Curve)) 151 if err != nil { 152 return nil, fmt.Errorf("key generation failed for test case %d/%d: %s", group.ID, test.ID, err) 153 } 154 155 sigGenPrivateKey = result[0] 156 response.QxHex = hex.EncodeToString(result[1]) 157 response.QyHex = hex.EncodeToString(result[2]) 158 } 159 160 msg, err := hex.DecodeString(test.MsgHex) 161 if err != nil { 162 return nil, fmt.Errorf("failed to decode message hex in test case %d/%d: %s", group.ID, test.ID, err) 163 } 164 op := e.algo + "/" + "sigGen" 165 if group.ComponentTest { 166 if len(msg) != h.size { 167 return nil, fmt.Errorf("test case %d/%d contains message %q of length %d, but expected length %d", group.ID, test.ID, test.MsgHex, len(msg), h.size) 168 } 169 op += "/componentTest" 170 } 171 m.TransactAsync(op, 2, [][]byte{[]byte(group.Curve), sigGenPrivateKey, []byte(group.HashAlgo), msg}, func(result [][]byte) error { 172 testResp.RHex = hex.EncodeToString(result[0]) 173 testResp.SHex = hex.EncodeToString(result[1]) 174 response.Tests = append(response.Tests, testResp) 175 return nil 176 }) 177 178 case "sigVer": 179 p := e.primitives[group.HashAlgo] 180 _, ok := p.(*hashPrimitive) 181 if !ok { 182 return nil, fmt.Errorf("unsupported hash algorithm %q in test group %d", group.HashAlgo, group.ID) 183 } 184 185 msg, err := hex.DecodeString(test.MsgHex) 186 if err != nil { 187 return nil, fmt.Errorf("failed to decode message hex in test case %d/%d: %s", group.ID, test.ID, err) 188 } 189 qx, err := hex.DecodeString(test.QxHex) 190 if err != nil { 191 return nil, fmt.Errorf("failed to decode qx in test case %d/%d: %s", group.ID, test.ID, err) 192 } 193 qy, err := hex.DecodeString(test.QyHex) 194 if err != nil { 195 return nil, fmt.Errorf("failed to decode qy in test case %d/%d: %s", group.ID, test.ID, err) 196 } 197 r, err := hex.DecodeString(test.RHex) 198 if err != nil { 199 return nil, fmt.Errorf("failed to decode R in test case %d/%d: %s", group.ID, test.ID, err) 200 } 201 s, err := hex.DecodeString(test.SHex) 202 if err != nil { 203 return nil, fmt.Errorf("failed to decode S in test case %d/%d: %s", group.ID, test.ID, err) 204 } 205 m.TransactAsync(e.algo+"/"+"sigVer", 1, [][]byte{[]byte(group.Curve), []byte(group.HashAlgo), msg, qx, qy, r, s}, func(result [][]byte) error { 206 // result[0] should be a single byte: zero if false, one if true 207 switch { 208 case bytes.Equal(result[0], []byte{00}): 209 f := false 210 testResp.Passed = &f 211 case bytes.Equal(result[0], []byte{01}): 212 t := true 213 testResp.Passed = &t 214 default: 215 return fmt.Errorf("signature verification returned unexpected result: %q", result[0]) 216 } 217 response.Tests = append(response.Tests, testResp) 218 return nil 219 }) 220 221 default: 222 return nil, fmt.Errorf("invalid mode %q in ECDSA vector set", parsed.Mode) 223 } 224 } 225 226 m.Barrier(func() { 227 ret = append(ret, response) 228 }) 229 } 230 231 if err := m.Flush(); err != nil { 232 return nil, err 233 } 234 235 return ret, nil 236} 237