// 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 }