blob: bd121422ed7a4f8edee87fdb41053ba7dcc03255 [file] [log] [blame]
// Copyright (c) 2023, Google Inc.
//
// 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 (
"crypto/sha256"
"crypto/sha512"
"encoding/hex"
"encoding/json"
"fmt"
)
// The following structures reflect the JSON of TLS 1.3 tests. See
// https://pages.nist.gov/ACVP/draft-hammett-acvp-kdf-tls-v1.3.html
type tls13TestVectorSet struct {
Groups []tls13TestGroup `json:"testGroups"`
}
type tls13TestGroup struct {
ID uint64 `json:"tgId"`
HashFunc string `json:"hmacAlg"`
Tests []tls13Test `json:"tests"`
}
type tls13Test struct {
ID uint64 `json:"tcId"`
// Although ACVP refers to these as client and server randoms, these
// fields are misnamed and really contain portions of the handshake
// transcript. Concatenated in order, they give the transcript up to
// the named message. In case of HelloRetryRequest, ClientHelloHex
// includes up to the second ClientHello.
ClientHelloHex string `json:"helloClientRandom"`
ServerHelloHex string `json:"helloServerRandom"`
ServerFinishedHex string `json:"finishedServerRandom"`
ClientFinishedHex string `json:"finishedClientRandom"`
DHEInputHex string `json:"dhe"`
PSKInputHex string `json:"psk"`
}
type tls13TestGroupResponse struct {
ID uint64 `json:"tgId"`
Tests []tls13TestResponse `json:"tests"`
}
type tls13TestResponse struct {
ID uint64 `json:"tcId"`
ClientEarlyTrafficSecretHex string `json:"clientEarlyTrafficSecret"`
EarlyExporterMasterSecretHex string `json:"earlyExporterMasterSecret"`
ClientHandshakeTrafficSecretHex string `json:"clientHandshakeTrafficSecret"`
ServerHandshakeTrafficSecretHex string `json:"serverHandshakeTrafficSecret"`
ClientApplicationTrafficSecretHex string `json:"clientApplicationTrafficSecret"`
ServerApplicationTrafficSecretHex string `json:"serverApplicationTrafficSecret"`
ExporterMasterSecretHex string `json:"exporterMasterSecret"`
ResumptionMasterSecretHex string `json:"resumptionMasterSecret"`
}
type tls13 struct{}
func (k *tls13) Process(vectorSet []byte, m Transactable) (any, error) {
var parsed tls13TestVectorSet
if err := json.Unmarshal(vectorSet, &parsed); err != nil {
return nil, err
}
var respGroups []tls13TestGroupResponse
for _, group := range parsed.Groups {
group := group
groupResp := tls13TestGroupResponse{ID: group.ID}
for _, test := range group.Tests {
test := test
testResp := tls13TestResponse{ID: test.ID}
clientHello, err := hex.DecodeString(test.ClientHelloHex)
if err != nil {
return nil, err
}
serverHello, err := hex.DecodeString(test.ServerHelloHex)
if err != nil {
return nil, err
}
serverFinished, err := hex.DecodeString(test.ServerFinishedHex)
if err != nil {
return nil, err
}
clientFinished, err := hex.DecodeString(test.ClientFinishedHex)
if err != nil {
return nil, err
}
// See https://www.rfc-editor.org/rfc/rfc8446#section-7.1
var hashLen int
var emptyHash []byte
switch group.HashFunc {
case "SHA2-256":
hashLen = 256 / 8
digest := sha256.Sum256(nil)
emptyHash = digest[:]
case "SHA2-384":
hashLen = 384 / 8
digest := sha512.Sum384(nil)
emptyHash = digest[:]
default:
return nil, fmt.Errorf("hash function %q is not supported for TLS v1.3", group.HashFunc)
}
hashLenBytes := uint32le(uint32(hashLen))
psk, err := hex.DecodeString(test.PSKInputHex)
if err != nil {
return nil, err
}
if len(psk) == 0 {
psk = make([]byte, hashLen)
}
dhe, err := hex.DecodeString(test.DHEInputHex)
if err != nil {
return nil, err
}
if len(dhe) == 0 {
dhe = make([]byte, hashLen)
}
zeros := make([]byte, hashLen)
earlySecret, err := m.Transact("HKDFExtract/"+group.HashFunc, 1, psk, zeros)
if err != nil {
return nil, fmt.Errorf("HKDFExtract operation failed: %s", err)
}
hashedToClientHello, err := m.Transact(group.HashFunc, 1, clientHello)
if err != nil {
return nil, fmt.Errorf("%q operation failed: %s", group.HashFunc, err)
}
hashedToServerHello, err := m.Transact(group.HashFunc, 1, concat(clientHello, serverHello))
if err != nil {
return nil, fmt.Errorf("%q operation failed: %s", group.HashFunc, err)
}
hashedToServerFinished, err := m.Transact(group.HashFunc, 1, concat(clientHello, serverHello, serverFinished))
if err != nil {
return nil, fmt.Errorf("%q operation failed: %s", group.HashFunc, err)
}
hashedMessages, err := m.Transact(group.HashFunc, 1, concat(clientHello, serverHello, serverFinished, clientFinished))
if err != nil {
return nil, fmt.Errorf("%q operation failed: %s", group.HashFunc, err)
}
clientEarlyTrafficSecret, err := m.Transact("HKDFExpandLabel/"+group.HashFunc, 1, hashLenBytes, earlySecret[0], []byte("c e traffic"), hashedToClientHello[0])
if err != nil {
return nil, fmt.Errorf("HKDFExpandLabel operation failed: %s", err)
}
testResp.ClientEarlyTrafficSecretHex = hex.EncodeToString(clientEarlyTrafficSecret[0])
earlyExporter, err := m.Transact("HKDFExpandLabel/"+group.HashFunc, 1, hashLenBytes, earlySecret[0], []byte("e exp master"), hashedToClientHello[0])
if err != nil {
return nil, fmt.Errorf("HKDFExpandLabel operation failed: %s", err)
}
testResp.EarlyExporterMasterSecretHex = hex.EncodeToString(earlyExporter[0])
derivedSecret, err := m.Transact("HKDFExpandLabel/"+group.HashFunc, 1, hashLenBytes, earlySecret[0], []byte("derived"), emptyHash[:])
if err != nil {
return nil, fmt.Errorf("HKDFExpandLabel operation failed: %s", err)
}
handshakeSecret, err := m.Transact("HKDFExtract/"+group.HashFunc, 1, dhe, derivedSecret[0])
if err != nil {
return nil, fmt.Errorf("HKDFExtract operation failed: %s", err)
}
clientHandshakeTrafficSecret, err := m.Transact("HKDFExpandLabel/"+group.HashFunc, 1, hashLenBytes, handshakeSecret[0], []byte("c hs traffic"), hashedToServerHello[0])
if err != nil {
return nil, fmt.Errorf("HKDFExpandLabel operation failed: %s", err)
}
testResp.ClientHandshakeTrafficSecretHex = hex.EncodeToString(clientHandshakeTrafficSecret[0])
serverHandshakeTrafficSecret, err := m.Transact("HKDFExpandLabel/"+group.HashFunc, 1, hashLenBytes, handshakeSecret[0], []byte("s hs traffic"), hashedToServerHello[0])
if err != nil {
return nil, fmt.Errorf("HKDFExpandLabel operation failed: %s", err)
}
testResp.ServerHandshakeTrafficSecretHex = hex.EncodeToString(serverHandshakeTrafficSecret[0])
derivedSecret, err = m.Transact("HKDFExpandLabel/"+group.HashFunc, 1, hashLenBytes, handshakeSecret[0], []byte("derived"), emptyHash[:])
if err != nil {
return nil, fmt.Errorf("HKDFExpandLabel operation failed: %s", err)
}
masterSecret, err := m.Transact("HKDFExtract/"+group.HashFunc, 1, zeros, derivedSecret[0])
if err != nil {
return nil, fmt.Errorf("HKDFExtract operation failed: %s", err)
}
clientAppTrafficSecret, err := m.Transact("HKDFExpandLabel/"+group.HashFunc, 1, hashLenBytes, masterSecret[0], []byte("c ap traffic"), hashedToServerFinished[0])
if err != nil {
return nil, fmt.Errorf("HKDFExpandLabel operation failed: %s", err)
}
testResp.ClientApplicationTrafficSecretHex = hex.EncodeToString(clientAppTrafficSecret[0])
serverAppTrafficSecret, err := m.Transact("HKDFExpandLabel/"+group.HashFunc, 1, hashLenBytes, masterSecret[0], []byte("s ap traffic"), hashedToServerFinished[0])
if err != nil {
return nil, fmt.Errorf("HKDFExpandLabel operation failed: %s", err)
}
testResp.ServerApplicationTrafficSecretHex = hex.EncodeToString(serverAppTrafficSecret[0])
exporterSecret, err := m.Transact("HKDFExpandLabel/"+group.HashFunc, 1, hashLenBytes, masterSecret[0], []byte("exp master"), hashedToServerFinished[0])
if err != nil {
return nil, fmt.Errorf("HKDFExpandLabel operation failed: %s", err)
}
testResp.ExporterMasterSecretHex = hex.EncodeToString(exporterSecret[0])
resumptionSecret, err := m.Transact("HKDFExpandLabel/"+group.HashFunc, 1, hashLenBytes, masterSecret[0], []byte("res master"), hashedMessages[0])
if err != nil {
return nil, fmt.Errorf("HKDFExpandLabel operation failed: %s", err)
}
testResp.ResumptionMasterSecretHex = hex.EncodeToString(resumptionSecret[0])
groupResp.Tests = append(groupResp.Tests, testResp)
}
respGroups = append(respGroups, groupResp)
}
return respGroups, nil
}
func concat(slices ...[]byte) []byte {
var ret []byte
for _, slice := range slices {
ret = append(ret, slice...)
}
return ret
}