// Copyright 2015 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/cert/internal/test_helpers.h" #include "base/base_paths.h" #include "base/files/file_util.h" #include "base/path_service.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/boringssl/src/include/openssl/pool.h" #include "third_party/boringssl/src/pki/cert_errors.h" #include "third_party/boringssl/src/pki/pem.h" namespace net { ::testing::AssertionResult ReadTestDataFromPemFile( const std::string& file_path_ascii, const PemBlockMapping* mappings, size_t mappings_length) { std::string file_data = ReadTestFileToString(file_path_ascii); // mappings_copy is used to keep track of which mappings have already been // satisfied (by nulling the |value| field). This is used to track when // blocks are multiply defined. std::vector mappings_copy(mappings, mappings + mappings_length); // Build the |pem_headers| vector needed for PEMTokenzier. std::vector pem_headers; for (const auto& mapping : mappings_copy) { pem_headers.push_back(mapping.block_name); } bssl::PEMTokenizer pem_tokenizer(file_data, pem_headers); while (pem_tokenizer.GetNext()) { for (auto& mapping : mappings_copy) { // Find the mapping for this block type. if (pem_tokenizer.block_type() == mapping.block_name) { if (!mapping.value) { return ::testing::AssertionFailure() << "PEM block defined multiple times: " << mapping.block_name; } // Copy the data to the result. mapping.value->assign(pem_tokenizer.data()); // Mark the mapping as having been satisfied. mapping.value = nullptr; } } } // Ensure that all specified blocks were found. for (const auto& mapping : mappings_copy) { if (mapping.value && !mapping.optional) { return ::testing::AssertionFailure() << "PEM block missing: " << mapping.block_name; } } return ::testing::AssertionSuccess(); } bool ReadCertChainFromFile(const std::string& file_path_ascii, bssl::ParsedCertificateList* chain) { // Reset all the out parameters to their defaults. chain->clear(); std::string file_data = ReadTestFileToString(file_path_ascii); if (file_data.empty()) { return false; } std::vector pem_headers = {"CERTIFICATE"}; bssl::PEMTokenizer pem_tokenizer(file_data, pem_headers); while (pem_tokenizer.GetNext()) { const std::string& block_data = pem_tokenizer.data(); bssl::CertErrors errors; if (!bssl::ParsedCertificate::CreateAndAddToVector( bssl::UniquePtr(CRYPTO_BUFFER_new( reinterpret_cast(block_data.data()), block_data.size(), nullptr)), {}, chain, &errors)) { ADD_FAILURE() << errors.ToDebugString(); return false; } } return true; } std::shared_ptr ReadCertFromFile( const std::string& file_path_ascii) { bssl::ParsedCertificateList chain; if (!ReadCertChainFromFile(file_path_ascii, &chain)) { return nullptr; } if (chain.size() != 1) { return nullptr; } return chain[0]; } std::string ReadTestFileToString(const std::string& file_path_ascii) { // Compute the full path, relative to the src/ directory. base::FilePath src_root; base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &src_root); base::FilePath filepath = src_root.AppendASCII(file_path_ascii); // Read the full contents of the file. std::string file_data; if (!base::ReadFileToString(filepath, &file_data)) { ADD_FAILURE() << "Couldn't read file: " << filepath.value(); return std::string(); } return file_data; } } // namespace net