Bug 1312306 - Update expires handling on RTCCertificate to match spec, r=jib,bkelly draft
authorMartin Thomson <martin.thomson@gmail.com>
Mon, 24 Oct 2016 14:11:43 +1100
changeset 432068 1d8aa56ce64bfec49dd471a1132aafc69d9ac73f
parent 431012 1561c917ee27c3ea04bd69467e5b8c7c08102f2a
child 535535 7f660abd3e9bb85a65b9289a797d06f591397637
push id34186
push userbmo:martin.thomson@gmail.com
push dateTue, 01 Nov 2016 06:37:42 +0000
reviewersjib, bkelly
bugs1312306
milestone52.0a1
Bug 1312306 - Update expires handling on RTCCertificate to match spec, r=jib,bkelly MozReview-Commit-ID: Idnigs48DpY
dom/media/webrtc/RTCCertificate.cpp
dom/media/webrtc/RTCCertificate.h
dom/webidl/RTCCertificate.webidl
--- a/dom/media/webrtc/RTCCertificate.cpp
+++ b/dom/media/webrtc/RTCCertificate.cpp
@@ -41,47 +41,24 @@ NS_INTERFACE_MAP_END
 const size_t RTCCertificateCommonNameLength = 16;
 const size_t RTCCertificateMinRsaSize = 1024;
 
 class GenerateRTCCertificateTask : public GenerateAsymmetricKeyTask
 {
 public:
   GenerateRTCCertificateTask(nsIGlobalObject* aGlobal, JSContext* aCx,
                              const ObjectOrString& aAlgorithm,
-                             const Sequence<nsString>& aKeyUsages)
+                             const Sequence<nsString>& aKeyUsages,
+                             PRTime expires)
       : GenerateAsymmetricKeyTask(aGlobal, aCx, aAlgorithm, true, aKeyUsages),
-        mExpires(0),
+        mExpires(expires),
         mAuthType(ssl_kea_null),
         mCertificate(nullptr),
         mSignatureAlg(SEC_OID_UNKNOWN)
   {
-    // Expiry is 30 days after by default.
-    // This is a sort of arbitrary range designed to be valid
-    // now with some slack in case the other side expects
-    // some before expiry.
-    //
-
-    mExpires = EXPIRATION_DEFAULT;
-    if (!aAlgorithm.IsObject()) {
-      return;
-    }
-
-    // Load the "expires" attribute from the algorithm dictionary.  This is
-    // (currently) non-standard; it exists to support testing of certificate
-    // expiration, since one month is too long to wait for a test to run.
-    JS::Rooted<JS::Value> exp(aCx, JS::UndefinedValue());
-    JS::Rooted<JSObject*> jsval(aCx, aAlgorithm.GetAsObject());
-    bool ok = JS_GetProperty(aCx, jsval, "expires", &exp);
-    int64_t expval;
-    if (ok) {
-      ok = JS::ToInt64(aCx, exp, &expval);
-    }
-    if (ok && expval > 0) {
-      mExpires = std::min(expval, EXPIRATION_MAX);
-    }
   }
 
 private:
   PRTime mExpires;
   SSLKEAType mAuthType;
   ScopedCERTCertificate mCertificate;
   SECOidTag mSignatureAlg;
 
@@ -244,33 +221,67 @@ private:
     CERTCertificate* cert = CERT_DupCertificate(mCertificate);
     RefPtr<RTCCertificate> result =
         new RTCCertificate(mResultPromise->GetParentObject(),
                            key, cert, mAuthType, mExpires);
     mResultPromise->MaybeResolve(result);
   }
 };
 
+static PRTime
+ReadExpires(JSContext* aCx, const ObjectOrString& aOptions,
+            ErrorResult& aRv)
+{
+  // This conversion might fail, but we don't really care; use the default.
+  // If this isn't an object, or it doesn't coerce into the right type,
+  // then we won't get the |expires| value.  Either will be caught later.
+  RTCCertificateExpiration expiration;
+  if (!aOptions.IsObject()) {
+    return EXPIRATION_DEFAULT;
+  }
+  JS::RootedValue value(aCx, JS::ObjectValue(*aOptions.GetAsObject()));
+  if (!expiration.Init(aCx, value)) {
+    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
+    return 0;
+  }
+
+  if (!expiration.mExpires.WasPassed()) {
+    return EXPIRATION_DEFAULT;
+  }
+  static const uint64_t max =
+      static_cast<uint64_t>(EXPIRATION_MAX / PR_USEC_PER_MSEC);
+  if (expiration.mExpires.Value() > max) {
+    return EXPIRATION_MAX;
+  }
+  return static_cast<PRTime>(expiration.mExpires.Value() * PR_USEC_PER_MSEC);
+}
+
 already_AddRefed<Promise>
 RTCCertificate::GenerateCertificate(
-    const GlobalObject& aGlobal, const ObjectOrString& aKeygenAlgorithm,
+    const GlobalObject& aGlobal, const ObjectOrString& aOptions,
     ErrorResult& aRv, JSCompartment* aCompartment)
 {
   nsIGlobalObject* global = xpc::NativeGlobal(aGlobal.Get());
   RefPtr<Promise> p = Promise::Create(global, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
   Sequence<nsString> usages;
   if (!usages.AppendElement(NS_LITERAL_STRING("sign"), fallible)) {
+    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+    return nullptr;
+  }
+
+  PRTime expires = ReadExpires(aGlobal.Context(), aOptions, aRv);
+  if (aRv.Failed()) {
     return nullptr;
   }
   RefPtr<WebCryptoTask> task =
       new GenerateRTCCertificateTask(global, aGlobal.Context(),
-                                     aKeygenAlgorithm, usages);
+                                     aOptions, usages, expires);
   task->DispatchWithPromise(p);
   return p.forget();
 }
 
 RTCCertificate::RTCCertificate(nsIGlobalObject* aGlobal)
     : mGlobal(aGlobal),
       mPrivateKey(nullptr),
       mCertificate(nullptr),
--- a/dom/media/webrtc/RTCCertificate.h
+++ b/dom/media/webrtc/RTCCertificate.h
@@ -14,16 +14,17 @@
 #include "prtime.h"
 #include "sslt.h"
 #include "ScopedNSSTypes.h"
 
 #include "mozilla/ErrorResult.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/dom/CryptoKey.h"
+#include "mozilla/dom/RTCCertificateBinding.h"
 #include "mtransport/dtlsidentity.h"
 #include "js/StructuredClone.h"
 #include "js/TypeDecls.h"
 
 namespace mozilla {
 namespace dom {
 
 class ObjectOrString;
@@ -34,17 +35,17 @@ class RTCCertificate final
       public nsNSSShutDownObject
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(RTCCertificate)
 
   // WebIDL method that implements RTCPeerConnection.generateCertificate.
   static already_AddRefed<Promise> GenerateCertificate(
-      const GlobalObject& global, const ObjectOrString& keygenAlgorithm,
+      const GlobalObject& aGlobal, const ObjectOrString& aOptions,
       ErrorResult& aRv, JSCompartment* aCompartment = nullptr);
 
   explicit RTCCertificate(nsIGlobalObject* aGlobal);
   RTCCertificate(nsIGlobalObject* aGlobal, SECKEYPrivateKey* aPrivateKey,
                  CERTCertificate* aCertificate, SSLKEAType aAuthType,
                  PRTime aExpires);
 
   nsIGlobalObject* GetParentObject() const { return mGlobal; }
--- a/dom/webidl/RTCCertificate.webidl
+++ b/dom/webidl/RTCCertificate.webidl
@@ -1,12 +1,17 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  *
  * Specification: http://w3c.github.io/webrtc-pc/#certificate-management
  */
 
+dictionary RTCCertificateExpiration {
+  [EnforceRange]
+  DOMTimeStamp expires;
+};
+
 [Pref="media.peerconnection.enabled"]
 interface RTCCertificate {
   readonly attribute DOMTimeStamp expires;
 };