1// Copyright 2020 The BoringSSL Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package subprocess
16
17import (
18	"bytes"
19	"encoding/hex"
20	"encoding/json"
21	"fmt"
22)
23
24type kasDHVectorSet struct {
25	Groups []kasDHTestGroup `json:"testGroups"`
26}
27
28type kasDHTestGroup struct {
29	ID     uint64      `json:"tgId"`
30	Type   string      `json:"testType"`
31	Role   string      `json:"kasRole"`
32	Mode   string      `json:"kasMode"`
33	Scheme string      `json:"scheme"`
34	PHex   string      `json:"p"`
35	QHex   string      `json:"q"`
36	GHex   string      `json:"g"`
37	Tests  []kasDHTest `json:"tests"`
38}
39
40type kasDHTest struct {
41	ID            uint64 `json:"tcId"`
42	PeerPublicHex string `json:"ephemeralPublicServer"`
43	PrivateKeyHex string `json:"ephemeralPrivateIut"`
44	PublicKeyHex  string `json:"ephemeralPublicIut"`
45	ResultHex     string `json:"z"`
46}
47
48type kasDHTestGroupResponse struct {
49	ID    uint64              `json:"tgId"`
50	Tests []kasDHTestResponse `json:"tests"`
51}
52
53type kasDHTestResponse struct {
54	ID             uint64 `json:"tcId"`
55	LocalPublicHex string `json:"ephemeralPublicIut,omitempty"`
56	ResultHex      string `json:"z,omitempty"`
57	Passed         *bool  `json:"testPassed,omitempty"`
58}
59
60type kasDH struct{}
61
62func (k *kasDH) Process(vectorSet []byte, m Transactable) (any, error) {
63	var parsed kasDHVectorSet
64	if err := json.Unmarshal(vectorSet, &parsed); err != nil {
65		return nil, err
66	}
67
68	// See https://pages.nist.gov/ACVP/draft-hammett-acvp-kas-ffc-sp800-56ar3.html
69	var ret []kasDHTestGroupResponse
70	for _, group := range parsed.Groups {
71		group := group
72		response := kasDHTestGroupResponse{
73			ID: group.ID,
74		}
75
76		var privateKeyGiven bool
77		switch group.Type {
78		case "AFT":
79			privateKeyGiven = false
80		case "VAL":
81			privateKeyGiven = true
82		default:
83			return nil, fmt.Errorf("unknown test type %q", group.Type)
84		}
85
86		switch group.Role {
87		case "initiator", "responder":
88			break
89		default:
90			return nil, fmt.Errorf("unknown role %q", group.Role)
91		}
92
93		if group.Scheme != "dhEphem" {
94			return nil, fmt.Errorf("unknown scheme %q", group.Scheme)
95		}
96
97		p, err := hex.DecodeString(group.PHex)
98		if err != nil {
99			return nil, err
100		}
101
102		q, err := hex.DecodeString(group.QHex)
103		if err != nil {
104			return nil, err
105		}
106
107		g, err := hex.DecodeString(group.GHex)
108		if err != nil {
109			return nil, err
110		}
111
112		const method = "FFDH"
113		for _, test := range group.Tests {
114			test := test
115
116			if len(test.PeerPublicHex) == 0 {
117				return nil, fmt.Errorf("%d/%d is missing peer's key", group.ID, test.ID)
118			}
119
120			peerPublic, err := hex.DecodeString(test.PeerPublicHex)
121			if err != nil {
122				return nil, err
123			}
124
125			if (len(test.PrivateKeyHex) != 0) != privateKeyGiven {
126				return nil, fmt.Errorf("%d/%d incorrect private key presence", group.ID, test.ID)
127			}
128
129			if privateKeyGiven {
130				privateKey, err := hex.DecodeString(test.PrivateKeyHex)
131				if err != nil {
132					return nil, err
133				}
134
135				publicKey, err := hex.DecodeString(test.PublicKeyHex)
136				if err != nil {
137					return nil, err
138				}
139
140				expectedOutput, err := hex.DecodeString(test.ResultHex)
141				if err != nil {
142					return nil, err
143				}
144
145				m.TransactAsync(method, 2, [][]byte{p, q, g, peerPublic, privateKey, publicKey}, func(result [][]byte) error {
146					ok := bytes.Equal(result[1], expectedOutput)
147					response.Tests = append(response.Tests, kasDHTestResponse{
148						ID:     test.ID,
149						Passed: &ok,
150					})
151					return nil
152				})
153			} else {
154				m.TransactAsync(method, 2, [][]byte{p, q, g, peerPublic, nil, nil}, func(result [][]byte) error {
155					response.Tests = append(response.Tests, kasDHTestResponse{
156						ID:             test.ID,
157						LocalPublicHex: hex.EncodeToString(result[0]),
158						ResultHex:      hex.EncodeToString(result[1]),
159					})
160					return nil
161				})
162			}
163		}
164
165		m.Barrier(func() {
166			ret = append(ret, response)
167		})
168	}
169
170	if err := m.Flush(); err != nil {
171		return nil, err
172	}
173
174	return ret, nil
175}
176