RTCCertificate::Expires() and ::HasExpired() implemented using SSLCertificate::CertificateExpirationTime().

This is a re-upload of https://codereview.webrtc.org/1494103003 which was reverted and now re-landing.

BUG=chromium:544894

Review URL: https://codereview.webrtc.org/1511753003

Cr-Original-Commit-Position: refs/heads/master@{#10951}
Cr-Mirrored-From: https://chromium.googlesource.com/external/webrtc
Cr-Mirrored-Commit: 3980d4696059b0f92a181c127d9051176cc44872
diff --git a/base/base_tests.gyp b/base/base_tests.gyp
index 23b1f9a..1da93de 100644
--- a/base/base_tests.gyp
+++ b/base/base_tests.gyp
@@ -88,6 +88,7 @@
           'ratetracker_unittest.cc',
           'referencecountedsingletonfactory_unittest.cc',
           'rollingaccumulator_unittest.cc',
+          'rtccertificate_unittests.cc',
           'scopedptrcollection_unittest.cc',
           'sha1digest_unittest.cc',
           'sharedexclusivelock_unittest.cc',
diff --git a/base/fakesslidentity.h b/base/fakesslidentity.h
index 69d590b..ec603a5 100644
--- a/base/fakesslidentity.h
+++ b/base/fakesslidentity.h
@@ -25,9 +25,11 @@
   // SHA-1 is the default digest algorithm because it is available in all build
   // configurations used for unit testing.
   explicit FakeSSLCertificate(const std::string& data)
-      : data_(data), digest_algorithm_(DIGEST_SHA_1) {}
+      : data_(data), digest_algorithm_(DIGEST_SHA_1), expiration_time_(-1) {}
   explicit FakeSSLCertificate(const std::vector<std::string>& certs)
-      : data_(certs.front()), digest_algorithm_(DIGEST_SHA_1) {
+      : data_(certs.front()),
+        digest_algorithm_(DIGEST_SHA_1),
+        expiration_time_(-1) {
     std::vector<std::string>::const_iterator it;
     // Skip certs[0].
     for (it = certs.begin() + 1; it != certs.end(); ++it) {
@@ -45,7 +47,12 @@
     VERIFY(SSLIdentity::PemToDer(kPemTypeCertificate, data_, &der_string));
     der_buffer->SetData(der_string.c_str(), der_string.size());
   }
-  int64_t CertificateExpirationTime() const override { return -1; }
+  int64_t CertificateExpirationTime() const override {
+    return expiration_time_;
+  }
+  void SetCertificateExpirationTime(int64_t expiration_time) {
+    expiration_time_ = expiration_time;
+  }
   void set_digest_algorithm(const std::string& algorithm) {
     digest_algorithm_ = algorithm;
   }
@@ -79,6 +86,8 @@
   std::string data_;
   std::vector<FakeSSLCertificate> certs_;
   std::string digest_algorithm_;
+  // Expiration time in seconds relative to epoch, 1970-01-01T00:00:00Z (UTC).
+  int64_t expiration_time_;
 };
 
 class FakeSSLIdentity : public rtc::SSLIdentity {
diff --git a/base/rtccertificate.cc b/base/rtccertificate.cc
index a176d90..7b764bd 100644
--- a/base/rtccertificate.cc
+++ b/base/rtccertificate.cc
@@ -11,7 +11,6 @@
 #include "webrtc/base/rtccertificate.h"
 
 #include "webrtc/base/checks.h"
-#include "webrtc/base/timeutils.h"
 
 namespace rtc {
 
@@ -28,13 +27,16 @@
 RTCCertificate::~RTCCertificate() {
 }
 
-uint64_t RTCCertificate::expires_timestamp_ns() const {
-  // TODO(hbos): Update once SSLIdentity/SSLCertificate supports expires field.
-  return 0;
+uint64_t RTCCertificate::Expires() const {
+  int64_t expires = ssl_certificate().CertificateExpirationTime();
+  if (expires != -1)
+    return static_cast<uint64_t>(expires) * kNumMillisecsPerSec;
+  // If the expiration time could not be retrieved return an expired timestamp.
+  return 0;  // = 1970-01-01
 }
 
-bool RTCCertificate::HasExpired() const {
-  return expires_timestamp_ns() <= TimeNanos();
+bool RTCCertificate::HasExpired(uint64_t now) const {
+  return Expires() <= now;
 }
 
 const SSLCertificate& RTCCertificate::ssl_certificate() const {
diff --git a/base/rtccertificate.h b/base/rtccertificate.h
index d238938..600739b 100644
--- a/base/rtccertificate.h
+++ b/base/rtccertificate.h
@@ -27,8 +27,11 @@
   // Takes ownership of |identity|.
   static scoped_refptr<RTCCertificate> Create(scoped_ptr<SSLIdentity> identity);
 
-  uint64_t expires_timestamp_ns() const;
-  bool HasExpired() const;
+  // Returns the expiration time in ms relative to epoch, 1970-01-01T00:00:00Z.
+  uint64_t Expires() const;
+  // Checks if the certificate has expired, where |now| is expressed in ms
+  // relative to epoch, 1970-01-01T00:00:00Z.
+  bool HasExpired(uint64_t now) const;
   const SSLCertificate& ssl_certificate() const;
 
   // TODO(hbos): If possible, remove once RTCCertificate and its
diff --git a/base/rtccertificate_unittests.cc b/base/rtccertificate_unittests.cc
new file mode 100644
index 0000000..3e9439f
--- /dev/null
+++ b/base/rtccertificate_unittests.cc
@@ -0,0 +1,116 @@
+/*
+ *  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 "webrtc/base/checks.h"
+#include "webrtc/base/fakesslidentity.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/rtccertificate.h"
+#include "webrtc/base/safe_conversions.h"
+#include "webrtc/base/scoped_ptr.h"
+#include "webrtc/base/sslidentity.h"
+#include "webrtc/base/thread.h"
+#include "webrtc/base/timeutils.h"
+
+namespace rtc {
+
+namespace {
+
+static const char* kTestCertCommonName = "RTCCertificateTest's certificate";
+
+}  // namespace
+
+class RTCCertificateTest : public testing::Test {
+ public:
+  RTCCertificateTest() {}
+  ~RTCCertificateTest() {}
+
+ protected:
+  // 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<RTCCertificate>& 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<RTCCertificate>& 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<RTCCertificate> GenerateCertificateWithExpires(
+      uint64_t expires_s) const {
+    RTC_CHECK(IsValueInRangeForNumericType<time_t>(expires_s));
+
+    SSLIdentityParams params;
+    params.common_name = kTestCertCommonName;
+    params.not_before = 0;
+    params.not_after = static_cast<time_t>(expires_s);
+    // Certificate type does not matter for our purposes, using ECDSA because it
+    // is fast to generate.
+    params.key_params = KeyParams::ECDSA();
+
+    scoped_ptr<SSLIdentity> identity(SSLIdentity::GenerateForTest(params));
+    return RTCCertificate::Create(identity.Pass());
+  }
+};
+
+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_ptr<SSLIdentity> identity(
+      SSLIdentity::Generate(kTestCertCommonName, KeyParams::ECDSA()));
+  scoped_refptr<RTCCertificate> certificate =
+      RTCCertificate::Create(identity.Pass());
+
+  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<RTCCertificate> 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<RTCCertificate> 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));
+}
+
+}  // namespace rtc
diff --git a/base/sslidentity.h b/base/sslidentity.h
index b8063ce..a143ee4 100644
--- a/base/sslidentity.h
+++ b/base/sslidentity.h
@@ -70,7 +70,8 @@
                              size_t size,
                              size_t* length) const = 0;
 
-  // Returns the time in seconds relative to epoch.
+  // Returns the time in seconds relative to epoch, 1970-01-01T00:00:00Z (UTC),
+  // or -1 if an expiration time could not be retrieved.
   virtual int64_t CertificateExpirationTime() const = 0;
 };