1// Copyright 2020 The BoringSSL Authors 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) (any, 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 group := group 81 response := kasTestGroupResponse{ 82 ID: group.ID, 83 } 84 85 var privateKeyGiven bool 86 switch group.Type { 87 case "AFT": 88 privateKeyGiven = false 89 case "VAL": 90 privateKeyGiven = true 91 default: 92 return nil, fmt.Errorf("unknown test type %q", group.Type) 93 } 94 95 switch group.Curve { 96 case "P-224", "P-256", "P-384", "P-521": 97 break 98 default: 99 return nil, fmt.Errorf("unknown curve %q", group.Curve) 100 } 101 102 switch group.Role { 103 case "initiator", "responder": 104 break 105 default: 106 return nil, fmt.Errorf("unknown role %q", group.Role) 107 } 108 109 var useStaticNamedFields bool 110 switch group.Scheme { 111 case "ephemeralUnified": 112 break 113 case "staticUnified": 114 useStaticNamedFields = true 115 break 116 default: 117 return nil, fmt.Errorf("unknown scheme %q", group.Scheme) 118 } 119 120 method := "ECDH/" + group.Curve 121 122 for _, test := range group.Tests { 123 test := test 124 125 var xHex, yHex, privateKeyHex string 126 if useStaticNamedFields { 127 xHex, yHex, privateKeyHex = test.StaticXHex, test.StaticYHex, test.StaticPrivateKeyHex 128 } else { 129 xHex, yHex, privateKeyHex = test.EphemeralXHex, test.EphemeralYHex, test.EphemeralPrivateKeyHex 130 } 131 132 if len(xHex) == 0 || len(yHex) == 0 { 133 return nil, fmt.Errorf("%d/%d is missing peer's point", group.ID, test.ID) 134 } 135 136 peerX, err := hex.DecodeString(xHex) 137 if err != nil { 138 return nil, err 139 } 140 141 peerY, err := hex.DecodeString(yHex) 142 if err != nil { 143 return nil, err 144 } 145 146 if (len(privateKeyHex) != 0) != privateKeyGiven { 147 return nil, fmt.Errorf("%d/%d incorrect private key presence", group.ID, test.ID) 148 } 149 150 if privateKeyGiven { 151 privateKey, err := hex.DecodeString(privateKeyHex) 152 if err != nil { 153 return nil, err 154 } 155 156 expectedOutput, err := hex.DecodeString(test.ResultHex) 157 if err != nil { 158 return nil, err 159 } 160 161 m.TransactAsync(method, 3, [][]byte{peerX, peerY, privateKey}, func(result [][]byte) error { 162 ok := bytes.Equal(result[2], expectedOutput) 163 response.Tests = append(response.Tests, kasTestResponse{ 164 ID: test.ID, 165 Passed: &ok, 166 }) 167 return nil 168 }) 169 } else { 170 m.TransactAsync(method, 3, [][]byte{peerX, peerY, nil}, func(result [][]byte) error { 171 testResponse := kasTestResponse{ 172 ID: test.ID, 173 ResultHex: hex.EncodeToString(result[2]), 174 } 175 176 if useStaticNamedFields { 177 testResponse.StaticXHex = hex.EncodeToString(result[0]) 178 testResponse.StaticYHex = hex.EncodeToString(result[1]) 179 } else { 180 testResponse.EphemeralXHex = hex.EncodeToString(result[0]) 181 testResponse.EphemeralYHex = hex.EncodeToString(result[1]) 182 } 183 184 response.Tests = append(response.Tests, testResponse) 185 return nil 186 }) 187 } 188 } 189 190 m.Barrier(func() { 191 ret = append(ret, response) 192 }) 193 } 194 195 if err := m.Flush(); err != nil { 196 return nil, err 197 } 198 199 return ret, nil 200} 201