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
--- 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");