| // Copyright 2020 The BoringSSL Authors | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // https://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| package subprocess | |
| import ( | |
| "encoding/binary" | |
| "encoding/hex" | |
| "encoding/json" | |
| "fmt" | |
| ) | |
| type tlsKDFVectorSet struct { | |
| Groups []tlsKDFTestGroup `json:"testGroups"` | |
| } | |
| type tlsKDFTestGroup struct { | |
| ID uint64 `json:"tgId"` | |
| Hash string `json:"hashAlg"` | |
| TLSVersion string `json:"tlsVersion"` | |
| KeyBlockBits uint64 `json:"keyBlockLength"` | |
| PMSLength uint64 `json:"preMasterSecretLength"` | |
| Tests []tlsKDFTest `json:"tests"` | |
| } | |
| type tlsKDFTest struct { | |
| ID uint64 `json:"tcId"` | |
| PMSHex string `json:"preMasterSecret"` | |
| ClientRandomHex string `json:"clientRandom"` | |
| ServerRandomHex string `json:"serverRandom"` | |
| SessionHashHex string `json:"sessionHash"` | |
| } | |
| type tlsKDFTestGroupResponse struct { | |
| ID uint64 `json:"tgId"` | |
| Tests []tlsKDFTestResponse `json:"tests"` | |
| } | |
| type tlsKDFTestResponse struct { | |
| ID uint64 `json:"tcId"` | |
| MasterSecretHex string `json:"masterSecret"` | |
| KeyBlockHex string `json:"keyBlock"` | |
| } | |
| type tlsKDF struct{} | |
| func (k *tlsKDF) Process(vectorSet []byte, m Transactable) (any, error) { | |
| var parsed tlsKDFVectorSet | |
| if err := json.Unmarshal(vectorSet, &parsed); err != nil { | |
| return nil, err | |
| } | |
| // See https://pages.nist.gov/ACVP/draft-celi-acvp-kdf-tls.html | |
| var ret []tlsKDFTestGroupResponse | |
| for _, group := range parsed.Groups { | |
| group := group | |
| response := tlsKDFTestGroupResponse{ | |
| ID: group.ID, | |
| } | |
| switch group.Hash { | |
| case "SHA2-256", "SHA2-384", "SHA2-512": | |
| break | |
| default: | |
| return nil, fmt.Errorf("unknown hash %q", group.Hash) | |
| } | |
| if group.KeyBlockBits%8 != 0 { | |
| return nil, fmt.Errorf("requested key-block length (%d bits) is not a whole number of bytes", group.KeyBlockBits) | |
| } | |
| method := "TLSKDF/1.2/" + group.Hash | |
| for _, test := range group.Tests { | |
| test := test | |
| pms, err := hex.DecodeString(test.PMSHex) | |
| if err != nil { | |
| return nil, err | |
| } | |
| clientRandom, err := hex.DecodeString(test.ClientRandomHex) | |
| if err != nil { | |
| return nil, err | |
| } | |
| serverRandom, err := hex.DecodeString(test.ServerRandomHex) | |
| if err != nil { | |
| return nil, err | |
| } | |
| sessionHash, err := hex.DecodeString(test.SessionHashHex) | |
| if err != nil { | |
| return nil, err | |
| } | |
| const ( | |
| masterSecretLength = 48 | |
| masterSecretLabel = "extended master secret" | |
| keyBlockLabel = "key expansion" | |
| ) | |
| var outLenBytes [4]byte | |
| binary.LittleEndian.PutUint32(outLenBytes[:], uint32(masterSecretLength)) | |
| result, err := m.Transact(method, 1, outLenBytes[:], pms, []byte(masterSecretLabel), sessionHash, nil) | |
| if err != nil { | |
| return nil, err | |
| } | |
| binary.LittleEndian.PutUint32(outLenBytes[:], uint32(group.KeyBlockBits/8)) | |
| // TLS 1.0, 1.1, and 1.2 use a different order for the client and server | |
| // randoms when computing the key block. | |
| m.TransactAsync(method, 1, [][]byte{outLenBytes[:], result[0], []byte(keyBlockLabel), serverRandom, clientRandom}, func(result2 [][]byte) error { | |
| response.Tests = append(response.Tests, tlsKDFTestResponse{ | |
| ID: test.ID, | |
| MasterSecretHex: hex.EncodeToString(result[0]), | |
| KeyBlockHex: hex.EncodeToString(result2[0]), | |
| }) | |
| return nil | |
| }) | |
| } | |
| m.Barrier(func() { | |
| ret = append(ret, response) | |
| }) | |
| } | |
| if err := m.Flush(); err != nil { | |
| return nil, err | |
| } | |
| return ret, nil | |
| } |