blob: df2aa438f821c4785c7940bef5e70f9f3acbbc07 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "services/network/nss_temp_certs_cache_chromeos.h"
#include <cert.h>
#include <certdb.h>
#include <secitem.h>
#include <string>
#include <vector>
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "net/cert/internal/cert_errors.h"
#include "net/cert/internal/parse_certificate.h"
#include "net/cert/pem.h"
#include "net/cert/x509_certificate.h"
#include "net/der/input.h"
#include "net/test/test_data_directory.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace network {
namespace {
class NSSTempCertsCacheChromeOSTest : public testing::Test {
public:
NSSTempCertsCacheChromeOSTest() {}
~NSSTempCertsCacheChromeOSTest() override {}
protected:
// Checks if the certificate stored in |pem_cert_file| can be found in the
// default NSS certificate database using CERT_FindCertByName.
// Stores the result in *|out_available|.
// Note: This funcion uses ASSERT_ macros, so the caller must verify for
// failures after it returns.
void CheckIsCertificateAvailable(const base::FilePath& pem_cert_file,
bool* out_available) {
std::string cert_contents_buffer;
net::der::Input subject;
ASSERT_NO_FATAL_FAILURE(GetCertificateSubjectDN(
pem_cert_file, &cert_contents_buffer, &subject));
SECItem subject_item;
subject_item.len = subject.Length();
subject_item.data = const_cast<unsigned char*>(subject.UnsafeData());
net::ScopedCERTCertificate found_cert(
CERT_FindCertByName(CERT_GetDefaultCertDB(), &subject_item));
*out_available = static_cast<bool>(found_cert);
}
// Determines the subject DN of the certificate stored in
// |pem_cert_file|. Stores the result in *|out_subject|.
// The der::Input data structure contains unowned pointers into the
// certificate data buffer. The caller must pass a buffer in
// |cert_contents_buffer| and ensure to only use *|out_subject| while
// *|cert_contents_buffer| is in scope.
// Note: This funcion uses ASSERT_ macros, so the caller must verify for
// failures after it returns.
void GetCertificateSubjectDN(const base::FilePath& pem_cert_file,
std::string* cert_contents_buffer,
net::der::Input* out_subject) {
std::string file_data;
ASSERT_TRUE(base::ReadFileToString(pem_cert_file, &file_data));
std::vector<std::string> pem_headers;
pem_headers.push_back("CERTIFICATE");
net::PEMTokenizer pem_tokenizer(file_data, pem_headers);
ASSERT_TRUE(pem_tokenizer.GetNext());
*cert_contents_buffer = pem_tokenizer.data();
// Parsing the certificate.
net::der::Input tbs_certificate_tlv;
net::der::Input signature_algorithm_tlv;
net::der::BitString signature_value;
net::CertErrors errors;
ASSERT_TRUE(net::ParseCertificate(
net::der::Input(cert_contents_buffer), &tbs_certificate_tlv,
&signature_algorithm_tlv, &signature_value, &errors));
net::ParsedTbsCertificate tbs;
net::ParseCertificateOptions options;
options.allow_invalid_serial_numbers = true;
ASSERT_TRUE(
net::ParseTbsCertificate(tbs_certificate_tlv, options, &tbs, nullptr));
*out_subject = tbs.subject_tlv;
}
private:
DISALLOW_COPY_AND_ASSIGN(NSSTempCertsCacheChromeOSTest);
};
// Checks that a certificate made available through the
// NSSTempCertsCacheChromeOS can be found by NSS. We specifically check for
// lookup through the CERT_FindCertByName function, as this is what is used in
// client certificate matching (see MatchClientCertificateIssuers in
// net/third_party/nss/ssl/cmpcert.cc). Additionally, checks that the
// certificate is not available after the NSSTempCertsCacheChromeOS goes out of
// scope.
TEST_F(NSSTempCertsCacheChromeOSTest, CertMadeAvailable) {
base::FilePath cert_file_path =
net::GetTestCertsDirectory().AppendASCII("client_1_ca.pem");
{
std::string x509_authority_cert;
ASSERT_TRUE(base::ReadFileToString(cert_file_path, &x509_authority_cert));
net::CertificateList x509_authority_certs =
net::X509Certificate::CreateCertificateListFromBytes(
x509_authority_cert.data(), x509_authority_cert.length(),
net::X509Certificate::Format::FORMAT_AUTO);
NSSTempCertsCacheChromeOS cache(x509_authority_certs);
bool cert_available = false;
ASSERT_NO_FATAL_FAILURE(
CheckIsCertificateAvailable(cert_file_path, &cert_available));
EXPECT_TRUE(cert_available);
}
bool cert_available_no_cache = true;
ASSERT_NO_FATAL_FAILURE(
CheckIsCertificateAvailable(cert_file_path, &cert_available_no_cache));
EXPECT_FALSE(cert_available_no_cache);
}
} // namespace
} // namespace network