/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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/. */ #include "AuthrsBridge_ffi.h" #include "mozilla/Base64.h" #include "mozilla/HoldDropJSObjects.h" #include "mozilla/dom/AuthenticatorAttestationResponse.h" #include "mozilla/dom/WebAuthenticationBinding.h" namespace mozilla::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 aGivenProto) { return AuthenticatorAttestationResponse_Binding::Wrap(aCx, this, aGivenProto); } void AuthenticatorAttestationResponse::GetAttestationObject( JSContext* aCx, JS::MutableHandle aValue, ErrorResult& aRv) { if (!mAttestationObjectCachedObj) { mAttestationObjectCachedObj = ArrayBuffer::Create(aCx, mAttestationObject, aRv); if (aRv.Failed()) { return; } } aValue.set(mAttestationObjectCachedObj); } void AuthenticatorAttestationResponse::SetAttestationObject( const nsTArray& aBuffer) { mAttestationObject.Assign(aBuffer); } void AuthenticatorAttestationResponse::GetTransports( nsTArray& aTransports) { aTransports.Assign(mTransports); } void AuthenticatorAttestationResponse::SetTransports( const nsTArray& aTransports) { mTransports.Assign(aTransports); } nsresult AuthenticatorAttestationResponse::GetAuthenticatorDataBytes( nsTArray& aAuthenticatorData) { if (!mAttestationObjectParsed) { nsresult rv = authrs_webauthn_att_obj_constructor( mAttestationObject, /* anonymize */ false, getter_AddRefs(mAttestationObjectParsed)); if (NS_FAILED(rv)) { return rv; } } nsresult rv = mAttestationObjectParsed->GetAuthenticatorData(aAuthenticatorData); if (NS_FAILED(rv)) { return rv; } return NS_OK; } void AuthenticatorAttestationResponse::GetAuthenticatorData( JSContext* aCx, JS::MutableHandle aValue, ErrorResult& aRv) { nsTArray authenticatorData; nsresult rv = GetAuthenticatorDataBytes(authenticatorData); if (NS_FAILED(rv)) { aRv.Throw(rv); return; } JS::Heap buffer(ArrayBuffer::Create(aCx, authenticatorData, aRv)); if (aRv.Failed()) { return; } aValue.set(buffer); } nsresult AuthenticatorAttestationResponse::GetPublicKeyBytes( nsTArray& aPublicKeyBytes) { if (!mAttestationObjectParsed) { nsresult rv = authrs_webauthn_att_obj_constructor( mAttestationObject, /* anonymize */ false, getter_AddRefs(mAttestationObjectParsed)); if (NS_FAILED(rv)) { return rv; } } nsresult rv = mAttestationObjectParsed->GetPublicKey(aPublicKeyBytes); if (NS_FAILED(rv)) { return rv; } return NS_OK; } void AuthenticatorAttestationResponse::GetPublicKey( JSContext* aCx, JS::MutableHandle aValue, ErrorResult& aRv) { nsTArray publicKey; nsresult rv = GetPublicKeyBytes(publicKey); if (NS_FAILED(rv)) { if (rv == NS_ERROR_NOT_AVAILABLE) { aValue.set(nullptr); } else { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); } return; } JS::Heap buffer(ArrayBuffer::Create(aCx, publicKey, aRv)); if (aRv.Failed()) { return; } aValue.set(buffer); } COSEAlgorithmIdentifier AuthenticatorAttestationResponse::GetPublicKeyAlgorithm( ErrorResult& aRv) { if (!mAttestationObjectParsed) { nsresult rv = authrs_webauthn_att_obj_constructor( mAttestationObject, false, getter_AddRefs(mAttestationObjectParsed)); if (NS_FAILED(rv)) { aRv.Throw(rv); return 0; } } COSEAlgorithmIdentifier alg; mAttestationObjectParsed->GetPublicKeyAlgorithm(&alg); return alg; } void AuthenticatorAttestationResponse::ToJSON( AuthenticatorAttestationResponseJSON& aJSON, ErrorResult& aError) { nsAutoCString clientDataJSONBase64; nsresult rv = Base64URLEncode( mClientDataJSON.Length(), reinterpret_cast(mClientDataJSON.get()), mozilla::Base64URLEncodePaddingPolicy::Omit, clientDataJSONBase64); // This will only fail if the length is so long that it overflows 32-bits // when calculating the encoded size. if (NS_FAILED(rv)) { aError.ThrowDataError("clientDataJSON too long"); return; } aJSON.mClientDataJSON.Assign(NS_ConvertUTF8toUTF16(clientDataJSONBase64)); nsTArray authenticatorData; rv = GetAuthenticatorDataBytes(authenticatorData); if (NS_FAILED(rv)) { aError.ThrowUnknownError("could not get authenticatorData"); return; } nsAutoCString authenticatorDataBase64; rv = Base64URLEncode(authenticatorData.Length(), authenticatorData.Elements(), mozilla::Base64URLEncodePaddingPolicy::Omit, authenticatorDataBase64); if (NS_FAILED(rv)) { aError.ThrowDataError("authenticatorData too long"); return; } aJSON.mAuthenticatorData.Assign( NS_ConvertUTF8toUTF16(authenticatorDataBase64)); if (!aJSON.mTransports.Assign(mTransports, mozilla::fallible)) { aError.Throw(NS_ERROR_OUT_OF_MEMORY); return; } nsTArray publicKeyBytes; rv = GetPublicKeyBytes(publicKeyBytes); if (NS_SUCCEEDED(rv)) { nsAutoCString publicKeyBytesBase64; rv = Base64URLEncode(publicKeyBytes.Length(), publicKeyBytes.Elements(), mozilla::Base64URLEncodePaddingPolicy::Omit, publicKeyBytesBase64); if (NS_FAILED(rv)) { aError.ThrowDataError("publicKey too long"); return; } aJSON.mPublicKey.Construct(NS_ConvertUTF8toUTF16(publicKeyBytesBase64)); } else if (rv != NS_ERROR_NOT_AVAILABLE) { aError.ThrowUnknownError("could not get publicKey"); return; } COSEAlgorithmIdentifier publicKeyAlgorithm = GetPublicKeyAlgorithm(aError); if (aError.Failed()) { aError.ThrowUnknownError("could not get publicKeyAlgorithm"); return; } aJSON.mPublicKeyAlgorithm = publicKeyAlgorithm; nsAutoCString attestationObjectBase64; rv = Base64URLEncode( mAttestationObject.Length(), mAttestationObject.Elements(), mozilla::Base64URLEncodePaddingPolicy::Omit, attestationObjectBase64); if (NS_FAILED(rv)) { aError.ThrowDataError("attestationObject too long"); return; } aJSON.mAttestationObject.Assign( NS_ConvertUTF8toUTF16(attestationObjectBase64)); } } // namespace mozilla::dom