1 /*
2 * Copyright 2015 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include "rtc_base/rtc_certificate.h"
12
13 #include <time.h>
14
15 #include <memory>
16 #include <utility>
17
18 #include "rtc_base/checks.h"
19 #include "rtc_base/numerics/safe_conversions.h"
20 #include "rtc_base/ssl_identity.h"
21 #include "rtc_base/time_utils.h"
22 #include "test/gtest.h"
23
24 namespace rtc {
25
26 namespace {
27
28 static const char* kTestCertCommonName = "RTCCertificateTest's certificate";
29
30 } // namespace
31
32 class RTCCertificateTest : public ::testing::Test {
33 protected:
GenerateECDSA()34 scoped_refptr<RTCCertificate> GenerateECDSA() {
35 std::unique_ptr<SSLIdentity> identity(
36 SSLIdentity::Create(kTestCertCommonName, KeyParams::ECDSA()));
37 RTC_CHECK(identity);
38 return RTCCertificate::Create(std::move(identity));
39 }
40
41 // Timestamp note:
42 // All timestamps in this unittest are expressed in number of seconds since
43 // epoch, 1970-01-01T00:00:00Z (UTC). The RTCCertificate interface uses ms,
44 // but only seconds-precision is supported by SSLCertificate. To make the
45 // tests clearer we convert everything to seconds since the precision matters
46 // when generating certificates or comparing timestamps.
47 // As a result, ExpiresSeconds and HasExpiredSeconds are used instead of
48 // RTCCertificate::Expires and ::HasExpired for ms -> s conversion.
49
NowSeconds() const50 uint64_t NowSeconds() const { return TimeNanos() / kNumNanosecsPerSec; }
51
ExpiresSeconds(const scoped_refptr<RTCCertificate> & cert) const52 uint64_t ExpiresSeconds(const scoped_refptr<RTCCertificate>& cert) const {
53 uint64_t exp_ms = cert->Expires();
54 uint64_t exp_s = exp_ms / kNumMillisecsPerSec;
55 // Make sure this did not result in loss of precision.
56 RTC_CHECK_EQ(exp_s * kNumMillisecsPerSec, exp_ms);
57 return exp_s;
58 }
59
HasExpiredSeconds(const scoped_refptr<RTCCertificate> & cert,uint64_t now_s) const60 bool HasExpiredSeconds(const scoped_refptr<RTCCertificate>& cert,
61 uint64_t now_s) const {
62 return cert->HasExpired(now_s * kNumMillisecsPerSec);
63 }
64
65 // An RTC_CHECK ensures that `expires_s` this is in valid range of time_t as
66 // is required by SSLIdentityParams. On some 32-bit systems time_t is limited
67 // to < 2^31. On such systems this will fail for expiration times of year 2038
68 // or later.
GenerateCertificateWithExpires(uint64_t expires_s) const69 scoped_refptr<RTCCertificate> GenerateCertificateWithExpires(
70 uint64_t expires_s) const {
71 RTC_CHECK(IsValueInRangeForNumericType<time_t>(expires_s));
72
73 SSLIdentityParams params;
74 params.common_name = kTestCertCommonName;
75 params.not_before = 0;
76 params.not_after = static_cast<time_t>(expires_s);
77 // Certificate type does not matter for our purposes, using ECDSA because it
78 // is fast to generate.
79 params.key_params = KeyParams::ECDSA();
80
81 std::unique_ptr<SSLIdentity> identity(SSLIdentity::CreateForTest(params));
82 return RTCCertificate::Create(std::move(identity));
83 }
84 };
85
TEST_F(RTCCertificateTest,NewCertificateNotExpired)86 TEST_F(RTCCertificateTest, NewCertificateNotExpired) {
87 // Generate a real certificate without specifying the expiration time.
88 // Certificate type doesn't matter, using ECDSA because it's fast to generate.
89 scoped_refptr<RTCCertificate> certificate = GenerateECDSA();
90
91 uint64_t now = NowSeconds();
92 EXPECT_FALSE(HasExpiredSeconds(certificate, now));
93 // Even without specifying the expiration time we would expect it to be valid
94 // for at least half an hour.
95 EXPECT_FALSE(HasExpiredSeconds(certificate, now + 30 * 60));
96 }
97
TEST_F(RTCCertificateTest,UsesExpiresAskedFor)98 TEST_F(RTCCertificateTest, UsesExpiresAskedFor) {
99 uint64_t now = NowSeconds();
100 scoped_refptr<RTCCertificate> certificate =
101 GenerateCertificateWithExpires(now);
102 EXPECT_EQ(now, ExpiresSeconds(certificate));
103 }
104
TEST_F(RTCCertificateTest,ExpiresInOneSecond)105 TEST_F(RTCCertificateTest, ExpiresInOneSecond) {
106 // Generate a certificate that expires in 1s.
107 uint64_t now = NowSeconds();
108 scoped_refptr<RTCCertificate> certificate =
109 GenerateCertificateWithExpires(now + 1);
110 // Now it should not have expired.
111 EXPECT_FALSE(HasExpiredSeconds(certificate, now));
112 // In 2s it should have expired.
113 EXPECT_TRUE(HasExpiredSeconds(certificate, now + 2));
114 }
115
TEST_F(RTCCertificateTest,DifferentCertificatesNotEqual)116 TEST_F(RTCCertificateTest, DifferentCertificatesNotEqual) {
117 scoped_refptr<RTCCertificate> a = GenerateECDSA();
118 scoped_refptr<RTCCertificate> b = GenerateECDSA();
119 EXPECT_TRUE(*a != *b);
120 }
121
TEST_F(RTCCertificateTest,CloneWithPEMSerialization)122 TEST_F(RTCCertificateTest, CloneWithPEMSerialization) {
123 scoped_refptr<RTCCertificate> orig = GenerateECDSA();
124
125 // To PEM.
126 RTCCertificatePEM orig_pem = orig->ToPEM();
127 // Clone from PEM.
128 scoped_refptr<RTCCertificate> clone = RTCCertificate::FromPEM(orig_pem);
129 EXPECT_TRUE(clone);
130 EXPECT_TRUE(*orig == *clone);
131 EXPECT_EQ(orig->Expires(), clone->Expires());
132 }
133
TEST_F(RTCCertificateTest,FromPEMWithInvalidPEM)134 TEST_F(RTCCertificateTest, FromPEMWithInvalidPEM) {
135 RTCCertificatePEM pem("not a valid PEM", "not a valid PEM");
136 scoped_refptr<RTCCertificate> certificate = RTCCertificate::FromPEM(pem);
137 EXPECT_FALSE(certificate);
138 }
139
140 } // namespace rtc
141