1package subprocess
2
3import (
4	"encoding/hex"
5	"encoding/json"
6	"fmt"
7)
8
9// The following structures reflect the JSON of KDF SSH tests. See
10// https://pages.nist.gov/ACVP/draft-celi-acvp-kdf-ssh.html#name-test-vectors
11
12type sshTestVectorSet struct {
13	Algorithm string         `json:"algorithm"`
14	Mode      string         `json:"mode"`
15	Groups    []sshTestGroup `json:"testGroups"`
16}
17
18type sshTestGroup struct {
19	ID       uint64 `json:"tgId"`
20	TestType string `json:"testType"`
21	HashAlg  string `json:"hashAlg"`
22	Cipher   string `json:"cipher"`
23	Tests    []struct {
24		ID           uint64 `json:"tcId"`
25		KHex         string `json:"k"`
26		HHex         string `json:"h"`
27		SessionIDHex string `json:"sessionID"`
28	} `json:"tests"`
29}
30
31type sshTestGroupResponse struct {
32	ID    uint64            `json:"tgId"`
33	Tests []sshTestResponse `json:"tests"`
34}
35
36type sshTestResponse struct {
37	ID                     uint64 `json:"tcId"`
38	InitialIvClientHex     string `json:"initialIvClient"`
39	InitialIvServerHex     string `json:"initialIvServer"`
40	EncryptionKeyClientHex string `json:"encryptionKeyClient"`
41	EncryptionKeyServerHex string `json:"encryptionKeyServer"`
42	IntegrityKeyClientHex  string `json:"integrityKeyClient"`
43	IntegrityKeyServerHex  string `json:"integrityKeyServer"`
44}
45
46type ssh struct {
47}
48
49func (s *ssh) Process(vectorSet []byte, m Transactable) (any, error) {
50	var parsed sshTestVectorSet
51	if err := json.Unmarshal(vectorSet, &parsed); err != nil {
52		return nil, err
53	}
54
55	if parsed.Algorithm != "kdf-components" {
56		return nil, fmt.Errorf("unexpected algorithm: %q", parsed.Algorithm)
57	}
58	if parsed.Mode != "ssh" {
59		return nil, fmt.Errorf("unexpected mode: %q", parsed.Mode)
60	}
61
62	var ret []sshTestGroupResponse
63	for _, group := range parsed.Groups {
64		group := group
65
66		// Only the AFT test type is specified for SSH:
67		// https://pages.nist.gov/ACVP/draft-celi-acvp-kdf-ssh.html#name-test-types
68		if group.TestType != "AFT" {
69			return nil, fmt.Errorf("test group %d had unexpected test type: %q", group.ID, group.TestType)
70		}
71
72		response := sshTestGroupResponse{
73			ID: group.ID,
74		}
75
76		for _, test := range group.Tests {
77			test := test
78
79			resp := sshTestResponse{
80				ID: test.ID,
81			}
82
83			k, err := hex.DecodeString(test.KHex)
84			if err != nil {
85				return nil, fmt.Errorf("failed to decode K hex in test case %d/%d: %s", group.ID, test.ID, err)
86			}
87			h, err := hex.DecodeString(test.HHex)
88			if err != nil {
89				return nil, fmt.Errorf("failed to decode H hex in test case %d/%d: %s", group.ID, test.ID, err)
90			}
91			sessionID, err := hex.DecodeString(test.SessionIDHex)
92			if err != nil {
93				return nil, fmt.Errorf("failed to decode session ID hex in test case %d/%d: %s", group.ID, test.ID, err)
94			}
95
96			cmd := fmt.Sprintf("SSHKDF/%s/client", group.HashAlg)
97			m.TransactAsync(cmd, 3, [][]byte{k, h, sessionID, []byte(group.Cipher)}, func(result [][]byte) error {
98				resp.InitialIvClientHex = hex.EncodeToString(result[0])
99				resp.EncryptionKeyClientHex = hex.EncodeToString(result[1])
100				resp.IntegrityKeyClientHex = hex.EncodeToString(result[2])
101				return nil
102			})
103
104			cmd = fmt.Sprintf("SSHKDF/%s/server", group.HashAlg)
105			m.TransactAsync(cmd, 3, [][]byte{k, h, sessionID, []byte(group.Cipher)}, func(result [][]byte) error {
106				resp.InitialIvServerHex = hex.EncodeToString(result[0])
107				resp.EncryptionKeyServerHex = hex.EncodeToString(result[1])
108				resp.IntegrityKeyServerHex = hex.EncodeToString(result[2])
109				return nil
110			})
111
112			m.Barrier(func() {
113				response.Tests = append(response.Tests, resp)
114			})
115		}
116
117		m.Barrier(func() {
118			ret = append(ret, response)
119		})
120	}
121
122	if err := m.Flush(); err != nil {
123		return nil, err
124	}
125
126	return ret, nil
127}
128