blob: ffbe095956ea62d6b78f41790d66f54eb32e37d7 [file] [log] [blame]
// Copyright 2025 The BoringSSL Authors
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package subprocess
import (
"encoding/hex"
"encoding/json"
"fmt"
)
// The following structures reflect the JSON of ACVP KTS-IFC tests. See
// https://pages.nist.gov/ACVP/draft-hammett-acvp-kas-ifc.html#name-test-vectors
type ktsVectorSet struct {
Groups []ktsTestGroup `json:"testGroups"`
Revision string `json:"revision"`
}
type ktsTestGroup struct {
ID uint64 `json:"tgId"`
Type string `json:"testType"`
Scheme string `json:"scheme"`
Role string `json:"kasRole"`
Modulo uint64 `json:"modulo"`
KeyGen string `json:"keyGenerationMethod"`
KTSConf ktsConfig `json:"ktsConfiguration"`
OutputBits uint64 `json:"l"`
Tests []ktsTest `json:"tests"`
}
type ktsConfig struct {
HashAlg string `json:"hashAlg"`
AssociatedDataPattern string `json:"associatedDataPattern"`
Encoding string `json:"encoding"`
}
type ktsTest struct {
ID uint64 `json:"tcId"`
ServerN string `json:"serverN,omitempty"`
ServerE string `json:"serverE,omitempty"`
ServerC string `json:"serverC,omitempty"`
IutN string `json:"iutN,omitempty"`
IutE string `json:"iutE,omitempty"`
IutP string `json:"iutP,omitempty"`
IutQ string `json:"iutQ,omitempty"`
IutD string `json:"iutD,omitempty"`
}
type ktsTestGroupResponse struct {
ID uint64 `json:"tgId"`
Tests []ktsTestResponse `json:"tests"`
}
type ktsTestResponse struct {
ID uint64 `json:"tcId"`
IutC string `json:"iutC,omitempty"` // initiator role only
Dkm string `json:"dkm,omitempty"`
}
type kts struct {
hashAlgs map[string]bool // the supported hash algorithm primitives
}
func (k *kts) Process(vectorSet []byte, m Transactable) (any, error) {
var parsed ktsVectorSet
if err := json.Unmarshal(vectorSet, &parsed); err != nil {
return nil, err
}
if parsed.Revision != "Sp800-56Br2" {
return nil, fmt.Errorf("unsupported revision %q", parsed.Revision)
}
var ret []ktsTestGroupResponse
for _, group := range parsed.Groups {
group := group
response := ktsTestGroupResponse{
ID: group.ID,
}
if group.Type != "AFT" {
return nil, fmt.Errorf("unsupported test type %q in test group %d", group.Type, group.ID)
}
if group.Scheme != "KTS-OAEP-basic" {
return nil, fmt.Errorf("unsupported scheme %q in test group %d", group.Scheme, group.ID)
}
if group.KeyGen != "rsakpg1-basic" {
return nil, fmt.Errorf("unsupported key generation method %q in test group %d - only fixed public exponent (rsakpg1-basic) is supported", group.KeyGen, group.ID)
}
if group.OutputBits%8 != 0 {
return nil, fmt.Errorf("%d bit L in test group %d: fractional bytes not supported", group.OutputBits, group.ID)
}
if _, ok := k.hashAlgs[group.KTSConf.HashAlg]; !ok {
return nil, fmt.Errorf("test group %d specifies unsupported hash alg %q", group.ID, group.KTSConf.HashAlg)
}
var testResponses []ktsTestResponse
for _, test := range group.Tests {
test := test
var err error
switch group.Role {
case "initiator":
err = k.processInitiator(m, &testResponses, group.KTSConf.HashAlg, group.OutputBits, test)
case "responder":
err = k.processResponder(m, &testResponses, group.KTSConf.HashAlg, test)
default:
err = fmt.Errorf("unknown role %q", group.Role)
}
if err != nil {
return nil, err
}
}
m.Barrier(func() {
response.Tests = testResponses
ret = append(ret, response)
})
}
if err := m.Flush(); err != nil {
return nil, err
}
return ret, nil
}
func (k *kts) processInitiator(m Transactable, responses *[]ktsTestResponse, hashAlg string, outputBits uint64, test ktsTest) error {
outputBytes := uint32le(uint32(outputBits / 8))
nBytes, err := hex.DecodeString(test.ServerN)
if err != nil {
return fmt.Errorf("invalid ServerN: %v", err)
}
eBytes, err := hex.DecodeString(test.ServerE)
if err != nil {
return fmt.Errorf("invalid ServerE: %v", err)
}
cmd := fmt.Sprintf("KTS-IFC/%s/initiator", hashAlg)
args := [][]byte{outputBytes, nBytes, eBytes}
m.TransactAsync(cmd, 2, args, func(result [][]byte) error {
*responses = append(*responses,
ktsTestResponse{
ID: test.ID,
IutC: hex.EncodeToString(result[0]),
Dkm: hex.EncodeToString(result[1]),
})
return nil
})
return nil
}
func (k *kts) processResponder(m Transactable, responses *[]ktsTestResponse, hashAlg string, test ktsTest) error {
nBytes, err := hex.DecodeString(test.IutN)
if err != nil {
return fmt.Errorf("invalid IutN: %v", err)
}
eBytes, err := hex.DecodeString(test.IutE)
if err != nil {
return fmt.Errorf("invalid IutE: %v", err)
}
pBytes, err := hex.DecodeString(test.IutP)
if err != nil {
return fmt.Errorf("invalid IutP: %v", err)
}
qBytes, err := hex.DecodeString(test.IutQ)
if err != nil {
return fmt.Errorf("invalid IutQ: %v", err)
}
dBytes, err := hex.DecodeString(test.IutD)
if err != nil {
return fmt.Errorf("invalid IutD: %v", err)
}
cBytes, err := hex.DecodeString(test.ServerC)
if err != nil {
return fmt.Errorf("invalid ServerC: %v", err)
}
cmd := fmt.Sprintf("KTS-IFC/%s/responder", hashAlg)
args := [][]byte{nBytes, eBytes, pBytes, qBytes, dBytes, cBytes}
m.TransactAsync(cmd, 1, args, func(result [][]byte) error {
*responses = append(*responses,
ktsTestResponse{
ID: test.ID,
Dkm: hex.EncodeToString(result[0]),
})
return nil
})
return nil
}