Bug 1384623 - WebAuthn [SameObject] attributes must cache those objects r=qdot
authorJ.C. Jones <jjones@mozilla.com>
Wed, 26 Jul 2017 10:03:17 -0700
changeset 428574 53ef4a4139b9d7a85d3f4f1e0201cb50eb860711
parent 428573 963ff2e15b6bd366177ddfb03cd873e69f5ba524
child 428575 71322aab413bf464bfdbe5ce9eed7c5db4f26297
push id7761
push userjlund@mozilla.com
push dateFri, 15 Sep 2017 00:19:52 +0000
treeherdermozilla-beta@c38455951db4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersqdot
bugs1384623, 1382888
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1384623 - WebAuthn [SameObject] attributes must cache those objects r=qdot Peter points out [1] that I made assumptions that [SameObject] would handle caching at the JS-layer, but it does not. This bug is to cache those objects [2] on the heap, and add tests that they are indeed the same. [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1382888#c6 [2] https://hg.mozilla.org/mozilla-central/rev/811510fdb51a MozReview-Commit-ID: KQySNAOnyeE
dom/webauthn/AuthenticatorAssertionResponse.cpp
dom/webauthn/AuthenticatorAssertionResponse.h
dom/webauthn/AuthenticatorAttestationResponse.cpp
dom/webauthn/AuthenticatorAttestationResponse.h
dom/webauthn/AuthenticatorResponse.cpp
dom/webauthn/AuthenticatorResponse.h
dom/webauthn/PublicKeyCredential.cpp
dom/webauthn/PublicKeyCredential.h
dom/webauthn/tests/test_webauthn_loopback.html
--- a/dom/webauthn/AuthenticatorAssertionResponse.cpp
+++ b/dom/webauthn/AuthenticatorAssertionResponse.cpp
@@ -5,57 +5,87 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/WebAuthenticationBinding.h"
 #include "mozilla/dom/AuthenticatorAssertionResponse.h"
 
 namespace mozilla {
 namespace dom {
 
+NS_IMPL_CYCLE_COLLECTION_CLASS(AuthenticatorAssertionResponse)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(AuthenticatorAssertionResponse,
+                                                AuthenticatorResponse)
+  tmp->mAuthenticatorDataCachedObj = nullptr;
+  tmp->mSignatureCachedObj = nullptr;
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(AuthenticatorAssertionResponse,
+                                               AuthenticatorResponse)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mAuthenticatorDataCachedObj)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mSignatureCachedObj)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(AuthenticatorAssertionResponse,
+                                                  AuthenticatorResponse)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
 NS_IMPL_ADDREF_INHERITED(AuthenticatorAssertionResponse, AuthenticatorResponse)
 NS_IMPL_RELEASE_INHERITED(AuthenticatorAssertionResponse, AuthenticatorResponse)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AuthenticatorAssertionResponse)
 NS_INTERFACE_MAP_END_INHERITING(AuthenticatorResponse)
 
 AuthenticatorAssertionResponse::AuthenticatorAssertionResponse(nsPIDOMWindowInner* aParent)
   : AuthenticatorResponse(aParent)
-{}
+  , mAuthenticatorDataCachedObj(nullptr)
+  , mSignatureCachedObj(nullptr)
+{
+  mozilla::HoldJSObjects(this);
+}
 
 AuthenticatorAssertionResponse::~AuthenticatorAssertionResponse()
-{}
+{
+  mozilla::DropJSObjects(this);
+}
 
 JSObject*
 AuthenticatorAssertionResponse::WrapObject(JSContext* aCx,
                                            JS::Handle<JSObject*> aGivenProto)
 {
   return AuthenticatorAssertionResponseBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
 AuthenticatorAssertionResponse::GetAuthenticatorData(JSContext* aCx,
-                                                     JS::MutableHandle<JSObject*> aRetVal) const
+                                                     JS::MutableHandle<JSObject*> aRetVal)
 {
-  aRetVal.set(mAuthenticatorData.ToUint8Array(aCx));
+  if (!mAuthenticatorDataCachedObj) {
+    mAuthenticatorDataCachedObj = mAuthenticatorData.ToUint8Array(aCx);
+  }
+  aRetVal.set(mAuthenticatorDataCachedObj);
 }
 
 nsresult
 AuthenticatorAssertionResponse::SetAuthenticatorData(CryptoBuffer& aBuffer)
 {
   if (NS_WARN_IF(!mAuthenticatorData.Assign(aBuffer))) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
   return NS_OK;
 }
 
 void
 AuthenticatorAssertionResponse::GetSignature(JSContext* aCx,
-                                             JS::MutableHandle<JSObject*> aRetVal) const
+                                             JS::MutableHandle<JSObject*> aRetVal)
 {
-  aRetVal.set(mSignature.ToUint8Array(aCx));
+  if (!mSignatureCachedObj) {
+    mSignatureCachedObj = mSignature.ToUint8Array(aCx);
+  }
+  aRetVal.set(mSignatureCachedObj);
 }
 
 nsresult
 AuthenticatorAssertionResponse::SetSignature(CryptoBuffer& aBuffer)
 {
   if (NS_WARN_IF(!mSignature.Assign(aBuffer))) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
--- a/dom/webauthn/AuthenticatorAssertionResponse.h
+++ b/dom/webauthn/AuthenticatorAssertionResponse.h
@@ -18,39 +18,43 @@
 
 namespace mozilla {
 namespace dom {
 
 class AuthenticatorAssertionResponse final : public AuthenticatorResponse
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(AuthenticatorAssertionResponse,
+                                                         AuthenticatorResponse)
 
   explicit AuthenticatorAssertionResponse(nsPIDOMWindowInner* aParent);
 
 protected:
   ~AuthenticatorAssertionResponse() override;
 
 public:
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   void
-  GetAuthenticatorData(JSContext* aCx, JS::MutableHandle<JSObject*> aRetVal) const;
+  GetAuthenticatorData(JSContext* aCx, JS::MutableHandle<JSObject*> aRetVal);
 
   nsresult
   SetAuthenticatorData(CryptoBuffer& aBuffer);
 
   void
-  GetSignature(JSContext* aCx, JS::MutableHandle<JSObject*> aRetVal) const;
+  GetSignature(JSContext* aCx, JS::MutableHandle<JSObject*> aRetVal);
 
   nsresult
   SetSignature(CryptoBuffer& aBuffer);
 
 private:
   CryptoBuffer mAuthenticatorData;
+  JS::Heap<JSObject*> mAuthenticatorDataCachedObj;
   CryptoBuffer mSignature;
+  JS::Heap<JSObject*> mSignatureCachedObj;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_AuthenticatorAssertionResponse_h
--- a/dom/webauthn/AuthenticatorAttestationResponse.cpp
+++ b/dom/webauthn/AuthenticatorAttestationResponse.cpp
@@ -5,41 +5,65 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/WebAuthenticationBinding.h"
 #include "mozilla/dom/AuthenticatorAttestationResponse.h"
 
 namespace mozilla {
 namespace dom {
 
+NS_IMPL_CYCLE_COLLECTION_CLASS(AuthenticatorAttestationResponse)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(AuthenticatorAttestationResponse,
+                                                AuthenticatorResponse)
+  tmp->mAttestationObjectCachedObj = nullptr;
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(AuthenticatorAttestationResponse,
+                                               AuthenticatorResponse)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mAttestationObjectCachedObj)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(AuthenticatorAttestationResponse,
+                                                  AuthenticatorResponse)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
 NS_IMPL_ADDREF_INHERITED(AuthenticatorAttestationResponse, AuthenticatorResponse)
 NS_IMPL_RELEASE_INHERITED(AuthenticatorAttestationResponse, AuthenticatorResponse)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AuthenticatorAttestationResponse)
 NS_INTERFACE_MAP_END_INHERITING(AuthenticatorResponse)
 
 AuthenticatorAttestationResponse::AuthenticatorAttestationResponse(nsPIDOMWindowInner* aParent)
   : AuthenticatorResponse(aParent)
-{}
+  , mAttestationObjectCachedObj(nullptr)
+{
+  mozilla::HoldJSObjects(this);
+}
 
 AuthenticatorAttestationResponse::~AuthenticatorAttestationResponse()
-{}
+{
+  mozilla::DropJSObjects(this);
+}
 
 JSObject*
 AuthenticatorAttestationResponse::WrapObject(JSContext* aCx,
                                              JS::Handle<JSObject*> aGivenProto)
 {
   return AuthenticatorAttestationResponseBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
 AuthenticatorAttestationResponse::GetAttestationObject(JSContext* aCx,
-                                                       JS::MutableHandle<JSObject*> aRetVal) const
+                                                       JS::MutableHandle<JSObject*> aRetVal)
 {
-  aRetVal.set(mAttestationObject.ToUint8Array(aCx));
+  if (!mAttestationObjectCachedObj) {
+    mAttestationObjectCachedObj = mAttestationObject.ToUint8Array(aCx);
+  }
+  aRetVal.set(mAttestationObjectCachedObj);
 }
 
 nsresult
 AuthenticatorAttestationResponse::SetAttestationObject(CryptoBuffer& aBuffer)
 {
   if (NS_WARN_IF(!mAttestationObject.Assign(aBuffer))) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
--- a/dom/webauthn/AuthenticatorAttestationResponse.h
+++ b/dom/webauthn/AuthenticatorAttestationResponse.h
@@ -18,32 +18,35 @@
 
 namespace mozilla {
 namespace dom {
 
 class AuthenticatorAttestationResponse final : public AuthenticatorResponse
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(AuthenticatorAttestationResponse,
+                                                         AuthenticatorResponse)
 
   explicit AuthenticatorAttestationResponse(nsPIDOMWindowInner* aParent);
 
 protected:
   ~AuthenticatorAttestationResponse() override;
 
 public:
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   void
-  GetAttestationObject(JSContext* aCx, JS::MutableHandle<JSObject*> aRetVal) const;
+  GetAttestationObject(JSContext* aCx, JS::MutableHandle<JSObject*> aRetVal);
 
   nsresult
   SetAttestationObject(CryptoBuffer& aBuffer);
 
 private:
   CryptoBuffer mAttestationObject;
+  JS::Heap<JSObject*> mAttestationObjectCachedObj;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_AuthenticatorAttestationResponse_h
--- a/dom/webauthn/AuthenticatorResponse.cpp
+++ b/dom/webauthn/AuthenticatorResponse.cpp
@@ -5,44 +5,67 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/WebAuthenticationBinding.h"
 #include "mozilla/dom/AuthenticatorResponse.h"
 
 namespace mozilla {
 namespace dom {
 
-// Only needed for refcounted objects.
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AuthenticatorResponse, mParent)
+NS_IMPL_CYCLE_COLLECTION_CLASS(AuthenticatorResponse)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AuthenticatorResponse)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+  tmp->mClientDataJSONCachedObj = nullptr;
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(AuthenticatorResponse)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mClientDataJSONCachedObj)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AuthenticatorResponse)
+   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
 NS_IMPL_CYCLE_COLLECTING_ADDREF(AuthenticatorResponse)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(AuthenticatorResponse)
+
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AuthenticatorResponse)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 AuthenticatorResponse::AuthenticatorResponse(nsPIDOMWindowInner* aParent)
   : mParent(aParent)
-{}
+  , mClientDataJSONCachedObj(nullptr)
+{
+  mozilla::HoldJSObjects(this);
+}
 
 AuthenticatorResponse::~AuthenticatorResponse()
-{}
+{
+  mozilla::DropJSObjects(this);
+}
 
 JSObject*
 AuthenticatorResponse::WrapObject(JSContext* aCx,
                                   JS::Handle<JSObject*> aGivenProto)
 {
   return AuthenticatorResponseBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
 AuthenticatorResponse::GetClientDataJSON(JSContext* aCx,
-                                         JS::MutableHandle<JSObject*> aRetVal) const
+                                         JS::MutableHandle<JSObject*> aRetVal)
 {
-  aRetVal.set(mClientDataJSON.ToUint8Array(aCx));
+  if (!mClientDataJSONCachedObj) {
+    mClientDataJSONCachedObj = mClientDataJSON.ToUint8Array(aCx);
+  }
+  aRetVal.set(mClientDataJSONCachedObj);
 }
 
 nsresult
 AuthenticatorResponse::SetClientDataJSON(CryptoBuffer& aBuffer)
 {
   if (NS_WARN_IF(!mClientDataJSON.Assign(aBuffer))) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
--- a/dom/webauthn/AuthenticatorResponse.h
+++ b/dom/webauthn/AuthenticatorResponse.h
@@ -39,22 +39,23 @@ public:
 
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   void
   GetFormat(nsString& aRetVal) const;
 
   void
-  GetClientDataJSON(JSContext* aCx, JS::MutableHandle<JSObject*> aRetVal) const;
+  GetClientDataJSON(JSContext* aCx, JS::MutableHandle<JSObject*> aRetVal);
 
   nsresult
   SetClientDataJSON(CryptoBuffer& aBuffer);
 
 private:
   nsCOMPtr<nsPIDOMWindowInner> mParent;
   CryptoBuffer mClientDataJSON;
+  JS::Heap<JSObject*> mClientDataJSONCachedObj;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_AuthenticatorResponse_h
--- a/dom/webauthn/PublicKeyCredential.cpp
+++ b/dom/webauthn/PublicKeyCredential.cpp
@@ -6,46 +6,63 @@
 
 #include "mozilla/dom/PublicKeyCredential.h"
 #include "mozilla/dom/WebAuthenticationBinding.h"
 #include "nsCycleCollectionParticipant.h"
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED(PublicKeyCredential, Credential, mResponse)
+NS_IMPL_CYCLE_COLLECTION_CLASS(PublicKeyCredential)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PublicKeyCredential,
+                                                Credential)
+  tmp->mRawIdCachedObj = nullptr;
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(PublicKeyCredential, Credential)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mRawIdCachedObj)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PublicKeyCredential, Credential)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_ADDREF_INHERITED(PublicKeyCredential, Credential)
 NS_IMPL_RELEASE_INHERITED(PublicKeyCredential, Credential)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PublicKeyCredential)
 NS_INTERFACE_MAP_END_INHERITING(Credential)
 
-NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(PublicKeyCredential, Credential)
-NS_IMPL_CYCLE_COLLECTION_TRACE_END
-
 PublicKeyCredential::PublicKeyCredential(nsPIDOMWindowInner* aParent)
   : Credential(aParent)
-{}
+  , mRawIdCachedObj(nullptr)
+{
+  mozilla::HoldJSObjects(this);
+}
 
 PublicKeyCredential::~PublicKeyCredential()
-{}
+{
+  mozilla::DropJSObjects(this);
+}
 
 JSObject*
 PublicKeyCredential::WrapObject(JSContext* aCx,
                                 JS::Handle<JSObject*> aGivenProto)
 {
   return PublicKeyCredentialBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
 PublicKeyCredential::GetRawId(JSContext* aCx,
-                              JS::MutableHandle<JSObject*> aRetVal) const
+                              JS::MutableHandle<JSObject*> aRetVal)
 {
-  aRetVal.set(mRawId.ToUint8Array(aCx));
+  if (!mRawIdCachedObj) {
+    mRawIdCachedObj = mRawId.ToUint8Array(aCx);
+  }
+  aRetVal.set(mRawIdCachedObj);
 }
 
 already_AddRefed<AuthenticatorResponse>
 PublicKeyCredential::Response() const
 {
   RefPtr<AuthenticatorResponse> temp(mResponse);
   return temp.forget();
 }
--- a/dom/webauthn/PublicKeyCredential.h
+++ b/dom/webauthn/PublicKeyCredential.h
@@ -30,29 +30,30 @@ public:
 protected:
   ~PublicKeyCredential() override;
 
 public:
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   void
-  GetRawId(JSContext* cx, JS::MutableHandle<JSObject*> aRetVal) const;
+  GetRawId(JSContext* cx, JS::MutableHandle<JSObject*> aRetVal);
 
   already_AddRefed<AuthenticatorResponse>
   Response() const;
 
   nsresult
   SetRawId(CryptoBuffer& aBuffer);
 
   void
   SetResponse(RefPtr<AuthenticatorResponse>);
 
 private:
   CryptoBuffer mRawId;
+  JS::Heap<JSObject*> mRawIdCachedObj;
   RefPtr<AuthenticatorResponse> mResponse;
   // Extensions are not supported yet.
   // <some type> mClientExtensionResults;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/webauthn/tests/test_webauthn_loopback.html
+++ b/dom/webauthn/tests/test_webauthn_loopback.html
@@ -51,16 +51,21 @@ function() {
        - clientExtensionResults: (not yet supported)
     */
 
     is(aCredInfo.type, "public-key", "Credential type must be public-key")
 
     ok(aCredInfo.rawId.length > 0, "Key ID exists");
     is(aCredInfo.id, bytesToBase64UrlSafe(aCredInfo.rawId), "Encoded Key ID and Raw Key ID match");
 
+    ok(aCredInfo.rawId === aCredInfo.rawId, "PublicKeyCredential.RawID is SameObject");
+    ok(aCredInfo.response === aCredInfo.response, "PublicKeyCredential.Response is SameObject");
+    ok(aCredInfo.response.clientDataJSON === aCredInfo.response.clientDataJSON, "PublicKeyCredential.Response.ClientDataJSON is SameObject");
+    ok(aCredInfo.response.attestationObject === aCredInfo.response.attestationObject, "PublicKeyCredential.Response.AttestationObject is SameObject");
+
     let clientData = JSON.parse(buffer2string(aCredInfo.response.clientDataJSON));
     is(clientData.challenge, bytesToBase64UrlSafe(gCredentialChallenge), "Challenge is correct");
     // WD-05 vs. WD-06: In WD-06, the second parameter should be "window.location.origin". Fix
     // this in Bug 1384776
     is(clientData.origin, document.domain, "Origin is correct");
     is(clientData.hashAlg, "SHA-256", "Hash algorithm is correct");
 
     return webAuthnDecodeCBORAttestation(aCredInfo.response.attestationObject.buffer)
@@ -85,16 +90,19 @@ function() {
          - signature: U2F Sign() Response
     */
 
     is(aAssertion.type, "public-key", "Credential type must be public-key")
 
     ok(aAssertion.rawId.length > 0, "Key ID exists");
     is(aAssertion.id, bytesToBase64UrlSafe(aAssertion.rawId), "Encoded Key ID and Raw Key ID match");
 
+    ok(aAssertion.response.authenticatorData === aAssertion.response.authenticatorData, "AuthenticatorAssertionResponse.AuthenticatorData is SameObject");
+    ok(aAssertion.response.signature === aAssertion.response.signature, "AuthenticatorAssertionResponse.Signature is SameObject");
+
     ok(aAssertion.response.authenticatorData.length > 0, "Authenticator data exists");
     let clientData = JSON.parse(buffer2string(aAssertion.response.clientDataJSON));
     is(clientData.challenge, bytesToBase64UrlSafe(gAssertionChallenge), "Challenge is correct");
     // WD-05 vs. WD-06: In WD-06, the second parameter should be "window.location.origin". Fix
     // this in Bug 1384776
     is(clientData.origin, document.domain, "Origin is correct");
     is(clientData.hashAlg, "SHA-256", "Hash algorithm is correct");