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) (interface{}, 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 if _, ok := e.curves[group.Curve]; !ok { 87 return nil, fmt.Errorf("curve %q in test group %d not supported", group.Curve, group.ID) 88 } 89 90 response := ecdsaTestGroupResponse{ 91 ID: group.ID, 92 } 93 var sigGenPrivateKey []byte 94 95 for _, test := range group.Tests { 96 var testResp ecdsaTestResponse 97 98 switch parsed.Mode { 99 case "keyGen": 100 if group.SecretGenerationMode != "testing candidates" { 101 return nil, fmt.Errorf("invalid secret generation mode in test group %d: %q", group.ID, group.SecretGenerationMode) 102 } 103 result, err := m.Transact(e.algo+"/"+"keyGen", 3, []byte(group.Curve)) 104 if err != nil { 105 return nil, fmt.Errorf("key generation failed for test case %d/%d: %s", group.ID, test.ID, err) 106 } 107 testResp.DHex = hex.EncodeToString(result[0]) 108 testResp.QxHex = hex.EncodeToString(result[1]) 109 testResp.QyHex = hex.EncodeToString(result[2]) 110 111 case "keyVer": 112 qx, err := hex.DecodeString(test.QxHex) 113 if err != nil { 114 return nil, fmt.Errorf("failed to decode qx in test case %d/%d: %s", group.ID, test.ID, err) 115 } 116 qy, err := hex.DecodeString(test.QyHex) 117 if err != nil { 118 return nil, fmt.Errorf("failed to decode qy in test case %d/%d: %s", group.ID, test.ID, err) 119 } 120 result, err := m.Transact(e.algo+"/"+"keyVer", 1, []byte(group.Curve), qx, qy) 121 if err != nil { 122 return nil, fmt.Errorf("key verification failed for test case %d/%d: %s", group.ID, test.ID, err) 123 } 124 // result[0] should be a single byte: zero if false, one if true 125 switch { 126 case bytes.Equal(result[0], []byte{00}): 127 f := false 128 testResp.Passed = &f 129 case bytes.Equal(result[0], []byte{01}): 130 t := true 131 testResp.Passed = &t 132 default: 133 return nil, fmt.Errorf("key verification returned unexpected result: %q", result[0]) 134 } 135 136 case "sigGen": 137 p := e.primitives[group.HashAlgo] 138 h, ok := p.(*hashPrimitive) 139 if !ok { 140 return nil, fmt.Errorf("unsupported hash algorithm %q in test group %d", group.HashAlgo, group.ID) 141 } 142 143 if len(sigGenPrivateKey) == 0 { 144 // Ask the subprocess to generate a key for this test group. 145 result, err := m.Transact(e.algo+"/"+"keyGen", 3, []byte(group.Curve)) 146 if err != nil { 147 return nil, fmt.Errorf("key generation failed for test case %d/%d: %s", group.ID, test.ID, err) 148 } 149 150 sigGenPrivateKey = result[0] 151 response.QxHex = hex.EncodeToString(result[1]) 152 response.QyHex = hex.EncodeToString(result[2]) 153 } 154 155 msg, err := hex.DecodeString(test.MsgHex) 156 if err != nil { 157 return nil, fmt.Errorf("failed to decode message hex in test case %d/%d: %s", group.ID, test.ID, err) 158 } 159 op := e.algo + "/" + "sigGen" 160 if group.ComponentTest { 161 if len(msg) != h.size { 162 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) 163 } 164 op += "/componentTest" 165 } 166 result, err := m.Transact(op, 2, []byte(group.Curve), sigGenPrivateKey, []byte(group.HashAlgo), msg) 167 if err != nil { 168 return nil, fmt.Errorf("signature generation failed for test case %d/%d: %s", group.ID, test.ID, err) 169 } 170 testResp.RHex = hex.EncodeToString(result[0]) 171 testResp.SHex = hex.EncodeToString(result[1]) 172 173 case "sigVer": 174 p := e.primitives[group.HashAlgo] 175 _, ok := p.(*hashPrimitive) 176 if !ok { 177 return nil, fmt.Errorf("unsupported hash algorithm %q in test group %d", group.HashAlgo, group.ID) 178 } 179 180 msg, err := hex.DecodeString(test.MsgHex) 181 if err != nil { 182 return nil, fmt.Errorf("failed to decode message hex in test case %d/%d: %s", group.ID, test.ID, err) 183 } 184 qx, err := hex.DecodeString(test.QxHex) 185 if err != nil { 186 return nil, fmt.Errorf("failed to decode qx in test case %d/%d: %s", group.ID, test.ID, err) 187 } 188 qy, err := hex.DecodeString(test.QyHex) 189 if err != nil { 190 return nil, fmt.Errorf("failed to decode qy in test case %d/%d: %s", group.ID, test.ID, err) 191 } 192 r, err := hex.DecodeString(test.RHex) 193 if err != nil { 194 return nil, fmt.Errorf("failed to decode R in test case %d/%d: %s", group.ID, test.ID, err) 195 } 196 s, err := hex.DecodeString(test.SHex) 197 if err != nil { 198 return nil, fmt.Errorf("failed to decode S in test case %d/%d: %s", group.ID, test.ID, err) 199 } 200 result, err := m.Transact(e.algo+"/"+"sigVer", 1, []byte(group.Curve), []byte(group.HashAlgo), msg, qx, qy, r, s) 201 if err != nil { 202 return nil, fmt.Errorf("signature verification failed for test case %d/%d: %s", group.ID, test.ID, err) 203 } 204 // result[0] should be a single byte: zero if false, one if true 205 switch { 206 case bytes.Equal(result[0], []byte{00}): 207 f := false 208 testResp.Passed = &f 209 case bytes.Equal(result[0], []byte{01}): 210 t := true 211 testResp.Passed = &t 212 default: 213 return nil, fmt.Errorf("signature verification returned unexpected result: %q", result[0]) 214 } 215 216 default: 217 return nil, fmt.Errorf("invalid mode %q in ECDSA vector set", parsed.Mode) 218 } 219 220 testResp.ID = test.ID 221 response.Tests = append(response.Tests, testResp) 222 } 223 224 ret = append(ret, response) 225 } 226 227 return ret, nil 228} 229