MLSGroupView.cpp (49866B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "MLSGroupView.h" 8 9 #include "MLSTypeUtils.h" 10 #include "ipc/IPCMessageUtilsSpecializations.h" 11 #include "mozilla/BasePrincipal.h" 12 #include "mozilla/Logging.h" 13 #include "mozilla/dom/MLSBinding.h" 14 #include "mozilla/dom/MLSTransactionChild.h" 15 #include "mozilla/dom/MLSTransactionMessage.h" 16 #include "mozilla/dom/Promise.h" 17 #include "mozilla/dom/TypedArray.h" 18 #include "nsDebug.h" 19 #include "nsTArray.h" 20 namespace mozilla::dom { 21 22 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WITH_JS_MEMBERS(MLSGroupView, (mMLS), 23 (mJsGroupId, mJsClientId)) 24 25 NS_IMPL_CYCLE_COLLECTING_ADDREF(MLSGroupView) 26 NS_IMPL_CYCLE_COLLECTING_RELEASE(MLSGroupView) 27 28 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MLSGroupView) 29 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 30 NS_INTERFACE_MAP_ENTRY(nsISupports) 31 NS_INTERFACE_MAP_END 32 33 // Setup logging 34 extern mozilla::LazyLogModule gMlsLog; 35 36 MLSGroupView::MLSGroupView(MLS* aMLS, nsTArray<uint8_t>&& aGroupId, 37 nsTArray<uint8_t>&& aClientId) 38 : mMLS(aMLS), 39 mGroupId(std::move(aGroupId)), 40 mClientId(std::move(aClientId)) { 41 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLSGroupView::MLSGroupView()")); 42 43 // Indicate that the object holds JS objects 44 mozilla::HoldJSObjects(this); 45 } 46 47 JSObject* MLSGroupView::WrapObject(JSContext* aCx, 48 JS::Handle<JSObject*> aGivenProto) { 49 return MLSGroupView_Binding::Wrap(aCx, this, aGivenProto); 50 } 51 52 // 53 // API 54 // 55 56 void MLSGroupView::GetGroupId(JSContext* aCx, 57 JS::MutableHandle<JSObject*> aGroupId, 58 ErrorResult& aRv) { 59 if (!mJsGroupId) { 60 mJsGroupId = Uint8Array::Create(aCx, this, mGroupId, aRv); 61 if (aRv.Failed()) { 62 return; 63 } 64 } 65 aGroupId.set(mJsGroupId); 66 } 67 68 void MLSGroupView::GetClientId(JSContext* aCx, 69 JS::MutableHandle<JSObject*> aClientId, 70 ErrorResult& aRv) { 71 if (!mJsClientId) { 72 mJsClientId = Uint8Array::Create(aCx, this, mClientId, aRv); 73 if (aRv.Failed()) { 74 return; 75 } 76 } 77 aClientId.set(mJsClientId); 78 } 79 80 already_AddRefed<Promise> MLSGroupView::DeleteState(ErrorResult& aRv) { 81 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLSGroupView::DeleteState()")); 82 83 // Create a new Promise object for the result 84 RefPtr<Promise> promise = Promise::Create(mMLS->GetParentObject(), aRv); 85 if (NS_WARN_IF(aRv.Failed())) { 86 return nullptr; 87 } 88 89 mMLS->mTransactionChild->SendRequestGroupStateDelete(mGroupId, mClientId) 90 ->Then( 91 GetCurrentSerialEventTarget(), __func__, 92 [promise]( 93 Maybe<mozilla::security::mls::GkGroupIdEpoch>&& groupIdEpoch) { 94 // Check if the value is Nothing or Some with an empty group_epoch 95 if (groupIdEpoch.isNothing()) { 96 promise->MaybeRejectWithUnknownError( 97 "Failed to delete group state"); 98 return; 99 } 100 101 // Check if the epoch is 0xFFFF..FF 102 bool isMaxEpoch = 103 std::all_of(groupIdEpoch->group_epoch.begin(), 104 groupIdEpoch->group_epoch.end(), 105 [](uint8_t byte) { return byte == 0xFF; }); 106 107 // If the epoch is 0xFFFF..FF, then the group has been deleted 108 if (isMaxEpoch) { 109 promise->MaybeResolveWithUndefined(); 110 } else { 111 promise->MaybeRejectWithUnknownError( 112 "Group has not been deleted"); 113 } 114 }, 115 [promise](::mozilla::ipc::ResponseRejectReason aReason) { 116 promise->MaybeRejectWithUnknownError( 117 "Failed to delete group state"); 118 }); 119 120 return promise.forget(); 121 } 122 123 already_AddRefed<Promise> MLSGroupView::Add( 124 const MLSBytesOrUint8Array& aJsKeyPackage, ErrorResult& aRv) { 125 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLSGroupView::Add()")); 126 127 // Handle the key package parameter 128 nsTArray<uint8_t> keyPackage = ExtractMLSBytesOrUint8Array( 129 MLSObjectType::Key_package, aJsKeyPackage, aRv); 130 if (NS_WARN_IF(aRv.Failed())) { 131 return nullptr; 132 } 133 134 // Check if the key package is empty 135 if (NS_WARN_IF(keyPackage.IsEmpty())) { 136 aRv.ThrowTypeError("The key package must not be empty"); 137 return nullptr; 138 } 139 140 // Create a new Promise object for the result 141 RefPtr<Promise> promise = Promise::Create(mMLS->GetParentObject(), aRv); 142 if (NS_WARN_IF(aRv.Failed())) { 143 return nullptr; 144 } 145 146 mMLS->mTransactionChild->SendRequestGroupAdd(mGroupId, mClientId, keyPackage) 147 ->Then( 148 GetCurrentSerialEventTarget(), __func__, 149 [promise, self = RefPtr<MLSGroupView>(this)]( 150 Maybe<mozilla::security::mls::GkMlsCommitOutput>&& commitOutput) { 151 // Check if the value is Nothing 152 if (commitOutput.isNothing()) { 153 promise->MaybeReject(NS_ERROR_FAILURE); 154 return; 155 } 156 157 // Get the context from the GlobalObject 158 AutoJSAPI jsapi; 159 if (NS_WARN_IF(!jsapi.Init(self->mMLS->GetParentObject()))) { 160 promise->MaybeReject(NS_ERROR_FAILURE); 161 return; 162 } 163 JSContext* cx = jsapi.cx(); 164 165 // Construct the Uint8Array objects 166 ErrorResult error; 167 JS::Rooted<JSObject*> jsGroupId( 168 cx, Uint8Array::Create(cx, self->mGroupId, error)); 169 error.WouldReportJSException(); 170 if (error.Failed()) { 171 promise->MaybeReject(std::move(error)); 172 return; 173 } 174 JS::Rooted<JSObject*> jsClientId( 175 cx, Uint8Array::Create(cx, commitOutput->identity, error)); 176 error.WouldReportJSException(); 177 if (error.Failed()) { 178 promise->MaybeReject(std::move(error)); 179 return; 180 } 181 JS::Rooted<JSObject*> jsCommit( 182 cx, Uint8Array::Create(cx, commitOutput->commit, error)); 183 error.WouldReportJSException(); 184 if (error.Failed()) { 185 promise->MaybeReject(std::move(error)); 186 return; 187 } 188 JS::Rooted<JSObject*> jsWelcome( 189 cx, Uint8Array::Create(cx, commitOutput->welcome, error)); 190 error.WouldReportJSException(); 191 if (error.Failed()) { 192 promise->MaybeReject(std::move(error)); 193 return; 194 } 195 JS::Rooted<JSObject*> jsGroupInfo( 196 cx, Uint8Array::Create(cx, commitOutput->group_info, error)); 197 error.WouldReportJSException(); 198 if (error.Failed()) { 199 promise->MaybeReject(std::move(error)); 200 return; 201 } 202 JS::Rooted<JSObject*> jsRatchetTree( 203 cx, Uint8Array::Create(cx, commitOutput->ratchet_tree, error)); 204 error.WouldReportJSException(); 205 if (error.Failed()) { 206 promise->MaybeReject(std::move(error)); 207 return; 208 } 209 210 // Construct MLSCommitOutput with the parsed data 211 RootedDictionary<MLSCommitOutput> rvalue(cx); 212 rvalue.mType = MLSObjectType::Commit_output; 213 rvalue.mGroupId.Init(jsGroupId); 214 rvalue.mCommit.Init(jsCommit); 215 if (!commitOutput->welcome.IsEmpty()) { 216 rvalue.mWelcome.Construct(); 217 rvalue.mWelcome.Value().Init(jsWelcome); 218 } 219 if (!commitOutput->group_info.IsEmpty()) { 220 rvalue.mGroupInfo.Construct(); 221 rvalue.mGroupInfo.Value().Init(jsGroupInfo); 222 } 223 if (!commitOutput->ratchet_tree.IsEmpty()) { 224 rvalue.mRatchetTree.Construct(); 225 rvalue.mRatchetTree.Value().Init(jsRatchetTree); 226 } 227 if (!commitOutput->identity.IsEmpty()) { 228 rvalue.mClientId.Construct(); 229 rvalue.mClientId.Value().Init(jsClientId); 230 } 231 232 // Resolve the promise 233 promise->MaybeResolve(rvalue); 234 }, 235 [promise](::mozilla::ipc::ResponseRejectReason aReason) { 236 promise->MaybeRejectWithUnknownError("Failed to add to group"); 237 }); 238 239 return promise.forget(); 240 } 241 242 already_AddRefed<Promise> MLSGroupView::ProposeAdd( 243 const MLSBytesOrUint8Array& aJsKeyPackage, ErrorResult& aRv) { 244 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLSGroupView::ProposeAdd()")); 245 246 // Handle the key package parameter 247 nsTArray<uint8_t> keyPackage = ExtractMLSBytesOrUint8Array( 248 MLSObjectType::Key_package, aJsKeyPackage, aRv); 249 if (NS_WARN_IF(aRv.Failed())) { 250 return nullptr; 251 } 252 253 // Check if the key package is empty 254 if (NS_WARN_IF(keyPackage.IsEmpty())) { 255 aRv.ThrowTypeError("The key package must not be empty"); 256 return nullptr; 257 } 258 259 // Create a new Promise object for the result 260 RefPtr<Promise> promise = Promise::Create(mMLS->GetParentObject(), aRv); 261 if (NS_WARN_IF(aRv.Failed())) { 262 return nullptr; 263 } 264 265 mMLS->mTransactionChild 266 ->SendRequestGroupProposeAdd(mGroupId, mClientId, keyPackage) 267 ->Then( 268 GetCurrentSerialEventTarget(), __func__, 269 [promise, 270 self = RefPtr<MLSGroupView>(this)](Maybe<RawBytes>&& proposal) { 271 // Check if the value is Nothing 272 if (proposal.isNothing()) { 273 promise->MaybeRejectWithUnknownError( 274 "Failed to propose add to group"); 275 return; 276 } 277 278 // Get the context from the GlobalObject 279 AutoJSAPI jsapi; 280 if (NS_WARN_IF(!jsapi.Init(self->mMLS->GetParentObject()))) { 281 promise->MaybeReject(NS_ERROR_FAILURE); 282 return; 283 } 284 JSContext* cx = jsapi.cx(); 285 286 // Construct the Uint8Array object 287 ErrorResult error; 288 JS::Rooted<JSObject*> content( 289 cx, Uint8Array::Create(cx, proposal->data(), error)); 290 error.WouldReportJSException(); 291 if (error.Failed()) { 292 promise->MaybeReject(std::move(error)); 293 return; 294 } 295 296 // Construct MLSBytes with the proposal as content 297 RootedDictionary<MLSBytes> rvalue(cx); 298 rvalue.mType = MLSObjectType::Proposal; 299 rvalue.mContent.Init(content); 300 301 // Resolve the promise 302 promise->MaybeResolve(rvalue); 303 }, 304 [promise](::mozilla::ipc::ResponseRejectReason aReason) { 305 promise->MaybeRejectWithUnknownError( 306 "Failed to propose add to group"); 307 }); 308 309 return promise.forget(); 310 } 311 312 already_AddRefed<Promise> MLSGroupView::Remove( 313 const MLSBytesOrUint8Array& aJsRemClientIdentifier, ErrorResult& aRv) { 314 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLSGroupView::Remove()")); 315 316 // Handle the remove client identifier parameter 317 nsTArray<uint8_t> remClientIdentifier = ExtractMLSBytesOrUint8Array( 318 MLSObjectType::Client_identifier, aJsRemClientIdentifier, aRv); 319 if (NS_WARN_IF(aRv.Failed())) { 320 return nullptr; 321 } 322 323 // Check if the remove client identifier is empty 324 if (NS_WARN_IF(remClientIdentifier.IsEmpty())) { 325 aRv.ThrowTypeError("The remove client identifier must not be empty"); 326 return nullptr; 327 } 328 329 // Create a new Promise object for the result 330 RefPtr<Promise> promise = Promise::Create(mMLS->GetParentObject(), aRv); 331 if (NS_WARN_IF(aRv.Failed())) { 332 return nullptr; 333 } 334 335 // Use the static method or instance to send the IPC message 336 mMLS->mTransactionChild 337 ->SendRequestGroupRemove(mGroupId, mClientId, remClientIdentifier) 338 ->Then( 339 GetCurrentSerialEventTarget(), __func__, 340 [promise, self = RefPtr<MLSGroupView>(this)]( 341 Maybe<mozilla::security::mls::GkMlsCommitOutput>&& commitOutput) { 342 // Check if the value is Nothing 343 if (commitOutput.isNothing()) { 344 promise->MaybeReject(NS_ERROR_FAILURE); 345 return; 346 } 347 348 // Get the context from the GlobalObject 349 AutoJSAPI jsapi; 350 if (NS_WARN_IF(!jsapi.Init(self->mMLS->GetParentObject()))) { 351 promise->MaybeReject(NS_ERROR_FAILURE); 352 return; 353 } 354 JSContext* cx = jsapi.cx(); 355 356 // Construct the Uint8Array objects 357 ErrorResult error; 358 JS::Rooted<JSObject*> jsGroupId( 359 cx, Uint8Array::Create(cx, self->mGroupId, error)); 360 error.WouldReportJSException(); 361 if (error.Failed()) { 362 promise->MaybeReject(std::move(error)); 363 return; 364 } 365 JS::Rooted<JSObject*> jsClientId( 366 cx, Uint8Array::Create(cx, commitOutput->identity, error)); 367 error.WouldReportJSException(); 368 if (error.Failed()) { 369 promise->MaybeReject(std::move(error)); 370 return; 371 } 372 JS::Rooted<JSObject*> jsCommit( 373 cx, Uint8Array::Create(cx, commitOutput->commit, error)); 374 error.WouldReportJSException(); 375 if (error.Failed()) { 376 promise->MaybeReject(std::move(error)); 377 return; 378 } 379 JS::Rooted<JSObject*> jsWelcome( 380 cx, Uint8Array::Create(cx, commitOutput->welcome, error)); 381 error.WouldReportJSException(); 382 if (error.Failed()) { 383 promise->MaybeReject(std::move(error)); 384 return; 385 } 386 JS::Rooted<JSObject*> jsGroupInfo( 387 cx, Uint8Array::Create(cx, commitOutput->group_info, error)); 388 error.WouldReportJSException(); 389 if (error.Failed()) { 390 promise->MaybeReject(std::move(error)); 391 return; 392 } 393 JS::Rooted<JSObject*> jsRatchetTree( 394 cx, Uint8Array::Create(cx, commitOutput->ratchet_tree, error)); 395 error.WouldReportJSException(); 396 if (error.Failed()) { 397 promise->MaybeReject(std::move(error)); 398 return; 399 } 400 401 // Construct MLSCommitOutput with the parsed data 402 RootedDictionary<MLSCommitOutput> rvalue(cx); 403 rvalue.mType = MLSObjectType::Commit_output; 404 rvalue.mGroupId.Init(jsGroupId); 405 rvalue.mCommit.Init(jsCommit); 406 if (!commitOutput->welcome.IsEmpty()) { 407 rvalue.mWelcome.Construct(); 408 rvalue.mWelcome.Value().Init(jsWelcome); 409 } 410 if (!commitOutput->group_info.IsEmpty()) { 411 rvalue.mGroupInfo.Construct(); 412 rvalue.mGroupInfo.Value().Init(jsGroupInfo); 413 } 414 if (!commitOutput->ratchet_tree.IsEmpty()) { 415 rvalue.mRatchetTree.Construct(); 416 rvalue.mRatchetTree.Value().Init(jsRatchetTree); 417 } 418 if (!commitOutput->identity.IsEmpty()) { 419 rvalue.mClientId.Construct(); 420 rvalue.mClientId.Value().Init(jsClientId); 421 } 422 423 // Resolve the promise 424 promise->MaybeResolve(rvalue); 425 }, 426 [promise](::mozilla::ipc::ResponseRejectReason aReason) { 427 promise->MaybeRejectWithUnknownError("Failed to remove from group"); 428 }); 429 430 return promise.forget(); 431 } 432 433 already_AddRefed<Promise> MLSGroupView::ProposeRemove( 434 const MLSBytesOrUint8Array& aJsRemClientIdentifier, ErrorResult& aRv) { 435 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLSGroupView::ProposeRemove()")); 436 437 // Handle the remove client identifier parameter 438 nsTArray<uint8_t> remClientIdentifier = ExtractMLSBytesOrUint8Array( 439 MLSObjectType::Client_identifier, aJsRemClientIdentifier, aRv); 440 if (NS_WARN_IF(aRv.Failed())) { 441 return nullptr; 442 } 443 444 // Check if the removed client identifier is empty 445 if (NS_WARN_IF(remClientIdentifier.IsEmpty())) { 446 aRv.ThrowTypeError("The removed client identifier must not be empty"); 447 return nullptr; 448 } 449 450 // Create a new Promise object for the result 451 RefPtr<Promise> promise = Promise::Create(mMLS->GetParentObject(), aRv); 452 if (NS_WARN_IF(aRv.Failed())) { 453 return nullptr; 454 } 455 456 mMLS->mTransactionChild 457 ->SendRequestGroupProposeRemove(mGroupId, mClientId, remClientIdentifier) 458 ->Then( 459 GetCurrentSerialEventTarget(), __func__, 460 [promise, 461 self = RefPtr<MLSGroupView>(this)](Maybe<RawBytes>&& proposal) { 462 // Check if the value is Nothing 463 if (proposal.isNothing()) { 464 promise->MaybeReject(NS_ERROR_FAILURE); 465 return; 466 } 467 468 // Get the context from the GlobalObject 469 AutoJSAPI jsapi; 470 if (NS_WARN_IF(!jsapi.Init(self->mMLS->GetParentObject()))) { 471 promise->MaybeReject(NS_ERROR_FAILURE); 472 return; 473 } 474 JSContext* cx = jsapi.cx(); 475 476 // Construct the Uint8Array object 477 ErrorResult error; 478 JS::Rooted<JSObject*> content( 479 cx, Uint8Array::Create(cx, proposal->data(), error)); 480 error.WouldReportJSException(); 481 if (error.Failed()) { 482 promise->MaybeReject(std::move(error)); 483 return; 484 } 485 486 // Construct MLSBytes with the proposal as content 487 RootedDictionary<MLSBytes> rvalue(cx); 488 rvalue.mType = MLSObjectType::Proposal; 489 rvalue.mContent.Init(content); 490 491 // Resolve the promise 492 promise->MaybeResolve(rvalue); 493 }, 494 [promise](::mozilla::ipc::ResponseRejectReason aReason) { 495 promise->MaybeRejectWithUnknownError( 496 "Failed to propose remove from group"); 497 }); 498 499 return promise.forget(); 500 } 501 502 already_AddRefed<Promise> MLSGroupView::Close(ErrorResult& aRv) { 503 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLSGroupView::Close()")); 504 505 // Create a new Promise object for the result 506 RefPtr<Promise> promise = Promise::Create(mMLS->GetParentObject(), aRv); 507 if (NS_WARN_IF(aRv.Failed())) { 508 return nullptr; 509 } 510 511 mMLS->mTransactionChild->SendRequestGroupClose(mGroupId, mClientId) 512 ->Then( 513 GetCurrentSerialEventTarget(), __func__, 514 [promise, self = RefPtr<MLSGroupView>(this)]( 515 Maybe<mozilla::security::mls::GkMlsCommitOutput>&& commitOutput) { 516 // Check if the value is Nothing 517 if (commitOutput.isNothing()) { 518 promise->MaybeReject(NS_ERROR_FAILURE); 519 return; 520 } 521 522 // Get the context from the GlobalObject 523 AutoJSAPI jsapi; 524 if (NS_WARN_IF(!jsapi.Init(self->mMLS->GetParentObject()))) { 525 promise->MaybeReject(NS_ERROR_FAILURE); 526 return; 527 } 528 JSContext* cx = jsapi.cx(); 529 530 // Construct the Uint8Array objects 531 ErrorResult error; 532 JS::Rooted<JSObject*> jsGroupId( 533 cx, Uint8Array::Create(cx, self->mGroupId, error)); 534 error.WouldReportJSException(); 535 if (error.Failed()) { 536 promise->MaybeReject(std::move(error)); 537 return; 538 } 539 JS::Rooted<JSObject*> jsClientId( 540 cx, Uint8Array::Create(cx, commitOutput->identity, error)); 541 error.WouldReportJSException(); 542 if (error.Failed()) { 543 promise->MaybeReject(std::move(error)); 544 return; 545 } 546 JS::Rooted<JSObject*> jsCommit( 547 cx, Uint8Array::Create(cx, commitOutput->commit, error)); 548 error.WouldReportJSException(); 549 if (error.Failed()) { 550 promise->MaybeReject(std::move(error)); 551 return; 552 } 553 JS::Rooted<JSObject*> jsWelcome( 554 cx, Uint8Array::Create(cx, commitOutput->welcome, error)); 555 error.WouldReportJSException(); 556 if (error.Failed()) { 557 promise->MaybeReject(std::move(error)); 558 return; 559 } 560 JS::Rooted<JSObject*> jsGroupInfo( 561 cx, Uint8Array::Create(cx, commitOutput->group_info, error)); 562 error.WouldReportJSException(); 563 if (error.Failed()) { 564 promise->MaybeReject(std::move(error)); 565 return; 566 } 567 JS::Rooted<JSObject*> jsRatchetTree( 568 cx, Uint8Array::Create(cx, commitOutput->ratchet_tree, error)); 569 error.WouldReportJSException(); 570 if (error.Failed()) { 571 promise->MaybeReject(std::move(error)); 572 return; 573 } 574 575 // Construct MLSCommitOutput with the parsed data 576 RootedDictionary<MLSCommitOutput> rvalue(cx); 577 rvalue.mType = MLSObjectType::Commit_output; 578 rvalue.mGroupId.Init(jsGroupId); 579 rvalue.mCommit.Init(jsCommit); 580 if (!commitOutput->welcome.IsEmpty()) { 581 rvalue.mWelcome.Construct(); 582 rvalue.mWelcome.Value().Init(jsWelcome); 583 } 584 if (!commitOutput->group_info.IsEmpty()) { 585 rvalue.mGroupInfo.Construct(); 586 rvalue.mGroupInfo.Value().Init(jsGroupInfo); 587 } 588 if (!commitOutput->ratchet_tree.IsEmpty()) { 589 rvalue.mRatchetTree.Construct(); 590 rvalue.mRatchetTree.Value().Init(jsRatchetTree); 591 } 592 if (!commitOutput->identity.IsEmpty()) { 593 rvalue.mClientId.Construct(); 594 rvalue.mClientId.Value().Init(jsClientId); 595 } 596 597 // Resolve the promise 598 promise->MaybeResolve(rvalue); 599 }, 600 [promise](::mozilla::ipc::ResponseRejectReason aReason) { 601 promise->MaybeRejectWithUnknownError("Failed to close group"); 602 }); 603 604 return promise.forget(); 605 } 606 607 already_AddRefed<Promise> MLSGroupView::Details(ErrorResult& aRv) { 608 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLSGroupView::Details()")); 609 610 // Create a new Promise object for the result 611 RefPtr<Promise> promise = Promise::Create(mMLS->GetParentObject(), aRv); 612 if (NS_WARN_IF(aRv.Failed())) { 613 return nullptr; 614 } 615 616 mMLS->mTransactionChild->SendRequestGroupDetails(mGroupId, mClientId) 617 ->Then( 618 GetCurrentSerialEventTarget(), __func__, 619 [promise, self = RefPtr<MLSGroupView>(this)]( 620 Maybe<GkGroupDetails>&& groupDetails) { 621 // Check if the value is Nothing 622 if (groupDetails.isNothing()) { 623 promise->MaybeReject(NS_ERROR_FAILURE); 624 return; 625 } 626 627 // Get the context from the GlobalObject 628 AutoJSAPI jsapi; 629 if (NS_WARN_IF(!jsapi.Init(self->mMLS->GetParentObject()))) { 630 promise->MaybeReject(NS_ERROR_FAILURE); 631 return; 632 } 633 JSContext* cx = jsapi.cx(); 634 635 // Construct the Uint8Array objects 636 ErrorResult error; 637 JS::Rooted<JSObject*> jsGroupId( 638 cx, Uint8Array::Create(cx, groupDetails->group_id, error)); 639 error.WouldReportJSException(); 640 if (error.Failed()) { 641 promise->MaybeReject(std::move(error)); 642 return; 643 } 644 JS::Rooted<JSObject*> jsGroupEpoch( 645 cx, Uint8Array::Create(cx, groupDetails->group_epoch, error)); 646 error.WouldReportJSException(); 647 if (error.Failed()) { 648 promise->MaybeReject(std::move(error)); 649 return; 650 } 651 652 // Construct MLSGroupDetails 653 RootedDictionary<MLSGroupDetails> rvalue(cx); 654 rvalue.mType = MLSObjectType::Group_info; 655 rvalue.mGroupId.Init(jsGroupId); 656 rvalue.mGroupEpoch.Init(jsGroupEpoch); 657 if (!rvalue.mMembers.SetCapacity( 658 groupDetails->group_members.Length(), fallible)) { 659 promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY); 660 return; 661 } 662 663 for (const auto& member : groupDetails->group_members) { 664 JS::Rooted<JSObject*> jsClientId( 665 cx, Uint8Array::Create(cx, member.identity, error)); 666 error.WouldReportJSException(); 667 if (error.Failed()) { 668 promise->MaybeReject(std::move(error)); 669 return; 670 } 671 JS::Rooted<JSObject*> jsCredential( 672 cx, Uint8Array::Create(cx, member.credential, error)); 673 error.WouldReportJSException(); 674 if (error.Failed()) { 675 promise->MaybeReject(std::move(error)); 676 return; 677 } 678 679 // Guaranteed not to fail because of the SetCapacity above. 680 MLSGroupMember& jsMember = 681 *rvalue.mMembers.AppendElement(fallible); 682 jsMember.mClientId.Init(jsClientId); 683 jsMember.mCredential.Init(jsCredential); 684 } 685 686 // Resolve the promise 687 promise->MaybeResolve(rvalue); 688 }, 689 [promise](::mozilla::ipc::ResponseRejectReason aReason) { 690 promise->MaybeRejectWithUnknownError("Failed to get group details"); 691 }); 692 693 return promise.forget(); 694 } 695 696 already_AddRefed<Promise> MLSGroupView::Send( 697 const MLSBytesOrUint8ArrayOrUTF8String& aJsMessage, ErrorResult& aRv) { 698 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLSGroupView::Send()")); 699 700 // Handle the message parameter 701 nsTArray<uint8_t> message = ExtractMLSBytesOrUint8ArrayOrUTF8String( 702 MLSObjectType::Application_message_plaintext, aJsMessage, aRv); 703 if (NS_WARN_IF(aRv.Failed())) { 704 return nullptr; 705 } 706 707 // Create a new Promise object for the result 708 RefPtr<Promise> promise = Promise::Create(mMLS->GetParentObject(), aRv); 709 if (NS_WARN_IF(aRv.Failed())) { 710 return nullptr; 711 } 712 713 mMLS->mTransactionChild->SendRequestSend(mGroupId, mClientId, message) 714 ->Then( 715 GetCurrentSerialEventTarget(), __func__, 716 [promise, 717 self = RefPtr<MLSGroupView>(this)](Maybe<RawBytes>&& result) { 718 // Check if the value is Nothing 719 if (result.isNothing()) { 720 promise->MaybeReject(NS_ERROR_FAILURE); 721 return; 722 } 723 724 // Get the context from the GlobalObject 725 AutoJSAPI jsapi; 726 if (NS_WARN_IF(!jsapi.Init(self->mMLS->GetParentObject()))) { 727 promise->MaybeReject(NS_ERROR_FAILURE); 728 return; 729 } 730 JSContext* cx = jsapi.cx(); 731 732 // Construct the Uint8Array object 733 ErrorResult error; 734 JS::Rooted<JSObject*> content( 735 cx, Uint8Array::Create(cx, result->data(), error)); 736 error.WouldReportJSException(); 737 if (error.Failed()) { 738 promise->MaybeReject(std::move(error)); 739 return; 740 } 741 742 // Construct MLSBytes with the group identifier as content 743 RootedDictionary<MLSBytes> rvalue(cx); 744 rvalue.mType = MLSObjectType::Application_message_ciphertext; 745 rvalue.mContent.Init(content); 746 747 promise->MaybeResolve(rvalue); 748 }, 749 [promise](::mozilla::ipc::ResponseRejectReason aReason) { 750 promise->MaybeRejectWithUnknownError("Failed to send message"); 751 }); 752 753 return promise.forget(); 754 } 755 756 already_AddRefed<Promise> MLSGroupView::Receive( 757 const MLSBytesOrUint8Array& aJsMessage, ErrorResult& aRv) { 758 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLSGroupView::Receive()")); 759 760 // Handle the message parameter 761 nsTArray<uint8_t> message = 762 ExtractMLSBytesOrUint8ArrayWithUnknownType(aJsMessage, aRv); 763 if (NS_WARN_IF(aRv.Failed())) { 764 return nullptr; 765 } 766 767 // Check if the message is empty 768 if (NS_WARN_IF(message.IsEmpty())) { 769 aRv.ThrowTypeError("The receivedmessage must not be empty"); 770 return nullptr; 771 } 772 773 // Create a new Promise object for the result 774 RefPtr<Promise> promise = Promise::Create(mMLS->GetParentObject(), aRv); 775 if (NS_WARN_IF(aRv.Failed())) { 776 return nullptr; 777 } 778 779 // Receive the message 780 mMLS->mTransactionChild->SendRequestReceive(mClientId, message) 781 ->Then( 782 GetCurrentSerialEventTarget(), __func__, 783 [promise, self = RefPtr<MLSGroupView>(this)](GkReceived&& received) { 784 // Check if the Maybe contains a value 785 if (received.tag == GkReceived::Tag::None) { 786 promise->MaybeReject(NS_ERROR_FAILURE); 787 return; 788 } 789 790 // Get the context from the GlobalObject 791 AutoJSAPI jsapi; 792 if (NS_WARN_IF(!jsapi.Init(self->mMLS->GetParentObject()))) { 793 promise->MaybeReject(NS_ERROR_FAILURE); 794 return; 795 } 796 JSContext* cx = jsapi.cx(); 797 798 // Construct the Uint8Array objects based on the tag 799 ErrorResult error; 800 JS::Rooted<JSObject*> jsGroupId( 801 cx, Uint8Array::Create(cx, self->mGroupId, error)); 802 error.WouldReportJSException(); 803 if (error.Failed()) { 804 promise->MaybeReject(std::move(error)); 805 return; 806 } 807 808 // Initialize the Received dictionary 809 RootedDictionary<MLSReceived> rvalue(cx); 810 rvalue.mGroupId.Init(jsGroupId); 811 812 // Populate the Received object based on the tag 813 switch (received.tag) { 814 case GkReceived::Tag::GroupIdEpoch: { 815 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, 816 ("Processing GroupIdEpoch")); 817 818 JS::Rooted<JSObject*> jsGroupEpoch( 819 cx, Uint8Array::Create( 820 cx, received.group_id_epoch._0.group_epoch, error)); 821 error.WouldReportJSException(); 822 if (error.Failed()) { 823 promise->MaybeReject(std::move(error)); 824 return; 825 } 826 827 // Populate the Received object 828 rvalue.mType = MLSObjectType::Commit_processed; 829 rvalue.mGroupEpoch.Construct(); 830 rvalue.mGroupEpoch.Value().Init(jsGroupEpoch); 831 break; 832 } 833 case GkReceived::Tag::ApplicationMessage: { 834 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, 835 ("Processing ApplicationMessage")); 836 837 JS::Rooted<JSObject*> jsApplicationMessage( 838 cx, Uint8Array::Create(cx, received.application_message._0, 839 error)); 840 error.WouldReportJSException(); 841 if (error.Failed()) { 842 promise->MaybeReject(std::move(error)); 843 return; 844 } 845 846 // Populate the Received object 847 rvalue.mType = MLSObjectType::Application_message_plaintext; 848 rvalue.mContent.Construct(); 849 rvalue.mContent.Value().Init(jsApplicationMessage); 850 851 break; 852 } 853 case GkReceived::Tag::CommitOutput: { 854 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, 855 ("Processing CommitOutput")); 856 857 JS::Rooted<JSObject*> jsClientId( 858 cx, Uint8Array::Create( 859 cx, received.commit_output._0.identity, error)); 860 error.WouldReportJSException(); 861 if (error.Failed()) { 862 promise->MaybeReject(std::move(error)); 863 return; 864 } 865 JS::Rooted<JSObject*> jsCommit( 866 cx, Uint8Array::Create(cx, received.commit_output._0.commit, 867 error)); 868 error.WouldReportJSException(); 869 if (error.Failed()) { 870 promise->MaybeReject(std::move(error)); 871 return; 872 } 873 JS::Rooted<JSObject*> jsWelcome( 874 cx, Uint8Array::Create( 875 cx, received.commit_output._0.welcome, error)); 876 error.WouldReportJSException(); 877 if (error.Failed()) { 878 promise->MaybeReject(std::move(error)); 879 return; 880 } 881 JS::Rooted<JSObject*> jsGroupInfo( 882 cx, Uint8Array::Create( 883 cx, received.commit_output._0.group_info, error)); 884 error.WouldReportJSException(); 885 if (error.Failed()) { 886 promise->MaybeReject(std::move(error)); 887 return; 888 } 889 JS::Rooted<JSObject*> jsRatchetTree( 890 cx, Uint8Array::Create( 891 cx, received.commit_output._0.ratchet_tree, error)); 892 error.WouldReportJSException(); 893 if (error.Failed()) { 894 promise->MaybeReject(std::move(error)); 895 return; 896 } 897 898 // Construct MLSCommitOutput with the parsed data 899 rvalue.mType = MLSObjectType::Commit_output; 900 rvalue.mGroupId.Init(jsGroupId); 901 rvalue.mCommitOutput.Construct(); 902 rvalue.mCommitOutput.Value().mType = 903 MLSObjectType::Commit_output; 904 rvalue.mCommitOutput.Value().mCommit.Init(jsCommit); 905 rvalue.mCommitOutput.Value().mGroupId.Init(jsGroupId); 906 if (!received.commit_output._0.welcome.IsEmpty()) { 907 rvalue.mCommitOutput.Value().mWelcome.Construct(); 908 rvalue.mCommitOutput.Value().mWelcome.Value().Init(jsWelcome); 909 } 910 if (!received.commit_output._0.group_info.IsEmpty()) { 911 rvalue.mCommitOutput.Value().mGroupInfo.Construct(); 912 rvalue.mCommitOutput.Value().mGroupInfo.Value().Init( 913 jsGroupInfo); 914 } 915 if (!received.commit_output._0.ratchet_tree.IsEmpty()) { 916 rvalue.mCommitOutput.Value().mRatchetTree.Construct(); 917 rvalue.mCommitOutput.Value().mRatchetTree.Value().Init( 918 jsRatchetTree); 919 } 920 if (!received.commit_output._0.identity.IsEmpty()) { 921 rvalue.mCommitOutput.Value().mClientId.Construct(); 922 rvalue.mCommitOutput.Value().mClientId.Value().Init( 923 jsClientId); 924 } 925 926 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, 927 ("Finished processing CommitOutput")); 928 break; 929 } 930 default: 931 MOZ_LOG(gMlsLog, mozilla::LogLevel::Error, 932 ("Unhandled tag in received data")); 933 promise->MaybeRejectWithUnknownError( 934 "Unhandled tag in received data"); 935 return; 936 } 937 938 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, 939 ("Successfully constructed MLSReceived")); 940 941 // Resolve the promise 942 promise->MaybeResolve(rvalue); 943 }, 944 [promise](::mozilla::ipc::ResponseRejectReason aReason) { 945 MOZ_LOG(gMlsLog, mozilla::LogLevel::Error, 946 ("IPC call rejected with reason: %d", 947 static_cast<int>(aReason))); 948 promise->MaybeRejectWithUnknownError("Failed to receive message"); 949 }); 950 951 return promise.forget(); 952 } 953 954 already_AddRefed<Promise> MLSGroupView::HasPendingProposals(ErrorResult& aRv) { 955 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, 956 ("MLSGroupView::HasPendingProposals()")); 957 958 // Create a new Promise object for the result 959 RefPtr<Promise> promise = Promise::Create(mMLS->GetParentObject(), aRv); 960 if (NS_WARN_IF(aRv.Failed())) { 961 return nullptr; 962 } 963 964 // Receive the message 965 mMLS->mTransactionChild->SendRequestHasPendingProposals(mGroupId, mClientId) 966 ->Then( 967 GetCurrentSerialEventTarget(), __func__, 968 [promise, self = RefPtr<MLSGroupView>(this)](bool&& received) { 969 // Get the context from the GlobalObject 970 AutoJSAPI jsapi; 971 if (NS_WARN_IF(!jsapi.Init(self->mMLS->GetParentObject()))) { 972 MOZ_LOG(gMlsLog, mozilla::LogLevel::Error, 973 ("Failed to initialize JSAPI")); 974 promise->MaybeRejectWithUnknownError( 975 "Failed to initialize JSAPI"); 976 return; 977 } 978 979 // Resolve the promise directly with the boolean value 980 promise->MaybeResolve(received); 981 }, 982 [promise](::mozilla::ipc::ResponseRejectReason aReason) { 983 MOZ_LOG(gMlsLog, mozilla::LogLevel::Error, 984 ("IPC call rejected with reason: %d", 985 static_cast<int>(aReason))); 986 promise->MaybeRejectWithUnknownError( 987 "Failed to determine if there are pending proposals"); 988 }); 989 990 return promise.forget(); 991 } 992 993 already_AddRefed<Promise> MLSGroupView::ClearPendingProposals( 994 ErrorResult& aRv) { 995 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, 996 ("MLSGroupView::ClearPendingProposals()")); 997 998 // Create a new Promise object for the result 999 RefPtr<Promise> promise = Promise::Create(mMLS->GetParentObject(), aRv); 1000 if (NS_WARN_IF(aRv.Failed())) { 1001 return nullptr; 1002 } 1003 1004 // Receive the message 1005 mMLS->mTransactionChild->SendRequestClearPendingProposals(mGroupId, mClientId) 1006 ->Then( 1007 GetCurrentSerialEventTarget(), __func__, 1008 [promise, self = RefPtr<MLSGroupView>(this)](bool&& received) { 1009 // Get the context from the GlobalObject 1010 AutoJSAPI jsapi; 1011 if (NS_WARN_IF(!jsapi.Init(self->mMLS->GetParentObject()))) { 1012 MOZ_LOG(gMlsLog, mozilla::LogLevel::Error, 1013 ("Failed to initialize JSAPI")); 1014 promise->MaybeRejectWithUnknownError( 1015 "Failed to initialize JSAPI"); 1016 return; 1017 } 1018 1019 // Resolve the promise directly with the boolean value 1020 promise->MaybeResolve(received); 1021 }, 1022 [promise](::mozilla::ipc::ResponseRejectReason aReason) { 1023 MOZ_LOG(gMlsLog, mozilla::LogLevel::Error, 1024 ("IPC call rejected with reason: %d", 1025 static_cast<int>(aReason))); 1026 promise->MaybeRejectWithUnknownError( 1027 "Failed to clear pending proposals"); 1028 }); 1029 1030 return promise.forget(); 1031 } 1032 1033 already_AddRefed<Promise> MLSGroupView::HasPendingCommit(ErrorResult& aRv) { 1034 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, 1035 ("MLSGroupView::HasPendingCommit()")); 1036 1037 // Create a new Promise object for the result 1038 RefPtr<Promise> promise = Promise::Create(mMLS->GetParentObject(), aRv); 1039 if (NS_WARN_IF(aRv.Failed())) { 1040 return nullptr; 1041 } 1042 1043 // Receive the message 1044 mMLS->mTransactionChild->SendRequestHasPendingCommit(mGroupId, mClientId) 1045 ->Then( 1046 GetCurrentSerialEventTarget(), __func__, 1047 [promise, self = RefPtr<MLSGroupView>(this)](bool&& received) { 1048 // Get the context from the GlobalObject 1049 AutoJSAPI jsapi; 1050 if (NS_WARN_IF(!jsapi.Init(self->mMLS->GetParentObject()))) { 1051 MOZ_LOG(gMlsLog, mozilla::LogLevel::Error, 1052 ("Failed to initialize JSAPI")); 1053 promise->MaybeRejectWithUnknownError( 1054 "Failed to initialize JSAPI"); 1055 return; 1056 } 1057 1058 // Resolve the promise directly with the boolean value 1059 promise->MaybeResolve(received); 1060 }, 1061 [promise](::mozilla::ipc::ResponseRejectReason aReason) { 1062 MOZ_LOG(gMlsLog, mozilla::LogLevel::Error, 1063 ("IPC call rejected with reason: %d", 1064 static_cast<int>(aReason))); 1065 promise->MaybeRejectWithUnknownError( 1066 "Failed to determine if there is a pending commit"); 1067 }); 1068 1069 return promise.forget(); 1070 } 1071 1072 already_AddRefed<Promise> MLSGroupView::ClearPendingCommit(ErrorResult& aRv) { 1073 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, 1074 ("MLSGroupView::ClearPendingCommit()")); 1075 1076 // Create a new Promise object for the result 1077 RefPtr<Promise> promise = Promise::Create(mMLS->GetParentObject(), aRv); 1078 if (NS_WARN_IF(aRv.Failed())) { 1079 return nullptr; 1080 } 1081 1082 // Receive the message 1083 mMLS->mTransactionChild->SendRequestClearPendingCommit(mGroupId, mClientId) 1084 ->Then( 1085 GetCurrentSerialEventTarget(), __func__, 1086 [promise, self = RefPtr<MLSGroupView>(this)](bool&& received) { 1087 // Get the context from the GlobalObject 1088 AutoJSAPI jsapi; 1089 if (NS_WARN_IF(!jsapi.Init(self->mMLS->GetParentObject()))) { 1090 MOZ_LOG(gMlsLog, mozilla::LogLevel::Error, 1091 ("Failed to initialize JSAPI")); 1092 promise->MaybeRejectWithUnknownError( 1093 "Failed to initialize JSAPI"); 1094 return; 1095 } 1096 1097 // Resolve the promise directly with the boolean value 1098 promise->MaybeResolve(received); 1099 }, 1100 [promise](::mozilla::ipc::ResponseRejectReason aReason) { 1101 MOZ_LOG(gMlsLog, mozilla::LogLevel::Error, 1102 ("IPC call rejected with reason: %d", 1103 static_cast<int>(aReason))); 1104 promise->MaybeRejectWithUnknownError( 1105 "Failed to clear pending commit"); 1106 }); 1107 1108 return promise.forget(); 1109 } 1110 1111 already_AddRefed<Promise> MLSGroupView::ApplyPendingCommit(ErrorResult& aRv) { 1112 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, 1113 ("MLSGroupView::ApplyPendingCommit()")); 1114 1115 // Create a new Promise object for the result 1116 RefPtr<Promise> promise = Promise::Create(mMLS->GetParentObject(), aRv); 1117 if (NS_WARN_IF(aRv.Failed())) { 1118 return nullptr; 1119 } 1120 1121 // Receive the message 1122 mMLS->mTransactionChild->SendRequestApplyPendingCommit(mGroupId, mClientId) 1123 ->Then( 1124 GetCurrentSerialEventTarget(), __func__, 1125 [promise, self = RefPtr<MLSGroupView>(this)](GkReceived&& received) { 1126 // Check if the Maybe contains a value 1127 if (received.tag == GkReceived::Tag::None) { 1128 promise->MaybeRejectWithUnknownError( 1129 "Failed to apply pending commit"); 1130 return; 1131 } 1132 1133 // Get the context from the GlobalObject 1134 AutoJSAPI jsapi; 1135 if (NS_WARN_IF(!jsapi.Init(self->mMLS->GetParentObject()))) { 1136 MOZ_LOG(gMlsLog, mozilla::LogLevel::Error, 1137 ("Failed to initialize JSAPI")); 1138 promise->MaybeRejectWithUnknownError( 1139 "Failed to initialize JSAPI"); 1140 return; 1141 } 1142 JSContext* cx = jsapi.cx(); 1143 1144 // Construct the Uint8Array objects based on the tag 1145 ErrorResult error; 1146 JS::Rooted<JSObject*> jsGroupId( 1147 cx, Uint8Array::Create(cx, self->mGroupId, error)); 1148 error.WouldReportJSException(); 1149 if (error.Failed()) { 1150 promise->MaybeReject(std::move(error)); 1151 return; 1152 } 1153 1154 // Initialize the Received dictionary 1155 RootedDictionary<MLSReceived> rvalue(cx); 1156 rvalue.mGroupId.Init(jsGroupId); 1157 1158 // Populate the Received object based on the tag 1159 switch (received.tag) { 1160 case GkReceived::Tag::GroupIdEpoch: { 1161 JS::Rooted<JSObject*> jsGroupEpoch( 1162 cx, Uint8Array::Create( 1163 cx, received.group_id_epoch._0.group_epoch, error)); 1164 error.WouldReportJSException(); 1165 if (error.Failed()) { 1166 promise->MaybeReject(std::move(error)); 1167 return; 1168 } 1169 1170 // Populate the Received object 1171 rvalue.mType = MLSObjectType::Commit_processed; 1172 rvalue.mGroupEpoch.Construct(); 1173 rvalue.mGroupEpoch.Value().Init(jsGroupEpoch); 1174 break; 1175 } 1176 default: 1177 MOZ_LOG(gMlsLog, mozilla::LogLevel::Error, 1178 ("Unhandled tag in received data")); 1179 promise->MaybeRejectWithUnknownError( 1180 "Unhandled tag in received data"); 1181 return; 1182 } 1183 1184 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, 1185 ("Successfully constructed MLSReceived")); 1186 1187 // Resolve the promise 1188 promise->MaybeResolve(rvalue); 1189 }, 1190 [promise](::mozilla::ipc::ResponseRejectReason aReason) { 1191 MOZ_LOG(gMlsLog, mozilla::LogLevel::Error, 1192 ("IPC call rejected with reason: %d", 1193 static_cast<int>(aReason))); 1194 promise->MaybeRejectWithUnknownError( 1195 "Failed to apply pending commit"); 1196 }); 1197 1198 return promise.forget(); 1199 } 1200 1201 already_AddRefed<Promise> MLSGroupView::ExportSecret( 1202 const MLSBytesOrUint8ArrayOrUTF8String& aJsLabel, 1203 const MLSBytesOrUint8Array& aJsContext, const uint64_t aLen, 1204 ErrorResult& aRv) { 1205 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLSGroupView::ExportSecret()")); 1206 1207 // Handle the label parameter 1208 nsTArray<uint8_t> label = ExtractMLSBytesOrUint8ArrayOrUTF8String( 1209 MLSObjectType::Exporter_label, aJsLabel, aRv); 1210 if (NS_WARN_IF(aRv.Failed())) { 1211 return nullptr; 1212 } 1213 1214 // We allow the context to be empty 1215 if (NS_WARN_IF(label.IsEmpty())) { 1216 aRv.ThrowTypeError("The label must not be empty"); 1217 return nullptr; 1218 } 1219 1220 // Handle the context parameter 1221 // Note: we allow the context to be empty 1222 nsTArray<uint8_t> context = ExtractMLSBytesOrUint8Array( 1223 MLSObjectType::Exporter_context, aJsContext, aRv); 1224 if (NS_WARN_IF(aRv.Failed())) { 1225 return nullptr; 1226 } 1227 1228 // Create a new Promise object for the result 1229 RefPtr<Promise> promise = Promise::Create(mMLS->GetParentObject(), aRv); 1230 if (NS_WARN_IF(aRv.Failed())) { 1231 return nullptr; 1232 } 1233 1234 mMLS->mTransactionChild 1235 ->SendRequestExportSecret(mGroupId, mClientId, label, context, aLen) 1236 ->Then( 1237 GetCurrentSerialEventTarget(), __func__, 1238 [promise, self = RefPtr<MLSGroupView>(this)]( 1239 Maybe<mozilla::security::mls::GkExporterOutput>&& 1240 exporterOutput) { 1241 // Check if the Maybe contains a value 1242 if (exporterOutput.isNothing()) { 1243 promise->MaybeReject(NS_ERROR_FAILURE); 1244 return; 1245 } 1246 1247 // Get the context from the GlobalObject 1248 AutoJSAPI jsapi; 1249 if (NS_WARN_IF(!jsapi.Init(self->mMLS->GetParentObject()))) { 1250 promise->MaybeRejectWithUnknownError( 1251 "Failed to initialize JSAPI"); 1252 return; 1253 } 1254 JSContext* cx = jsapi.cx(); 1255 1256 // Construct the Uint8Array objects 1257 ErrorResult error; 1258 JS::Rooted<JSObject*> jsGroupId( 1259 cx, Uint8Array::Create(cx, exporterOutput->group_id, error)); 1260 error.WouldReportJSException(); 1261 if (error.Failed()) { 1262 promise->MaybeReject(std::move(error)); 1263 return; 1264 } 1265 JS::Rooted<JSObject*> jsGroupEpoch( 1266 cx, Uint8Array::Create(cx, exporterOutput->group_epoch, error)); 1267 error.WouldReportJSException(); 1268 if (error.Failed()) { 1269 promise->MaybeReject(std::move(error)); 1270 return; 1271 } 1272 JS::Rooted<JSObject*> jsLabel( 1273 cx, Uint8Array::Create(cx, exporterOutput->label, error)); 1274 error.WouldReportJSException(); 1275 if (error.Failed()) { 1276 promise->MaybeReject(std::move(error)); 1277 return; 1278 } 1279 JS::Rooted<JSObject*> jsContext( 1280 cx, Uint8Array::Create(cx, exporterOutput->context, error)); 1281 error.WouldReportJSException(); 1282 if (error.Failed()) { 1283 promise->MaybeReject(std::move(error)); 1284 return; 1285 } 1286 JS::Rooted<JSObject*> jsExporter( 1287 cx, Uint8Array::Create(cx, exporterOutput->exporter, error)); 1288 error.WouldReportJSException(); 1289 if (error.Failed()) { 1290 promise->MaybeReject(std::move(error)); 1291 return; 1292 } 1293 1294 // Construct MLSBytes with the group identifier as content 1295 RootedDictionary<MLSExporterOutput> rvalue(cx); 1296 rvalue.mType = MLSObjectType::Exporter_output; 1297 rvalue.mGroupId.Init(jsGroupId); 1298 rvalue.mGroupEpoch.Init(jsGroupEpoch); 1299 rvalue.mLabel.Init(jsLabel); 1300 rvalue.mContext.Init(jsContext); 1301 rvalue.mSecret.Init(jsExporter); 1302 1303 // Resolve the promise 1304 promise->MaybeResolve(rvalue); 1305 }, 1306 [promise](::mozilla::ipc::ResponseRejectReason aReason) { 1307 promise->MaybeRejectWithUnknownError("Failed to export secret"); 1308 }); 1309 1310 return promise.forget(); 1311 } 1312 1313 }; // namespace mozilla::dom