| package subprocess | |
| import ( | |
| "encoding/hex" | |
| "encoding/json" | |
| "fmt" | |
| ) | |
| // The following structures reflect the JSON of KDF SSH tests. See | |
| // https://pages.nist.gov/ACVP/draft-celi-acvp-kdf-ssh.html#name-test-vectors | |
| type sshTestVectorSet struct { | |
| Algorithm string `json:"algorithm"` | |
| Mode string `json:"mode"` | |
| Groups []sshTestGroup `json:"testGroups"` | |
| } | |
| type sshTestGroup struct { | |
| ID uint64 `json:"tgId"` | |
| TestType string `json:"testType"` | |
| HashAlg string `json:"hashAlg"` | |
| Cipher string `json:"cipher"` | |
| Tests []struct { | |
| ID uint64 `json:"tcId"` | |
| KHex string `json:"k"` | |
| HHex string `json:"h"` | |
| SessionIDHex string `json:"sessionID"` | |
| } `json:"tests"` | |
| } | |
| type sshTestGroupResponse struct { | |
| ID uint64 `json:"tgId"` | |
| Tests []sshTestResponse `json:"tests"` | |
| } | |
| type sshTestResponse struct { | |
| ID uint64 `json:"tcId"` | |
| InitialIvClientHex string `json:"initialIvClient"` | |
| InitialIvServerHex string `json:"initialIvServer"` | |
| EncryptionKeyClientHex string `json:"encryptionKeyClient"` | |
| EncryptionKeyServerHex string `json:"encryptionKeyServer"` | |
| IntegrityKeyClientHex string `json:"integrityKeyClient"` | |
| IntegrityKeyServerHex string `json:"integrityKeyServer"` | |
| } | |
| type ssh struct { | |
| } | |
| func (s *ssh) Process(vectorSet []byte, m Transactable) (any, error) { | |
| var parsed sshTestVectorSet | |
| if err := json.Unmarshal(vectorSet, &parsed); err != nil { | |
| return nil, err | |
| } | |
| if parsed.Algorithm != "kdf-components" { | |
| return nil, fmt.Errorf("unexpected algorithm: %q", parsed.Algorithm) | |
| } | |
| if parsed.Mode != "ssh" { | |
| return nil, fmt.Errorf("unexpected mode: %q", parsed.Mode) | |
| } | |
| var ret []sshTestGroupResponse | |
| for _, group := range parsed.Groups { | |
| group := group | |
| // Only the AFT test type is specified for SSH: | |
| // https://pages.nist.gov/ACVP/draft-celi-acvp-kdf-ssh.html#name-test-types | |
| if group.TestType != "AFT" { | |
| return nil, fmt.Errorf("test group %d had unexpected test type: %q", group.ID, group.TestType) | |
| } | |
| response := sshTestGroupResponse{ | |
| ID: group.ID, | |
| } | |
| for _, test := range group.Tests { | |
| test := test | |
| resp := sshTestResponse{ | |
| ID: test.ID, | |
| } | |
| k, err := hex.DecodeString(test.KHex) | |
| if err != nil { | |
| return nil, fmt.Errorf("failed to decode K hex in test case %d/%d: %s", group.ID, test.ID, err) | |
| } | |
| h, err := hex.DecodeString(test.HHex) | |
| if err != nil { | |
| return nil, fmt.Errorf("failed to decode H hex in test case %d/%d: %s", group.ID, test.ID, err) | |
| } | |
| sessionID, err := hex.DecodeString(test.SessionIDHex) | |
| if err != nil { | |
| return nil, fmt.Errorf("failed to decode session ID hex in test case %d/%d: %s", group.ID, test.ID, err) | |
| } | |
| cmd := fmt.Sprintf("SSHKDF/%s/client", group.HashAlg) | |
| m.TransactAsync(cmd, 3, [][]byte{k, h, sessionID, []byte(group.Cipher)}, func(result [][]byte) error { | |
| resp.InitialIvClientHex = hex.EncodeToString(result[0]) | |
| resp.EncryptionKeyClientHex = hex.EncodeToString(result[1]) | |
| resp.IntegrityKeyClientHex = hex.EncodeToString(result[2]) | |
| return nil | |
| }) | |
| cmd = fmt.Sprintf("SSHKDF/%s/server", group.HashAlg) | |
| m.TransactAsync(cmd, 3, [][]byte{k, h, sessionID, []byte(group.Cipher)}, func(result [][]byte) error { | |
| resp.InitialIvServerHex = hex.EncodeToString(result[0]) | |
| resp.EncryptionKeyServerHex = hex.EncodeToString(result[1]) | |
| resp.IntegrityKeyServerHex = hex.EncodeToString(result[2]) | |
| return nil | |
| }) | |
| m.Barrier(func() { | |
| response.Tests = append(response.Tests, resp) | |
| }) | |
| } | |
| m.Barrier(func() { | |
| ret = append(ret, response) | |
| }) | |
| } | |
| if err := m.Flush(); err != nil { | |
| return nil, err | |
| } | |
| return ret, nil | |
| } |