1314 lines
49 KiB
C++
1314 lines
49 KiB
C++
/* -*- 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 "MLSGroupView.h"
|
|
#include "mozilla/dom/MLSBinding.h"
|
|
#include "mozilla/dom/TypedArray.h"
|
|
#include "mozilla/dom/Promise.h"
|
|
#include "nsTArray.h"
|
|
#include "mozilla/dom/MLSTransactionChild.h"
|
|
#include "mozilla/dom/MLSTransactionMessage.h"
|
|
#include "ipc/IPCMessageUtilsSpecializations.h"
|
|
#include "mozilla/BasePrincipal.h"
|
|
#include "nsTArray.h"
|
|
#include "mozilla/Logging.h"
|
|
#include "mozilla/Span.h"
|
|
#include "nsDebug.h"
|
|
#include "MLSTypeUtils.h"
|
|
namespace mozilla::dom {
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WITH_JS_MEMBERS(MLSGroupView, (mMLS),
|
|
(mJsGroupId, mJsClientId))
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(MLSGroupView)
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(MLSGroupView)
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MLSGroupView)
|
|
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
|
NS_INTERFACE_MAP_END
|
|
|
|
// Setup logging
|
|
extern mozilla::LazyLogModule gMlsLog;
|
|
|
|
MLSGroupView::MLSGroupView(MLS* aMLS, nsTArray<uint8_t>&& aGroupId,
|
|
nsTArray<uint8_t>&& aClientId)
|
|
: mMLS(aMLS),
|
|
mGroupId(std::move(aGroupId)),
|
|
mClientId(std::move(aClientId)) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLSGroupView::MLSGroupView()"));
|
|
|
|
// Indicate that the object holds JS objects
|
|
mozilla::HoldJSObjects(this);
|
|
}
|
|
|
|
JSObject* MLSGroupView::WrapObject(JSContext* aCx,
|
|
JS::Handle<JSObject*> aGivenProto) {
|
|
return MLSGroupView_Binding::Wrap(aCx, this, aGivenProto);
|
|
}
|
|
|
|
//
|
|
// API
|
|
//
|
|
|
|
void MLSGroupView::GetGroupId(JSContext* aCx,
|
|
JS::MutableHandle<JSObject*> aGroupId,
|
|
ErrorResult& aRv) {
|
|
if (!mJsGroupId) {
|
|
mJsGroupId = Uint8Array::Create(aCx, this, mGroupId, aRv);
|
|
if (aRv.Failed()) {
|
|
return;
|
|
}
|
|
}
|
|
aGroupId.set(mJsGroupId);
|
|
}
|
|
|
|
void MLSGroupView::GetClientId(JSContext* aCx,
|
|
JS::MutableHandle<JSObject*> aClientId,
|
|
ErrorResult& aRv) {
|
|
if (!mJsClientId) {
|
|
mJsClientId = Uint8Array::Create(aCx, this, mClientId, aRv);
|
|
if (aRv.Failed()) {
|
|
return;
|
|
}
|
|
}
|
|
aClientId.set(mJsClientId);
|
|
}
|
|
|
|
already_AddRefed<Promise> MLSGroupView::DeleteState(ErrorResult& aRv) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLSGroupView::DeleteState()"));
|
|
|
|
// Create a new Promise object for the result
|
|
RefPtr<Promise> promise = Promise::Create(mMLS->GetParentObject(), aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
mMLS->mTransactionChild->SendRequestGroupStateDelete(mGroupId, mClientId)
|
|
->Then(
|
|
GetCurrentSerialEventTarget(), __func__,
|
|
[promise](
|
|
Maybe<mozilla::security::mls::GkGroupIdEpoch>&& groupIdEpoch) {
|
|
// Check if the value is Nothing or Some with an empty group_epoch
|
|
if (groupIdEpoch.isNothing()) {
|
|
promise->MaybeRejectWithUnknownError(
|
|
"Failed to delete group state");
|
|
return;
|
|
}
|
|
|
|
// Check if the epoch is 0xFFFF..FF
|
|
bool isMaxEpoch =
|
|
std::all_of(groupIdEpoch->group_epoch.begin(),
|
|
groupIdEpoch->group_epoch.end(),
|
|
[](uint8_t byte) { return byte == 0xFF; });
|
|
|
|
// If the epoch is 0xFFFF..FF, then the group has been deleted
|
|
if (isMaxEpoch) {
|
|
promise->MaybeResolveWithUndefined();
|
|
} else {
|
|
promise->MaybeRejectWithUnknownError(
|
|
"Group has not been deleted");
|
|
}
|
|
},
|
|
[promise](::mozilla::ipc::ResponseRejectReason aReason) {
|
|
promise->MaybeRejectWithUnknownError(
|
|
"Failed to delete group state");
|
|
});
|
|
|
|
return promise.forget();
|
|
}
|
|
|
|
already_AddRefed<Promise> MLSGroupView::Add(
|
|
const MLSBytesOrUint8Array& aJsKeyPackage, ErrorResult& aRv) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLSGroupView::Add()"));
|
|
|
|
// Handle the key package parameter
|
|
nsTArray<uint8_t> keyPackage = ExtractMLSBytesOrUint8Array(
|
|
MLSObjectType::Key_package, aJsKeyPackage, aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Check if the key package is empty
|
|
if (NS_WARN_IF(keyPackage.IsEmpty())) {
|
|
aRv.ThrowTypeError("The key package must not be empty");
|
|
return nullptr;
|
|
}
|
|
|
|
// Create a new Promise object for the result
|
|
RefPtr<Promise> promise = Promise::Create(mMLS->GetParentObject(), aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
mMLS->mTransactionChild->SendRequestGroupAdd(mGroupId, mClientId, keyPackage)
|
|
->Then(
|
|
GetCurrentSerialEventTarget(), __func__,
|
|
[promise, self = RefPtr<MLSGroupView>(this)](
|
|
Maybe<mozilla::security::mls::GkMlsCommitOutput>&& commitOutput) {
|
|
// Check if the value is Nothing
|
|
if (commitOutput.isNothing()) {
|
|
promise->MaybeReject(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
|
|
// Get the context from the GlobalObject
|
|
AutoJSAPI jsapi;
|
|
if (NS_WARN_IF(!jsapi.Init(self->mMLS->GetParentObject()))) {
|
|
promise->MaybeReject(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
JSContext* cx = jsapi.cx();
|
|
|
|
// Construct the Uint8Array objects
|
|
ErrorResult error;
|
|
JS::Rooted<JSObject*> jsGroupId(
|
|
cx, Uint8Array::Create(cx, self->mGroupId, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
JS::Rooted<JSObject*> jsClientId(
|
|
cx, Uint8Array::Create(cx, commitOutput->identity, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
JS::Rooted<JSObject*> jsCommit(
|
|
cx, Uint8Array::Create(cx, commitOutput->commit, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
JS::Rooted<JSObject*> jsWelcome(
|
|
cx, Uint8Array::Create(cx, commitOutput->welcome, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
JS::Rooted<JSObject*> jsGroupInfo(
|
|
cx, Uint8Array::Create(cx, commitOutput->group_info, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
JS::Rooted<JSObject*> jsRatchetTree(
|
|
cx, Uint8Array::Create(cx, commitOutput->ratchet_tree, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
|
|
// Construct MLSCommitOutput with the parsed data
|
|
RootedDictionary<MLSCommitOutput> rvalue(cx);
|
|
rvalue.mType = MLSObjectType::Commit_output;
|
|
rvalue.mGroupId.Init(jsGroupId);
|
|
rvalue.mCommit.Init(jsCommit);
|
|
if (!commitOutput->welcome.IsEmpty()) {
|
|
rvalue.mWelcome.Construct();
|
|
rvalue.mWelcome.Value().Init(jsWelcome);
|
|
}
|
|
if (!commitOutput->group_info.IsEmpty()) {
|
|
rvalue.mGroupInfo.Construct();
|
|
rvalue.mGroupInfo.Value().Init(jsGroupInfo);
|
|
}
|
|
if (!commitOutput->ratchet_tree.IsEmpty()) {
|
|
rvalue.mRatchetTree.Construct();
|
|
rvalue.mRatchetTree.Value().Init(jsRatchetTree);
|
|
}
|
|
if (!commitOutput->identity.IsEmpty()) {
|
|
rvalue.mClientId.Construct();
|
|
rvalue.mClientId.Value().Init(jsClientId);
|
|
}
|
|
|
|
// Resolve the promise
|
|
promise->MaybeResolve(rvalue);
|
|
},
|
|
[promise](::mozilla::ipc::ResponseRejectReason aReason) {
|
|
promise->MaybeRejectWithUnknownError("Failed to add to group");
|
|
});
|
|
|
|
return promise.forget();
|
|
}
|
|
|
|
already_AddRefed<Promise> MLSGroupView::ProposeAdd(
|
|
const MLSBytesOrUint8Array& aJsKeyPackage, ErrorResult& aRv) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLSGroupView::ProposeAdd()"));
|
|
|
|
// Handle the key package parameter
|
|
nsTArray<uint8_t> keyPackage = ExtractMLSBytesOrUint8Array(
|
|
MLSObjectType::Key_package, aJsKeyPackage, aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Check if the key package is empty
|
|
if (NS_WARN_IF(keyPackage.IsEmpty())) {
|
|
aRv.ThrowTypeError("The key package must not be empty");
|
|
return nullptr;
|
|
}
|
|
|
|
// Create a new Promise object for the result
|
|
RefPtr<Promise> promise = Promise::Create(mMLS->GetParentObject(), aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
mMLS->mTransactionChild
|
|
->SendRequestGroupProposeAdd(mGroupId, mClientId, keyPackage)
|
|
->Then(
|
|
GetCurrentSerialEventTarget(), __func__,
|
|
[promise,
|
|
self = RefPtr<MLSGroupView>(this)](Maybe<RawBytes>&& proposal) {
|
|
// Check if the value is Nothing
|
|
if (proposal.isNothing()) {
|
|
promise->MaybeRejectWithUnknownError(
|
|
"Failed to propose add to group");
|
|
return;
|
|
}
|
|
|
|
// Get the context from the GlobalObject
|
|
AutoJSAPI jsapi;
|
|
if (NS_WARN_IF(!jsapi.Init(self->mMLS->GetParentObject()))) {
|
|
promise->MaybeReject(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
JSContext* cx = jsapi.cx();
|
|
|
|
// Construct the Uint8Array object
|
|
ErrorResult error;
|
|
JS::Rooted<JSObject*> content(
|
|
cx, Uint8Array::Create(cx, proposal->data(), error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
|
|
// Construct MLSBytes with the proposal as content
|
|
RootedDictionary<MLSBytes> rvalue(cx);
|
|
rvalue.mType = MLSObjectType::Proposal;
|
|
rvalue.mContent.Init(content);
|
|
|
|
// Resolve the promise
|
|
promise->MaybeResolve(rvalue);
|
|
},
|
|
[promise](::mozilla::ipc::ResponseRejectReason aReason) {
|
|
promise->MaybeRejectWithUnknownError(
|
|
"Failed to propose add to group");
|
|
});
|
|
|
|
return promise.forget();
|
|
}
|
|
|
|
already_AddRefed<Promise> MLSGroupView::Remove(
|
|
const MLSBytesOrUint8Array& aJsRemClientIdentifier, ErrorResult& aRv) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLSGroupView::Remove()"));
|
|
|
|
// Handle the remove client identifier parameter
|
|
nsTArray<uint8_t> remClientIdentifier = ExtractMLSBytesOrUint8Array(
|
|
MLSObjectType::Client_identifier, aJsRemClientIdentifier, aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Check if the remove client identifier is empty
|
|
if (NS_WARN_IF(remClientIdentifier.IsEmpty())) {
|
|
aRv.ThrowTypeError("The remove client identifier must not be empty");
|
|
return nullptr;
|
|
}
|
|
|
|
// Create a new Promise object for the result
|
|
RefPtr<Promise> promise = Promise::Create(mMLS->GetParentObject(), aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Use the static method or instance to send the IPC message
|
|
mMLS->mTransactionChild
|
|
->SendRequestGroupRemove(mGroupId, mClientId, remClientIdentifier)
|
|
->Then(
|
|
GetCurrentSerialEventTarget(), __func__,
|
|
[promise, self = RefPtr<MLSGroupView>(this)](
|
|
Maybe<mozilla::security::mls::GkMlsCommitOutput>&& commitOutput) {
|
|
// Check if the value is Nothing
|
|
if (commitOutput.isNothing()) {
|
|
promise->MaybeReject(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
|
|
// Get the context from the GlobalObject
|
|
AutoJSAPI jsapi;
|
|
if (NS_WARN_IF(!jsapi.Init(self->mMLS->GetParentObject()))) {
|
|
promise->MaybeReject(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
JSContext* cx = jsapi.cx();
|
|
|
|
// Construct the Uint8Array objects
|
|
ErrorResult error;
|
|
JS::Rooted<JSObject*> jsGroupId(
|
|
cx, Uint8Array::Create(cx, self->mGroupId, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
JS::Rooted<JSObject*> jsClientId(
|
|
cx, Uint8Array::Create(cx, commitOutput->identity, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
JS::Rooted<JSObject*> jsCommit(
|
|
cx, Uint8Array::Create(cx, commitOutput->commit, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
JS::Rooted<JSObject*> jsWelcome(
|
|
cx, Uint8Array::Create(cx, commitOutput->welcome, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
JS::Rooted<JSObject*> jsGroupInfo(
|
|
cx, Uint8Array::Create(cx, commitOutput->group_info, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
JS::Rooted<JSObject*> jsRatchetTree(
|
|
cx, Uint8Array::Create(cx, commitOutput->ratchet_tree, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
|
|
// Construct MLSCommitOutput with the parsed data
|
|
RootedDictionary<MLSCommitOutput> rvalue(cx);
|
|
rvalue.mType = MLSObjectType::Commit_output;
|
|
rvalue.mGroupId.Init(jsGroupId);
|
|
rvalue.mCommit.Init(jsCommit);
|
|
if (!commitOutput->welcome.IsEmpty()) {
|
|
rvalue.mWelcome.Construct();
|
|
rvalue.mWelcome.Value().Init(jsWelcome);
|
|
}
|
|
if (!commitOutput->group_info.IsEmpty()) {
|
|
rvalue.mGroupInfo.Construct();
|
|
rvalue.mGroupInfo.Value().Init(jsGroupInfo);
|
|
}
|
|
if (!commitOutput->ratchet_tree.IsEmpty()) {
|
|
rvalue.mRatchetTree.Construct();
|
|
rvalue.mRatchetTree.Value().Init(jsRatchetTree);
|
|
}
|
|
if (!commitOutput->identity.IsEmpty()) {
|
|
rvalue.mClientId.Construct();
|
|
rvalue.mClientId.Value().Init(jsClientId);
|
|
}
|
|
|
|
// Resolve the promise
|
|
promise->MaybeResolve(rvalue);
|
|
},
|
|
[promise](::mozilla::ipc::ResponseRejectReason aReason) {
|
|
promise->MaybeRejectWithUnknownError("Failed to remove from group");
|
|
});
|
|
|
|
return promise.forget();
|
|
}
|
|
|
|
already_AddRefed<Promise> MLSGroupView::ProposeRemove(
|
|
const MLSBytesOrUint8Array& aJsRemClientIdentifier, ErrorResult& aRv) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLSGroupView::ProposeRemove()"));
|
|
|
|
// Handle the remove client identifier parameter
|
|
nsTArray<uint8_t> remClientIdentifier = ExtractMLSBytesOrUint8Array(
|
|
MLSObjectType::Client_identifier, aJsRemClientIdentifier, aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Check if the removed client identifier is empty
|
|
if (NS_WARN_IF(remClientIdentifier.IsEmpty())) {
|
|
aRv.ThrowTypeError("The removed client identifier must not be empty");
|
|
return nullptr;
|
|
}
|
|
|
|
// Create a new Promise object for the result
|
|
RefPtr<Promise> promise = Promise::Create(mMLS->GetParentObject(), aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
mMLS->mTransactionChild
|
|
->SendRequestGroupProposeRemove(mGroupId, mClientId, remClientIdentifier)
|
|
->Then(
|
|
GetCurrentSerialEventTarget(), __func__,
|
|
[promise,
|
|
self = RefPtr<MLSGroupView>(this)](Maybe<RawBytes>&& proposal) {
|
|
// Check if the value is Nothing
|
|
if (proposal.isNothing()) {
|
|
promise->MaybeReject(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
|
|
// Get the context from the GlobalObject
|
|
AutoJSAPI jsapi;
|
|
if (NS_WARN_IF(!jsapi.Init(self->mMLS->GetParentObject()))) {
|
|
promise->MaybeReject(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
JSContext* cx = jsapi.cx();
|
|
|
|
// Construct the Uint8Array object
|
|
ErrorResult error;
|
|
JS::Rooted<JSObject*> content(
|
|
cx, Uint8Array::Create(cx, proposal->data(), error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
|
|
// Construct MLSBytes with the proposal as content
|
|
RootedDictionary<MLSBytes> rvalue(cx);
|
|
rvalue.mType = MLSObjectType::Proposal;
|
|
rvalue.mContent.Init(content);
|
|
|
|
// Resolve the promise
|
|
promise->MaybeResolve(rvalue);
|
|
},
|
|
[promise](::mozilla::ipc::ResponseRejectReason aReason) {
|
|
promise->MaybeRejectWithUnknownError(
|
|
"Failed to propose remove from group");
|
|
});
|
|
|
|
return promise.forget();
|
|
}
|
|
|
|
already_AddRefed<Promise> MLSGroupView::Close(ErrorResult& aRv) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLSGroupView::Close()"));
|
|
|
|
// Create a new Promise object for the result
|
|
RefPtr<Promise> promise = Promise::Create(mMLS->GetParentObject(), aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
mMLS->mTransactionChild->SendRequestGroupClose(mGroupId, mClientId)
|
|
->Then(
|
|
GetCurrentSerialEventTarget(), __func__,
|
|
[promise, self = RefPtr<MLSGroupView>(this)](
|
|
Maybe<mozilla::security::mls::GkMlsCommitOutput>&& commitOutput) {
|
|
// Check if the value is Nothing
|
|
if (commitOutput.isNothing()) {
|
|
promise->MaybeReject(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
|
|
// Get the context from the GlobalObject
|
|
AutoJSAPI jsapi;
|
|
if (NS_WARN_IF(!jsapi.Init(self->mMLS->GetParentObject()))) {
|
|
promise->MaybeReject(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
JSContext* cx = jsapi.cx();
|
|
|
|
// Construct the Uint8Array objects
|
|
ErrorResult error;
|
|
JS::Rooted<JSObject*> jsGroupId(
|
|
cx, Uint8Array::Create(cx, self->mGroupId, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
JS::Rooted<JSObject*> jsClientId(
|
|
cx, Uint8Array::Create(cx, commitOutput->identity, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
JS::Rooted<JSObject*> jsCommit(
|
|
cx, Uint8Array::Create(cx, commitOutput->commit, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
JS::Rooted<JSObject*> jsWelcome(
|
|
cx, Uint8Array::Create(cx, commitOutput->welcome, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
JS::Rooted<JSObject*> jsGroupInfo(
|
|
cx, Uint8Array::Create(cx, commitOutput->group_info, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
JS::Rooted<JSObject*> jsRatchetTree(
|
|
cx, Uint8Array::Create(cx, commitOutput->ratchet_tree, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
|
|
// Construct MLSCommitOutput with the parsed data
|
|
RootedDictionary<MLSCommitOutput> rvalue(cx);
|
|
rvalue.mType = MLSObjectType::Commit_output;
|
|
rvalue.mGroupId.Init(jsGroupId);
|
|
rvalue.mCommit.Init(jsCommit);
|
|
if (!commitOutput->welcome.IsEmpty()) {
|
|
rvalue.mWelcome.Construct();
|
|
rvalue.mWelcome.Value().Init(jsWelcome);
|
|
}
|
|
if (!commitOutput->group_info.IsEmpty()) {
|
|
rvalue.mGroupInfo.Construct();
|
|
rvalue.mGroupInfo.Value().Init(jsGroupInfo);
|
|
}
|
|
if (!commitOutput->ratchet_tree.IsEmpty()) {
|
|
rvalue.mRatchetTree.Construct();
|
|
rvalue.mRatchetTree.Value().Init(jsRatchetTree);
|
|
}
|
|
if (!commitOutput->identity.IsEmpty()) {
|
|
rvalue.mClientId.Construct();
|
|
rvalue.mClientId.Value().Init(jsClientId);
|
|
}
|
|
|
|
// Resolve the promise
|
|
promise->MaybeResolve(rvalue);
|
|
},
|
|
[promise](::mozilla::ipc::ResponseRejectReason aReason) {
|
|
promise->MaybeRejectWithUnknownError("Failed to close group");
|
|
});
|
|
|
|
return promise.forget();
|
|
}
|
|
|
|
already_AddRefed<Promise> MLSGroupView::Details(ErrorResult& aRv) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLSGroupView::Details()"));
|
|
|
|
// Create a new Promise object for the result
|
|
RefPtr<Promise> promise = Promise::Create(mMLS->GetParentObject(), aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
mMLS->mTransactionChild->SendRequestGroupDetails(mGroupId, mClientId)
|
|
->Then(
|
|
GetCurrentSerialEventTarget(), __func__,
|
|
[promise, self = RefPtr<MLSGroupView>(this)](
|
|
Maybe<GkGroupDetails>&& groupDetails) {
|
|
// Check if the value is Nothing
|
|
if (groupDetails.isNothing()) {
|
|
promise->MaybeReject(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
|
|
// Get the context from the GlobalObject
|
|
AutoJSAPI jsapi;
|
|
if (NS_WARN_IF(!jsapi.Init(self->mMLS->GetParentObject()))) {
|
|
promise->MaybeReject(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
JSContext* cx = jsapi.cx();
|
|
|
|
// Construct the Uint8Array objects
|
|
ErrorResult error;
|
|
JS::Rooted<JSObject*> jsGroupId(
|
|
cx, Uint8Array::Create(cx, groupDetails->group_id, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
JS::Rooted<JSObject*> jsGroupEpoch(
|
|
cx, Uint8Array::Create(cx, groupDetails->group_epoch, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
|
|
// Construct MLSGroupDetails
|
|
RootedDictionary<MLSGroupDetails> rvalue(cx);
|
|
rvalue.mType = MLSObjectType::Group_info;
|
|
rvalue.mGroupId.Init(jsGroupId);
|
|
rvalue.mGroupEpoch.Init(jsGroupEpoch);
|
|
if (!rvalue.mMembers.SetCapacity(
|
|
groupDetails->group_members.Length(), fallible)) {
|
|
promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
|
|
return;
|
|
}
|
|
|
|
for (const auto& member : groupDetails->group_members) {
|
|
JS::Rooted<JSObject*> jsClientId(
|
|
cx, Uint8Array::Create(cx, member.identity, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
JS::Rooted<JSObject*> jsCredential(
|
|
cx, Uint8Array::Create(cx, member.credential, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
|
|
// Guaranteed not to fail because of the SetCapacity above.
|
|
MLSGroupMember& jsMember =
|
|
*rvalue.mMembers.AppendElement(fallible);
|
|
jsMember.mClientId.Init(jsClientId);
|
|
jsMember.mCredential.Init(jsCredential);
|
|
}
|
|
|
|
// Resolve the promise
|
|
promise->MaybeResolve(rvalue);
|
|
},
|
|
[promise](::mozilla::ipc::ResponseRejectReason aReason) {
|
|
promise->MaybeRejectWithUnknownError("Failed to get group details");
|
|
});
|
|
|
|
return promise.forget();
|
|
}
|
|
|
|
already_AddRefed<Promise> MLSGroupView::Send(
|
|
const MLSBytesOrUint8ArrayOrUTF8String& aJsMessage, ErrorResult& aRv) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLSGroupView::Send()"));
|
|
|
|
// Handle the message parameter
|
|
nsTArray<uint8_t> message = ExtractMLSBytesOrUint8ArrayOrUTF8String(
|
|
MLSObjectType::Application_message_plaintext, aJsMessage, aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Create a new Promise object for the result
|
|
RefPtr<Promise> promise = Promise::Create(mMLS->GetParentObject(), aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
mMLS->mTransactionChild->SendRequestSend(mGroupId, mClientId, message)
|
|
->Then(
|
|
GetCurrentSerialEventTarget(), __func__,
|
|
[promise,
|
|
self = RefPtr<MLSGroupView>(this)](Maybe<RawBytes>&& result) {
|
|
// Check if the value is Nothing
|
|
if (result.isNothing()) {
|
|
promise->MaybeReject(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
|
|
// Get the context from the GlobalObject
|
|
AutoJSAPI jsapi;
|
|
if (NS_WARN_IF(!jsapi.Init(self->mMLS->GetParentObject()))) {
|
|
promise->MaybeReject(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
JSContext* cx = jsapi.cx();
|
|
|
|
// Construct the Uint8Array object
|
|
ErrorResult error;
|
|
JS::Rooted<JSObject*> content(
|
|
cx, Uint8Array::Create(cx, result->data(), error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
|
|
// Construct MLSBytes with the group identifier as content
|
|
RootedDictionary<MLSBytes> rvalue(cx);
|
|
rvalue.mType = MLSObjectType::Application_message_ciphertext;
|
|
rvalue.mContent.Init(content);
|
|
|
|
promise->MaybeResolve(rvalue);
|
|
},
|
|
[promise](::mozilla::ipc::ResponseRejectReason aReason) {
|
|
promise->MaybeRejectWithUnknownError("Failed to send message");
|
|
});
|
|
|
|
return promise.forget();
|
|
}
|
|
|
|
already_AddRefed<Promise> MLSGroupView::Receive(
|
|
const MLSBytesOrUint8Array& aJsMessage, ErrorResult& aRv) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLSGroupView::Receive()"));
|
|
|
|
// Handle the message parameter
|
|
nsTArray<uint8_t> message =
|
|
ExtractMLSBytesOrUint8ArrayWithUnknownType(aJsMessage, aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Check if the message is empty
|
|
if (NS_WARN_IF(message.IsEmpty())) {
|
|
aRv.ThrowTypeError("The receivedmessage must not be empty");
|
|
return nullptr;
|
|
}
|
|
|
|
// Create a new Promise object for the result
|
|
RefPtr<Promise> promise = Promise::Create(mMLS->GetParentObject(), aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Receive the message
|
|
mMLS->mTransactionChild->SendRequestReceive(mClientId, message)
|
|
->Then(
|
|
GetCurrentSerialEventTarget(), __func__,
|
|
[promise, self = RefPtr<MLSGroupView>(this)](GkReceived&& received) {
|
|
// Check if the Maybe contains a value
|
|
if (received.tag == GkReceived::Tag::None) {
|
|
promise->MaybeReject(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
|
|
// Get the context from the GlobalObject
|
|
AutoJSAPI jsapi;
|
|
if (NS_WARN_IF(!jsapi.Init(self->mMLS->GetParentObject()))) {
|
|
promise->MaybeReject(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
JSContext* cx = jsapi.cx();
|
|
|
|
// Construct the Uint8Array objects based on the tag
|
|
ErrorResult error;
|
|
JS::Rooted<JSObject*> jsGroupId(
|
|
cx, Uint8Array::Create(cx, self->mGroupId, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
|
|
// Initialize the Received dictionary
|
|
RootedDictionary<MLSReceived> rvalue(cx);
|
|
rvalue.mGroupId.Init(jsGroupId);
|
|
|
|
// Populate the Received object based on the tag
|
|
switch (received.tag) {
|
|
case GkReceived::Tag::GroupIdEpoch: {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
|
("Processing GroupIdEpoch"));
|
|
|
|
JS::Rooted<JSObject*> jsGroupEpoch(
|
|
cx, Uint8Array::Create(
|
|
cx, received.group_id_epoch._0.group_epoch, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
|
|
// Populate the Received object
|
|
rvalue.mType = MLSObjectType::Commit_processed;
|
|
rvalue.mGroupEpoch.Construct();
|
|
rvalue.mGroupEpoch.Value().Init(jsGroupEpoch);
|
|
break;
|
|
}
|
|
case GkReceived::Tag::ApplicationMessage: {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
|
("Processing ApplicationMessage"));
|
|
|
|
JS::Rooted<JSObject*> jsApplicationMessage(
|
|
cx, Uint8Array::Create(cx, received.application_message._0,
|
|
error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
|
|
// Populate the Received object
|
|
rvalue.mType = MLSObjectType::Application_message_plaintext;
|
|
rvalue.mContent.Construct();
|
|
rvalue.mContent.Value().Init(jsApplicationMessage);
|
|
|
|
break;
|
|
}
|
|
case GkReceived::Tag::CommitOutput: {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
|
("Processing CommitOutput"));
|
|
|
|
JS::Rooted<JSObject*> jsClientId(
|
|
cx, Uint8Array::Create(
|
|
cx, received.commit_output._0.identity, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
JS::Rooted<JSObject*> jsCommit(
|
|
cx, Uint8Array::Create(cx, received.commit_output._0.commit,
|
|
error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
JS::Rooted<JSObject*> jsWelcome(
|
|
cx, Uint8Array::Create(
|
|
cx, received.commit_output._0.welcome, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
JS::Rooted<JSObject*> jsGroupInfo(
|
|
cx, Uint8Array::Create(
|
|
cx, received.commit_output._0.group_info, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
JS::Rooted<JSObject*> jsRatchetTree(
|
|
cx, Uint8Array::Create(
|
|
cx, received.commit_output._0.ratchet_tree, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
|
|
// Construct MLSCommitOutput with the parsed data
|
|
rvalue.mType = MLSObjectType::Commit_output;
|
|
rvalue.mGroupId.Init(jsGroupId);
|
|
rvalue.mCommitOutput.Construct();
|
|
rvalue.mCommitOutput.Value().mType =
|
|
MLSObjectType::Commit_output;
|
|
rvalue.mCommitOutput.Value().mCommit.Init(jsCommit);
|
|
rvalue.mCommitOutput.Value().mGroupId.Init(jsGroupId);
|
|
if (!received.commit_output._0.welcome.IsEmpty()) {
|
|
rvalue.mCommitOutput.Value().mWelcome.Construct();
|
|
rvalue.mCommitOutput.Value().mWelcome.Value().Init(jsWelcome);
|
|
}
|
|
if (!received.commit_output._0.group_info.IsEmpty()) {
|
|
rvalue.mCommitOutput.Value().mGroupInfo.Construct();
|
|
rvalue.mCommitOutput.Value().mGroupInfo.Value().Init(
|
|
jsGroupInfo);
|
|
}
|
|
if (!received.commit_output._0.ratchet_tree.IsEmpty()) {
|
|
rvalue.mCommitOutput.Value().mRatchetTree.Construct();
|
|
rvalue.mCommitOutput.Value().mRatchetTree.Value().Init(
|
|
jsRatchetTree);
|
|
}
|
|
if (!received.commit_output._0.identity.IsEmpty()) {
|
|
rvalue.mCommitOutput.Value().mClientId.Construct();
|
|
rvalue.mCommitOutput.Value().mClientId.Value().Init(
|
|
jsClientId);
|
|
}
|
|
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
|
("Finished processing CommitOutput"));
|
|
break;
|
|
}
|
|
default:
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Error,
|
|
("Unhandled tag in received data"));
|
|
promise->MaybeRejectWithUnknownError(
|
|
"Unhandled tag in received data");
|
|
return;
|
|
}
|
|
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
|
("Successfully constructed MLSReceived"));
|
|
|
|
// Resolve the promise
|
|
promise->MaybeResolve(rvalue);
|
|
},
|
|
[promise](::mozilla::ipc::ResponseRejectReason aReason) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Error,
|
|
("IPC call rejected with reason: %d",
|
|
static_cast<int>(aReason)));
|
|
promise->MaybeRejectWithUnknownError("Failed to receive message");
|
|
});
|
|
|
|
return promise.forget();
|
|
}
|
|
|
|
already_AddRefed<Promise> MLSGroupView::HasPendingProposals(ErrorResult& aRv) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
|
("MLSGroupView::HasPendingProposals()"));
|
|
|
|
// Create a new Promise object for the result
|
|
RefPtr<Promise> promise = Promise::Create(mMLS->GetParentObject(), aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Receive the message
|
|
mMLS->mTransactionChild->SendRequestHasPendingProposals(mGroupId, mClientId)
|
|
->Then(
|
|
GetCurrentSerialEventTarget(), __func__,
|
|
[promise, self = RefPtr<MLSGroupView>(this)](bool&& received) {
|
|
// Get the context from the GlobalObject
|
|
AutoJSAPI jsapi;
|
|
if (NS_WARN_IF(!jsapi.Init(self->mMLS->GetParentObject()))) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Error,
|
|
("Failed to initialize JSAPI"));
|
|
promise->MaybeRejectWithUnknownError(
|
|
"Failed to initialize JSAPI");
|
|
return;
|
|
}
|
|
|
|
// Resolve the promise directly with the boolean value
|
|
promise->MaybeResolve(received);
|
|
},
|
|
[promise](::mozilla::ipc::ResponseRejectReason aReason) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Error,
|
|
("IPC call rejected with reason: %d",
|
|
static_cast<int>(aReason)));
|
|
promise->MaybeRejectWithUnknownError(
|
|
"Failed to determine if there are pending proposals");
|
|
});
|
|
|
|
return promise.forget();
|
|
}
|
|
|
|
already_AddRefed<Promise> MLSGroupView::ClearPendingProposals(
|
|
ErrorResult& aRv) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
|
("MLSGroupView::ClearPendingProposals()"));
|
|
|
|
// Create a new Promise object for the result
|
|
RefPtr<Promise> promise = Promise::Create(mMLS->GetParentObject(), aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Receive the message
|
|
mMLS->mTransactionChild->SendRequestClearPendingProposals(mGroupId, mClientId)
|
|
->Then(
|
|
GetCurrentSerialEventTarget(), __func__,
|
|
[promise, self = RefPtr<MLSGroupView>(this)](bool&& received) {
|
|
// Get the context from the GlobalObject
|
|
AutoJSAPI jsapi;
|
|
if (NS_WARN_IF(!jsapi.Init(self->mMLS->GetParentObject()))) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Error,
|
|
("Failed to initialize JSAPI"));
|
|
promise->MaybeRejectWithUnknownError(
|
|
"Failed to initialize JSAPI");
|
|
return;
|
|
}
|
|
|
|
// Resolve the promise directly with the boolean value
|
|
promise->MaybeResolve(received);
|
|
},
|
|
[promise](::mozilla::ipc::ResponseRejectReason aReason) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Error,
|
|
("IPC call rejected with reason: %d",
|
|
static_cast<int>(aReason)));
|
|
promise->MaybeRejectWithUnknownError(
|
|
"Failed to clear pending proposals");
|
|
});
|
|
|
|
return promise.forget();
|
|
}
|
|
|
|
already_AddRefed<Promise> MLSGroupView::HasPendingCommit(ErrorResult& aRv) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
|
("MLSGroupView::HasPendingCommit()"));
|
|
|
|
// Create a new Promise object for the result
|
|
RefPtr<Promise> promise = Promise::Create(mMLS->GetParentObject(), aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Receive the message
|
|
mMLS->mTransactionChild->SendRequestHasPendingCommit(mGroupId, mClientId)
|
|
->Then(
|
|
GetCurrentSerialEventTarget(), __func__,
|
|
[promise, self = RefPtr<MLSGroupView>(this)](bool&& received) {
|
|
// Get the context from the GlobalObject
|
|
AutoJSAPI jsapi;
|
|
if (NS_WARN_IF(!jsapi.Init(self->mMLS->GetParentObject()))) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Error,
|
|
("Failed to initialize JSAPI"));
|
|
promise->MaybeRejectWithUnknownError(
|
|
"Failed to initialize JSAPI");
|
|
return;
|
|
}
|
|
|
|
// Resolve the promise directly with the boolean value
|
|
promise->MaybeResolve(received);
|
|
},
|
|
[promise](::mozilla::ipc::ResponseRejectReason aReason) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Error,
|
|
("IPC call rejected with reason: %d",
|
|
static_cast<int>(aReason)));
|
|
promise->MaybeRejectWithUnknownError(
|
|
"Failed to determine if there is a pending commit");
|
|
});
|
|
|
|
return promise.forget();
|
|
}
|
|
|
|
already_AddRefed<Promise> MLSGroupView::ClearPendingCommit(ErrorResult& aRv) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
|
("MLSGroupView::ClearPendingCommit()"));
|
|
|
|
// Create a new Promise object for the result
|
|
RefPtr<Promise> promise = Promise::Create(mMLS->GetParentObject(), aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Receive the message
|
|
mMLS->mTransactionChild->SendRequestClearPendingCommit(mGroupId, mClientId)
|
|
->Then(
|
|
GetCurrentSerialEventTarget(), __func__,
|
|
[promise, self = RefPtr<MLSGroupView>(this)](bool&& received) {
|
|
// Get the context from the GlobalObject
|
|
AutoJSAPI jsapi;
|
|
if (NS_WARN_IF(!jsapi.Init(self->mMLS->GetParentObject()))) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Error,
|
|
("Failed to initialize JSAPI"));
|
|
promise->MaybeRejectWithUnknownError(
|
|
"Failed to initialize JSAPI");
|
|
return;
|
|
}
|
|
|
|
// Resolve the promise directly with the boolean value
|
|
promise->MaybeResolve(received);
|
|
},
|
|
[promise](::mozilla::ipc::ResponseRejectReason aReason) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Error,
|
|
("IPC call rejected with reason: %d",
|
|
static_cast<int>(aReason)));
|
|
promise->MaybeRejectWithUnknownError(
|
|
"Failed to clear pending commit");
|
|
});
|
|
|
|
return promise.forget();
|
|
}
|
|
|
|
already_AddRefed<Promise> MLSGroupView::ApplyPendingCommit(ErrorResult& aRv) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
|
("MLSGroupView::ApplyPendingCommit()"));
|
|
|
|
// Create a new Promise object for the result
|
|
RefPtr<Promise> promise = Promise::Create(mMLS->GetParentObject(), aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Receive the message
|
|
mMLS->mTransactionChild->SendRequestApplyPendingCommit(mGroupId, mClientId)
|
|
->Then(
|
|
GetCurrentSerialEventTarget(), __func__,
|
|
[promise, self = RefPtr<MLSGroupView>(this)](GkReceived&& received) {
|
|
// Check if the Maybe contains a value
|
|
if (received.tag == GkReceived::Tag::None) {
|
|
promise->MaybeRejectWithUnknownError(
|
|
"Failed to apply pending commit");
|
|
return;
|
|
}
|
|
|
|
// Get the context from the GlobalObject
|
|
AutoJSAPI jsapi;
|
|
if (NS_WARN_IF(!jsapi.Init(self->mMLS->GetParentObject()))) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Error,
|
|
("Failed to initialize JSAPI"));
|
|
promise->MaybeRejectWithUnknownError(
|
|
"Failed to initialize JSAPI");
|
|
return;
|
|
}
|
|
JSContext* cx = jsapi.cx();
|
|
|
|
// Construct the Uint8Array objects based on the tag
|
|
ErrorResult error;
|
|
JS::Rooted<JSObject*> jsGroupId(
|
|
cx, Uint8Array::Create(cx, self->mGroupId, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
|
|
// Initialize the Received dictionary
|
|
RootedDictionary<MLSReceived> rvalue(cx);
|
|
rvalue.mGroupId.Init(jsGroupId);
|
|
|
|
// Populate the Received object based on the tag
|
|
switch (received.tag) {
|
|
case GkReceived::Tag::GroupIdEpoch: {
|
|
JS::Rooted<JSObject*> jsGroupEpoch(
|
|
cx, Uint8Array::Create(
|
|
cx, received.group_id_epoch._0.group_epoch, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
|
|
// Populate the Received object
|
|
rvalue.mType = MLSObjectType::Commit_processed;
|
|
rvalue.mGroupEpoch.Construct();
|
|
rvalue.mGroupEpoch.Value().Init(jsGroupEpoch);
|
|
break;
|
|
}
|
|
default:
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Error,
|
|
("Unhandled tag in received data"));
|
|
promise->MaybeRejectWithUnknownError(
|
|
"Unhandled tag in received data");
|
|
return;
|
|
}
|
|
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
|
("Successfully constructed MLSReceived"));
|
|
|
|
// Resolve the promise
|
|
promise->MaybeResolve(rvalue);
|
|
},
|
|
[promise](::mozilla::ipc::ResponseRejectReason aReason) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Error,
|
|
("IPC call rejected with reason: %d",
|
|
static_cast<int>(aReason)));
|
|
promise->MaybeRejectWithUnknownError(
|
|
"Failed to apply pending commit");
|
|
});
|
|
|
|
return promise.forget();
|
|
}
|
|
|
|
already_AddRefed<Promise> MLSGroupView::ExportSecret(
|
|
const MLSBytesOrUint8ArrayOrUTF8String& aJsLabel,
|
|
const MLSBytesOrUint8Array& aJsContext, const uint64_t aLen,
|
|
ErrorResult& aRv) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLSGroupView::ExportSecret()"));
|
|
|
|
// Handle the label parameter
|
|
nsTArray<uint8_t> label = ExtractMLSBytesOrUint8ArrayOrUTF8String(
|
|
MLSObjectType::Exporter_label, aJsLabel, aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
// We allow the context to be empty
|
|
if (NS_WARN_IF(label.IsEmpty())) {
|
|
aRv.ThrowTypeError("The label must not be empty");
|
|
return nullptr;
|
|
}
|
|
|
|
// Handle the context parameter
|
|
// Note: we allow the context to be empty
|
|
nsTArray<uint8_t> context = ExtractMLSBytesOrUint8Array(
|
|
MLSObjectType::Exporter_context, aJsContext, aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Create a new Promise object for the result
|
|
RefPtr<Promise> promise = Promise::Create(mMLS->GetParentObject(), aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
mMLS->mTransactionChild
|
|
->SendRequestExportSecret(mGroupId, mClientId, label, context, aLen)
|
|
->Then(
|
|
GetCurrentSerialEventTarget(), __func__,
|
|
[promise, self = RefPtr<MLSGroupView>(this)](
|
|
Maybe<mozilla::security::mls::GkExporterOutput>&&
|
|
exporterOutput) {
|
|
// Check if the Maybe contains a value
|
|
if (exporterOutput.isNothing()) {
|
|
promise->MaybeReject(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
|
|
// Get the context from the GlobalObject
|
|
AutoJSAPI jsapi;
|
|
if (NS_WARN_IF(!jsapi.Init(self->mMLS->GetParentObject()))) {
|
|
promise->MaybeRejectWithUnknownError(
|
|
"Failed to initialize JSAPI");
|
|
return;
|
|
}
|
|
JSContext* cx = jsapi.cx();
|
|
|
|
// Construct the Uint8Array objects
|
|
ErrorResult error;
|
|
JS::Rooted<JSObject*> jsGroupId(
|
|
cx, Uint8Array::Create(cx, exporterOutput->group_id, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
JS::Rooted<JSObject*> jsGroupEpoch(
|
|
cx, Uint8Array::Create(cx, exporterOutput->group_epoch, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
JS::Rooted<JSObject*> jsLabel(
|
|
cx, Uint8Array::Create(cx, exporterOutput->label, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
JS::Rooted<JSObject*> jsContext(
|
|
cx, Uint8Array::Create(cx, exporterOutput->context, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
JS::Rooted<JSObject*> jsExporter(
|
|
cx, Uint8Array::Create(cx, exporterOutput->exporter, error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
|
|
// Construct MLSBytes with the group identifier as content
|
|
RootedDictionary<MLSExporterOutput> rvalue(cx);
|
|
rvalue.mType = MLSObjectType::Exporter_output;
|
|
rvalue.mGroupId.Init(jsGroupId);
|
|
rvalue.mGroupEpoch.Init(jsGroupEpoch);
|
|
rvalue.mLabel.Init(jsLabel);
|
|
rvalue.mContext.Init(jsContext);
|
|
rvalue.mSecret.Init(jsExporter);
|
|
|
|
// Resolve the promise
|
|
promise->MaybeResolve(rvalue);
|
|
},
|
|
[promise](::mozilla::ipc::ResponseRejectReason aReason) {
|
|
promise->MaybeRejectWithUnknownError("Failed to export secret");
|
|
});
|
|
|
|
return promise.forget();
|
|
}
|
|
|
|
}; // namespace mozilla::dom
|