/* * Copyright 2015 The WebRTC Project Authors. All rights reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "rtc_base/rtc_certificate.h" #include #include #include #include "rtc_base/checks.h" #include "rtc_base/numerics/safe_conversions.h" #include "rtc_base/ssl_identity.h" #include "rtc_base/time_utils.h" #include "test/gtest.h" namespace rtc { namespace { static const char* kTestCertCommonName = "RTCCertificateTest's certificate"; } // namespace class RTCCertificateTest : public ::testing::Test { protected: scoped_refptr GenerateECDSA() { std::unique_ptr identity( SSLIdentity::Create(kTestCertCommonName, KeyParams::ECDSA())); RTC_CHECK(identity); return RTCCertificate::Create(std::move(identity)); } // Timestamp note: // All timestamps in this unittest are expressed in number of seconds since // epoch, 1970-01-01T00:00:00Z (UTC). The RTCCertificate interface uses ms, // but only seconds-precision is supported by SSLCertificate. To make the // tests clearer we convert everything to seconds since the precision matters // when generating certificates or comparing timestamps. // As a result, ExpiresSeconds and HasExpiredSeconds are used instead of // RTCCertificate::Expires and ::HasExpired for ms -> s conversion. uint64_t NowSeconds() const { return TimeNanos() / kNumNanosecsPerSec; } uint64_t ExpiresSeconds(const scoped_refptr& cert) const { uint64_t exp_ms = cert->Expires(); uint64_t exp_s = exp_ms / kNumMillisecsPerSec; // Make sure this did not result in loss of precision. RTC_CHECK_EQ(exp_s * kNumMillisecsPerSec, exp_ms); return exp_s; } bool HasExpiredSeconds(const scoped_refptr& cert, uint64_t now_s) const { return cert->HasExpired(now_s * kNumMillisecsPerSec); } // An RTC_CHECK ensures that `expires_s` this is in valid range of time_t as // is required by SSLIdentityParams. On some 32-bit systems time_t is limited // to < 2^31. On such systems this will fail for expiration times of year 2038 // or later. scoped_refptr GenerateCertificateWithExpires( uint64_t expires_s) const { RTC_CHECK(IsValueInRangeForNumericType(expires_s)); SSLIdentityParams params; params.common_name = kTestCertCommonName; params.not_before = 0; params.not_after = static_cast(expires_s); // Certificate type does not matter for our purposes, using ECDSA because it // is fast to generate. params.key_params = KeyParams::ECDSA(); std::unique_ptr identity(SSLIdentity::CreateForTest(params)); return RTCCertificate::Create(std::move(identity)); } }; TEST_F(RTCCertificateTest, NewCertificateNotExpired) { // Generate a real certificate without specifying the expiration time. // Certificate type doesn't matter, using ECDSA because it's fast to generate. scoped_refptr certificate = GenerateECDSA(); uint64_t now = NowSeconds(); EXPECT_FALSE(HasExpiredSeconds(certificate, now)); // Even without specifying the expiration time we would expect it to be valid // for at least half an hour. EXPECT_FALSE(HasExpiredSeconds(certificate, now + 30 * 60)); } TEST_F(RTCCertificateTest, UsesExpiresAskedFor) { uint64_t now = NowSeconds(); scoped_refptr certificate = GenerateCertificateWithExpires(now); EXPECT_EQ(now, ExpiresSeconds(certificate)); } TEST_F(RTCCertificateTest, ExpiresInOneSecond) { // Generate a certificate that expires in 1s. uint64_t now = NowSeconds(); scoped_refptr certificate = GenerateCertificateWithExpires(now + 1); // Now it should not have expired. EXPECT_FALSE(HasExpiredSeconds(certificate, now)); // In 2s it should have expired. EXPECT_TRUE(HasExpiredSeconds(certificate, now + 2)); } TEST_F(RTCCertificateTest, DifferentCertificatesNotEqual) { scoped_refptr a = GenerateECDSA(); scoped_refptr b = GenerateECDSA(); EXPECT_TRUE(*a != *b); } TEST_F(RTCCertificateTest, CloneWithPEMSerialization) { scoped_refptr orig = GenerateECDSA(); // To PEM. RTCCertificatePEM orig_pem = orig->ToPEM(); // Clone from PEM. scoped_refptr clone = RTCCertificate::FromPEM(orig_pem); EXPECT_TRUE(clone); EXPECT_TRUE(*orig == *clone); EXPECT_EQ(orig->Expires(), clone->Expires()); } TEST_F(RTCCertificateTest, FromPEMWithInvalidPEM) { RTCCertificatePEM pem("not a valid PEM", "not a valid PEM"); scoped_refptr certificate = RTCCertificate::FromPEM(pem); EXPECT_FALSE(certificate); } } // namespace rtc