1// Copyright (c) 2020, 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 24type kasVectorSet struct { 25 Groups []kasTestGroup `json:"testGroups"` 26} 27 28type kasTestGroup struct { 29 ID uint64 `json:"tgId"` 30 Type string `json:"testType"` 31 Curve string `json:"domainParameterGenerationMode"` 32 Role string `json:"kasRole"` 33 Scheme string `json:"scheme"` 34 Tests []kasTest `json:"tests"` 35} 36 37type kasTest struct { 38 ID uint64 `json:"tcId"` 39 40 EphemeralXHex string `json:"ephemeralPublicServerX"` 41 EphemeralYHex string `json:"ephemeralPublicServerY"` 42 EphemeralPrivateKeyHex string `json:"ephemeralPrivateIut"` 43 44 StaticXHex string `json:"staticPublicServerX"` 45 StaticYHex string `json:"staticPublicServerY"` 46 StaticPrivateKeyHex string `json:"staticPrivateIut"` 47 48 ResultHex string `json:"z"` 49} 50 51type kasTestGroupResponse struct { 52 ID uint64 `json:"tgId"` 53 Tests []kasTestResponse `json:"tests"` 54} 55 56type kasTestResponse struct { 57 ID uint64 `json:"tcId"` 58 59 EphemeralXHex string `json:"ephemeralPublicIutX,omitempty"` 60 EphemeralYHex string `json:"ephemeralPublicIutY,omitempty"` 61 62 StaticXHex string `json:"staticPublicIutX,omitempty"` 63 StaticYHex string `json:"staticPublicIutY,omitempty"` 64 65 ResultHex string `json:"z,omitempty"` 66 Passed *bool `json:"testPassed,omitempty"` 67} 68 69type kas struct{} 70 71func (k *kas) Process(vectorSet []byte, m Transactable) (interface{}, error) { 72 var parsed kasVectorSet 73 if err := json.Unmarshal(vectorSet, &parsed); err != nil { 74 return nil, err 75 } 76 77 // See https://pages.nist.gov/ACVP/draft-fussell-acvp-kas-ecc.html#name-test-vectors 78 var ret []kasTestGroupResponse 79 for _, group := range parsed.Groups { 80 response := kasTestGroupResponse{ 81 ID: group.ID, 82 } 83 84 var privateKeyGiven bool 85 switch group.Type { 86 case "AFT": 87 privateKeyGiven = false 88 case "VAL": 89 privateKeyGiven = true 90 default: 91 return nil, fmt.Errorf("unknown test type %q", group.Type) 92 } 93 94 switch group.Curve { 95 case "P-224", "P-256", "P-384", "P-521": 96 break 97 default: 98 return nil, fmt.Errorf("unknown curve %q", group.Curve) 99 } 100 101 switch group.Role { 102 case "initiator", "responder": 103 break 104 default: 105 return nil, fmt.Errorf("unknown role %q", group.Role) 106 } 107 108 var useStaticNamedFields bool 109 switch group.Scheme { 110 case "ephemeralUnified": 111 break 112 case "staticUnified": 113 useStaticNamedFields = true 114 break 115 default: 116 return nil, fmt.Errorf("unknown scheme %q", group.Scheme) 117 } 118 119 method := "ECDH/" + group.Curve 120 121 for _, test := range group.Tests { 122 var xHex, yHex, privateKeyHex string 123 if useStaticNamedFields { 124 xHex, yHex, privateKeyHex = test.StaticXHex, test.StaticYHex, test.StaticPrivateKeyHex 125 } else { 126 xHex, yHex, privateKeyHex = test.EphemeralXHex, test.EphemeralYHex, test.EphemeralPrivateKeyHex 127 } 128 129 if len(xHex) == 0 || len(yHex) == 0 { 130 return nil, fmt.Errorf("%d/%d is missing peer's point", group.ID, test.ID) 131 } 132 133 peerX, err := hex.DecodeString(xHex) 134 if err != nil { 135 return nil, err 136 } 137 138 peerY, err := hex.DecodeString(yHex) 139 if err != nil { 140 return nil, err 141 } 142 143 if (len(privateKeyHex) != 0) != privateKeyGiven { 144 return nil, fmt.Errorf("%d/%d incorrect private key presence", group.ID, test.ID) 145 } 146 147 if privateKeyGiven { 148 privateKey, err := hex.DecodeString(privateKeyHex) 149 if err != nil { 150 return nil, err 151 } 152 153 expectedOutput, err := hex.DecodeString(test.ResultHex) 154 if err != nil { 155 return nil, err 156 } 157 158 result, err := m.Transact(method, 3, peerX, peerY, privateKey) 159 if err != nil { 160 return nil, err 161 } 162 163 ok := bytes.Equal(result[2], expectedOutput) 164 response.Tests = append(response.Tests, kasTestResponse{ 165 ID: test.ID, 166 Passed: &ok, 167 }) 168 } else { 169 result, err := m.Transact(method, 3, peerX, peerY, nil) 170 if err != nil { 171 return nil, err 172 } 173 174 testResponse := kasTestResponse{ 175 ID: test.ID, 176 ResultHex: hex.EncodeToString(result[2]), 177 } 178 179 if useStaticNamedFields { 180 testResponse.StaticXHex = hex.EncodeToString(result[0]) 181 testResponse.StaticYHex = hex.EncodeToString(result[1]) 182 } else { 183 testResponse.EphemeralXHex = hex.EncodeToString(result[0]) 184 testResponse.EphemeralYHex = hex.EncodeToString(result[1]) 185 } 186 187 response.Tests = append(response.Tests, testResponse) 188 } 189 } 190 191 ret = append(ret, response) 192 } 193 194 return ret, nil 195} 196