MLS.cpp (24311B)
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 "mozilla/dom/MLS.h" 8 9 #include "MLSGroupView.h" 10 #include "MLSLogging.h" 11 #include "MLSTypeUtils.h" 12 #include "mozilla/BasePrincipal.h" 13 #include "mozilla/Logging.h" 14 #include "mozilla/dom/MLSGroupView.h" 15 #include "mozilla/dom/MLSTransactionChild.h" 16 #include "mozilla/dom/MLSTransactionMessage.h" 17 #include "mozilla/dom/PMLSTransaction.h" 18 #include "mozilla/dom/Promise.h" 19 #include "mozilla/dom/TypedArray.h" 20 #include "mozilla/ipc/BackgroundChild.h" 21 #include "mozilla/ipc/Endpoint.h" 22 #include "mozilla/ipc/PBackgroundChild.h" 23 #include "nsCOMPtr.h" 24 #include "nsDebug.h" 25 #include "nsIGlobalObject.h" 26 #include "nsTArray.h" 27 28 namespace mozilla::dom { 29 30 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MLS, mGlobalObject) 31 32 NS_IMPL_CYCLE_COLLECTING_ADDREF(MLS) 33 NS_IMPL_CYCLE_COLLECTING_RELEASE(MLS) 34 35 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MLS) 36 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 37 NS_INTERFACE_MAP_ENTRY(nsISupports) 38 NS_INTERFACE_MAP_END 39 40 // Setup logging 41 mozilla::LazyLogModule gMlsLog("MLS"); 42 43 /* static */ already_AddRefed<MLS> MLS::Constructor(GlobalObject& aGlobalObject, 44 ErrorResult& aRv) { 45 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLS::Constructor()")); 46 47 nsCOMPtr<nsIGlobalObject> global( 48 do_QueryInterface(aGlobalObject.GetAsSupports())); 49 if (NS_WARN_IF(!global)) { 50 aRv.Throw(NS_ERROR_FAILURE); 51 return nullptr; 52 } 53 54 // Get the principal and perform some validation on it. 55 // We do not allow MLS in Private Browsing Mode for now. 56 nsIPrincipal* principal = global->PrincipalOrNull(); 57 if (!principal || !principal->GetIsContentPrincipal() || 58 principal->GetIsInPrivateBrowsing()) { 59 aRv.ThrowSecurityError("Cannot create MLS store for origin"); 60 return nullptr; 61 } 62 63 // Create the endpoints for the MLS actor 64 mozilla::ipc::Endpoint<PMLSTransactionParent> parentEndpoint; 65 mozilla::ipc::Endpoint<PMLSTransactionChild> childEndpoint; 66 MOZ_ALWAYS_SUCCEEDS( 67 PMLSTransaction::CreateEndpoints(&parentEndpoint, &childEndpoint)); 68 69 mozilla::ipc::PBackgroundChild* backgroundChild = 70 mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(); 71 if (!backgroundChild) { 72 aRv.Throw(NS_ERROR_UNEXPECTED); 73 return nullptr; 74 } 75 76 // Bind the child actor, and send the parent endpoint. 77 RefPtr<MLSTransactionChild> actor = new MLSTransactionChild(); 78 MOZ_ALWAYS_TRUE(childEndpoint.Bind(actor)); 79 80 MOZ_ALWAYS_TRUE(backgroundChild->SendCreateMLSTransaction( 81 std::move(parentEndpoint), WrapNotNull(principal))); 82 83 return MakeAndAddRef<MLS>(global, actor); 84 } 85 86 MLS::MLS(nsIGlobalObject* aGlobalObject, MLSTransactionChild* aActor) 87 : mGlobalObject(aGlobalObject), mTransactionChild(aActor) { 88 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLS::MLS()")); 89 } 90 91 MLS::~MLS() { 92 if (mTransactionChild) { 93 mTransactionChild->Close(); 94 } 95 } 96 97 JSObject* MLS::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { 98 return MLS_Binding::Wrap(aCx, this, aGivenProto); 99 } 100 101 // 102 // API 103 // 104 105 already_AddRefed<Promise> MLS::DeleteState(ErrorResult& aRv) { 106 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLS::DeleteState()")); 107 108 // Create a new Promise object for the result 109 RefPtr<Promise> promise = Promise::Create(mGlobalObject, aRv); 110 if (NS_WARN_IF(aRv.Failed())) { 111 return nullptr; 112 } 113 114 mTransactionChild->SendRequestStateDelete( 115 [promise](bool result) { 116 if (result) { 117 promise->MaybeResolveWithUndefined(); 118 } else { 119 promise->MaybeReject(NS_ERROR_FAILURE); 120 } 121 }, 122 [promise](::mozilla::ipc::ResponseRejectReason) { 123 promise->MaybeRejectWithUnknownError("deleteState failed"); 124 }); 125 126 return promise.forget(); 127 } 128 129 already_AddRefed<Promise> MLS::GenerateIdentity(ErrorResult& aRv) { 130 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLS::GenerateIdentity()")); 131 132 // Create a new Promise object for the result 133 RefPtr<Promise> promise = Promise::Create(mGlobalObject, aRv); 134 if (NS_WARN_IF(aRv.Failed())) { 135 return nullptr; 136 } 137 138 mTransactionChild->SendRequestGenerateIdentityKeypair()->Then( 139 GetCurrentSerialEventTarget(), __func__, 140 [promise, self = RefPtr{this}](Maybe<RawBytes>&& result) { 141 // Check if the value is Nothing 142 if (result.isNothing()) { 143 promise->MaybeRejectWithUnknownError( 144 "generateIdentityKeypair failed"); 145 return; 146 } 147 148 // Get the context from the GlobalObject 149 AutoJSAPI jsapi; 150 if (NS_WARN_IF(!jsapi.Init(self->mGlobalObject))) { 151 promise->MaybeRejectWithUnknownError( 152 "generateIdentityKeypair failed"); 153 return; 154 } 155 JSContext* cx = jsapi.cx(); 156 157 // Construct the Uint8Array object 158 ErrorResult error; 159 JS::Rooted<JSObject*> content( 160 cx, Uint8Array::Create(cx, result->data(), error)); 161 error.WouldReportJSException(); 162 if (error.Failed()) { 163 promise->MaybeReject(std::move(error)); 164 return; 165 } 166 167 // Construct MLSBytes with the client identifer as content 168 RootedDictionary<MLSBytes> rvalue(cx); 169 rvalue.mType = MLSObjectType::Client_identifier; 170 rvalue.mContent.Init(content); 171 172 // Resolve the promise with the MLSBytes object 173 promise->MaybeResolve(rvalue); 174 }, 175 [promise](::mozilla::ipc::ResponseRejectReason aReason) { 176 promise->MaybeRejectWithUnknownError("generateIdentity failed"); 177 }); 178 179 return promise.forget(); 180 } 181 182 already_AddRefed<Promise> MLS::GenerateCredential( 183 const MLSBytesOrUint8ArrayOrUTF8String& aJsCredContent, ErrorResult& aRv) { 184 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, 185 ("MLS::GenerateCredentialBasic()")); 186 187 // Handle the credential content parameter 188 nsTArray<uint8_t> credContent = ExtractMLSBytesOrUint8ArrayOrUTF8String( 189 MLSObjectType::Credential_basic, aJsCredContent, aRv); 190 if (NS_WARN_IF(aRv.Failed())) { 191 return nullptr; 192 } 193 194 // Check if the credContent is empty 195 if (NS_WARN_IF(credContent.IsEmpty())) { 196 aRv.ThrowTypeError("The credential content must not be empty"); 197 return nullptr; 198 } 199 200 // Create a new Promise object for the result 201 RefPtr<Promise> promise = Promise::Create(mGlobalObject, aRv); 202 if (NS_WARN_IF(aRv.Failed())) { 203 return nullptr; 204 } 205 206 mTransactionChild->SendRequestGenerateCredentialBasic(credContent) 207 ->Then( 208 GetCurrentSerialEventTarget(), __func__, 209 [promise, self = RefPtr{this}](Maybe<RawBytes>&& result) { 210 // Check if the value is Nothing 211 if (result.isNothing()) { 212 promise->MaybeRejectWithUnknownError( 213 "generateCredentialBasic failed"); 214 return; 215 } 216 217 // Get the context from the GlobalObject 218 AutoJSAPI jsapi; 219 if (NS_WARN_IF(!jsapi.Init(self->mGlobalObject))) { 220 promise->MaybeRejectWithUnknownError( 221 "generateCredentialBasic failed"); 222 return; 223 } 224 JSContext* cx = jsapi.cx(); 225 226 // Construct the Uint8Array object 227 ErrorResult error; 228 JS::Rooted<JSObject*> content( 229 cx, Uint8Array::Create(cx, result->data(), error)); 230 error.WouldReportJSException(); 231 if (error.Failed()) { 232 promise->MaybeReject(std::move(error)); 233 return; 234 } 235 236 // Construct MLSBytes with the client identifer as content 237 RootedDictionary<MLSBytes> rvalue(cx); 238 rvalue.mType = MLSObjectType::Credential_basic; 239 rvalue.mContent.Init(content); 240 241 // Resolve the promise 242 promise->MaybeResolve(rvalue); 243 }, 244 [promise](::mozilla::ipc::ResponseRejectReason aReason) { 245 promise->MaybeRejectWithUnknownError( 246 "generateCredentialBasic failed"); 247 }); 248 249 return promise.forget(); 250 } 251 252 already_AddRefed<Promise> MLS::GenerateKeyPackage( 253 const MLSBytesOrUint8Array& aJsClientIdentifier, 254 const MLSBytesOrUint8Array& aJsCredential, ErrorResult& aRv) { 255 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLS::GenerateKeyPackage()")); 256 257 // Handle the client identifier parameter 258 nsTArray<uint8_t> clientIdentifier = ExtractMLSBytesOrUint8Array( 259 MLSObjectType::Client_identifier, aJsClientIdentifier, aRv); 260 if (NS_WARN_IF(aRv.Failed())) { 261 return nullptr; 262 } 263 264 // Check if the client identifier is empty 265 if (NS_WARN_IF(clientIdentifier.IsEmpty())) { 266 aRv.ThrowTypeError("The client identifier must not be empty"); 267 return nullptr; 268 } 269 270 // Handle the credential parameter 271 nsTArray<uint8_t> credential = ExtractMLSBytesOrUint8Array( 272 MLSObjectType::Credential_basic, aJsCredential, aRv); 273 if (NS_WARN_IF(aRv.Failed())) { 274 return nullptr; 275 } 276 277 // Check if the credential is empty 278 if (NS_WARN_IF(credential.IsEmpty())) { 279 aRv.ThrowTypeError("The credential must not be empty"); 280 return nullptr; 281 } 282 283 // Create a new Promise object for the result 284 RefPtr<Promise> promise = Promise::Create(mGlobalObject, aRv); 285 if (NS_WARN_IF(aRv.Failed())) { 286 return nullptr; 287 } 288 289 // Use the static method or instance to send the IPC message 290 mTransactionChild->SendRequestGenerateKeyPackage(clientIdentifier, credential) 291 ->Then( 292 GetCurrentSerialEventTarget(), __func__, 293 [promise, self = RefPtr{this}](Maybe<RawBytes>&& keyPackage) { 294 // Check if the value is Nothing 295 if (keyPackage.isNothing()) { 296 promise->MaybeReject(NS_ERROR_FAILURE); 297 return; 298 } 299 300 // Get the context from the GlobalObject 301 AutoJSAPI jsapi; 302 if (NS_WARN_IF(!jsapi.Init(self->mGlobalObject))) { 303 promise->MaybeReject(NS_ERROR_FAILURE); 304 return; 305 } 306 JSContext* cx = jsapi.cx(); 307 308 // Construct the Uint8Array object 309 ErrorResult error; 310 JS::Rooted<JSObject*> content( 311 cx, Uint8Array::Create(cx, keyPackage->data(), error)); 312 error.WouldReportJSException(); 313 if (error.Failed()) { 314 promise->MaybeReject(std::move(error)); 315 return; 316 } 317 318 // Construct MLSBytes with the client identifer as content 319 RootedDictionary<MLSBytes> rvalue(cx); 320 rvalue.mType = MLSObjectType::Key_package; 321 rvalue.mContent.Init(content); 322 323 // Resolve the promise 324 promise->MaybeResolve(rvalue); 325 }, 326 [promise](::mozilla::ipc::ResponseRejectReason aReason) { 327 promise->MaybeRejectWithUnknownError("generateKeyPackage failed"); 328 }); 329 330 return promise.forget(); 331 } 332 333 already_AddRefed<Promise> MLS::GroupCreate( 334 const MLSBytesOrUint8Array& aJsClientIdentifier, 335 const MLSBytesOrUint8Array& aJsCredential, ErrorResult& aRv) { 336 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLS::GroupCreate()")); 337 338 // Handle the client identifier parameter 339 nsTArray<uint8_t> clientIdentifier = ExtractMLSBytesOrUint8Array( 340 MLSObjectType::Client_identifier, aJsClientIdentifier, aRv); 341 if (NS_WARN_IF(aRv.Failed())) { 342 return nullptr; 343 } 344 345 // Check if the client identifier is empty 346 if (NS_WARN_IF(clientIdentifier.IsEmpty())) { 347 aRv.ThrowTypeError("The client identifier must not be empty"); 348 return nullptr; 349 } 350 351 // Handle the credential parameter 352 nsTArray<uint8_t> credential = ExtractMLSBytesOrUint8Array( 353 MLSObjectType::Credential_basic, aJsCredential, aRv); 354 if (NS_WARN_IF(aRv.Failed())) { 355 return nullptr; 356 } 357 358 // Check if the credential is empty 359 if (NS_WARN_IF(credential.IsEmpty())) { 360 aRv.ThrowTypeError("The credential must not be empty"); 361 return nullptr; 362 } 363 364 // Log the hex of clientIdentifier 365 if (MOZ_LOG_TEST(gMlsLog, LogLevel::Debug)) { 366 nsAutoCString clientIdHex; 367 for (uint8_t byte : clientIdentifier) { 368 clientIdHex.AppendPrintf("%02X", byte); 369 } 370 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, 371 ("clientIdentifier in hex: %s\n", clientIdHex.get())); 372 } 373 374 // Initialize jsGroupIdentifier to one byte of value 0xFF. 375 // We do not want to allow choosing the GID at this point. 376 // This value not being of the correct length will be discarded 377 // internally and a fresh GID will be generated. 378 // 379 // In the future, the caller will allow choosing the GID. 380 AutoTArray<uint8_t, 1> groupIdentifier{0xFF}; 381 382 // Create a new Promise object for the result 383 RefPtr<Promise> promise = Promise::Create(mGlobalObject, aRv); 384 if (NS_WARN_IF(aRv.Failed())) { 385 return nullptr; 386 } 387 388 // Use the static method or instance to send the IPC message 389 mTransactionChild 390 ->SendRequestGroupCreate(clientIdentifier, credential, groupIdentifier) 391 ->Then( 392 GetCurrentSerialEventTarget(), __func__, 393 [promise, self = RefPtr{this}, 394 clientIdentifier(std::move(clientIdentifier))]( 395 Maybe<mozilla::security::mls::GkGroupIdEpoch>&& 396 groupIdEpoch) mutable { 397 // Check if the value is Nothing 398 if (groupIdEpoch.isNothing()) { 399 promise->MaybeReject(NS_ERROR_FAILURE); 400 return; 401 } 402 403 RefPtr<MLSGroupView> group = 404 new MLSGroupView(self, std::move(groupIdEpoch->group_id), 405 std::move(clientIdentifier)); 406 407 promise->MaybeResolve(group); 408 }, 409 [promise](::mozilla::ipc::ResponseRejectReason aReason) { 410 MOZ_LOG(gMlsLog, mozilla::LogLevel::Error, 411 ("IPC message rejected with reason: %d", 412 static_cast<int>(aReason))); 413 promise->MaybeRejectWithUnknownError("groupCreate failed"); 414 }); 415 416 return promise.forget(); 417 } 418 419 already_AddRefed<mozilla::dom::Promise> MLS::GroupGet( 420 const MLSBytesOrUint8Array& aJsGroupIdentifier, 421 const MLSBytesOrUint8Array& aJsClientIdentifier, ErrorResult& aRv) { 422 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLS::GroupGet()")); 423 424 // Handle the group identifier parameter 425 nsTArray<uint8_t> groupIdentifier = ExtractMLSBytesOrUint8Array( 426 MLSObjectType::Group_identifier, aJsGroupIdentifier, aRv); 427 if (NS_WARN_IF(aRv.Failed())) { 428 return nullptr; 429 } 430 431 // Check if the group identifier is empty 432 if (NS_WARN_IF(groupIdentifier.IsEmpty())) { 433 aRv.ThrowTypeError("The group identifier must not be empty"); 434 return nullptr; 435 } 436 437 // Handle the client identifier parameter 438 nsTArray<uint8_t> clientIdentifier = ExtractMLSBytesOrUint8Array( 439 MLSObjectType::Client_identifier, aJsClientIdentifier, aRv); 440 if (NS_WARN_IF(aRv.Failed())) { 441 return nullptr; 442 } 443 444 // Check if the client identifier is empty 445 if (NS_WARN_IF(clientIdentifier.IsEmpty())) { 446 aRv.ThrowTypeError("The 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(mGlobalObject, aRv); 452 if (NS_WARN_IF(aRv.Failed())) { 453 return nullptr; 454 } 455 456 // Initialize label, context and len 457 // We pass this through IPC to be able to reuse the same code for different 458 // labels in the future 459 AutoTArray<uint8_t, 7> label{'l', 'i', 'v', 'e', 'n', 'e', 's', 's'}; 460 AutoTArray<uint8_t, 1> context{0x00}; 461 uint64_t len = 32; 462 463 // Use the static method or instance to send the IPC message 464 mTransactionChild 465 ->SendRequestExportSecret(groupIdentifier, clientIdentifier, label, 466 context, len) 467 ->Then( 468 GetCurrentSerialEventTarget(), __func__, 469 [promise, self = RefPtr{this}, 470 groupIdentifier(std::move(groupIdentifier)), 471 clientIdentifier(std::move(clientIdentifier))]( 472 Maybe<mozilla::security::mls::GkExporterOutput>&& 473 exporterOutput) mutable { 474 // Check if the exporterOutput contains a value 475 if (exporterOutput.isNothing()) { 476 promise->MaybeReject(NS_ERROR_FAILURE); 477 return; 478 } 479 480 RefPtr<MLSGroupView> group = 481 new MLSGroupView(self, std::move(exporterOutput->group_id), 482 std::move(clientIdentifier)); 483 promise->MaybeResolve(group); 484 }, 485 [promise](::mozilla::ipc::ResponseRejectReason aReason) { 486 promise->MaybeRejectWithUnknownError("exportSecret failed"); 487 }); 488 489 return promise.forget(); 490 } 491 492 already_AddRefed<Promise> MLS::GroupJoin( 493 const MLSBytesOrUint8Array& aJsClientIdentifier, 494 const MLSBytesOrUint8Array& aJsWelcome, ErrorResult& aRv) { 495 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLS::GroupJoin()")); 496 497 // Handle the client identifier parameter 498 nsTArray<uint8_t> clientIdentifier = ExtractMLSBytesOrUint8Array( 499 MLSObjectType::Client_identifier, aJsClientIdentifier, aRv); 500 if (NS_WARN_IF(aRv.Failed())) { 501 return nullptr; 502 } 503 504 // Check if the client identifier is empty 505 if (NS_WARN_IF(clientIdentifier.IsEmpty())) { 506 aRv.ThrowTypeError("The client identifier must not be empty"); 507 return nullptr; 508 } 509 510 // Handle the welcome parameter 511 nsTArray<uint8_t> welcome = 512 ExtractMLSBytesOrUint8Array(MLSObjectType::Welcome, aJsWelcome, aRv); 513 if (NS_WARN_IF(aRv.Failed())) { 514 return nullptr; 515 } 516 517 // Check if the welcome is empty 518 if (NS_WARN_IF(welcome.IsEmpty())) { 519 aRv.ThrowTypeError("The welcome must not be empty"); 520 return nullptr; 521 } 522 523 // Create a new Promise object for the result 524 RefPtr<Promise> promise = Promise::Create(mGlobalObject, aRv); 525 if (NS_WARN_IF(aRv.Failed())) { 526 return nullptr; 527 } 528 529 mTransactionChild->SendRequestGroupJoin(clientIdentifier, welcome) 530 ->Then( 531 GetCurrentSerialEventTarget(), __func__, 532 [promise, self = RefPtr{this}, 533 clientIdentifier(std::move(clientIdentifier))]( 534 Maybe<mozilla::security::mls::GkGroupIdEpoch>&& 535 groupIdEpoch) mutable { 536 // Check if the value is Nothing 537 if (groupIdEpoch.isNothing()) { 538 promise->MaybeReject(NS_ERROR_FAILURE); 539 return; 540 } 541 542 // Returns groupId and epoch 543 RefPtr<MLSGroupView> group = 544 new MLSGroupView(self, std::move(groupIdEpoch->group_id), 545 std::move(clientIdentifier)); 546 547 // Resolve the promise 548 promise->MaybeResolve(group); 549 }, 550 [promise](::mozilla::ipc::ResponseRejectReason aReason) { 551 promise->MaybeRejectWithUnknownError("groupJoin failed"); 552 }); 553 554 return promise.forget(); 555 } 556 557 already_AddRefed<Promise> MLS::GetGroupIdFromMessage( 558 const MLSBytesOrUint8Array& aJsMessage, ErrorResult& aRv) { 559 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLS::GetGroupIdFromMessage()")); 560 561 // Handle the message parameter 562 nsTArray<uint8_t> message = 563 ExtractMLSBytesOrUint8ArrayWithUnknownType(aJsMessage, aRv); 564 if (NS_WARN_IF(aRv.Failed())) { 565 return nullptr; 566 } 567 568 // Check if the message is empty 569 if (NS_WARN_IF(message.IsEmpty())) { 570 aRv.ThrowTypeError("The message must not be empty"); 571 return nullptr; 572 } 573 574 // Create a new Promise object for the result 575 RefPtr<Promise> promise = Promise::Create(mGlobalObject, aRv); 576 if (NS_WARN_IF(aRv.Failed())) { 577 return nullptr; 578 } 579 580 mTransactionChild->SendRequestGetGroupIdentifier(message)->Then( 581 GetCurrentSerialEventTarget(), __func__, 582 [promise, self = RefPtr{this}, 583 message(std::move(message))](Maybe<RawBytes>&& result) { 584 // Check if the value is Nothing 585 if (result.isNothing()) { 586 promise->MaybeReject(NS_ERROR_FAILURE); 587 return; 588 } 589 590 // Get the context from the GlobalObject 591 AutoJSAPI jsapi; 592 if (NS_WARN_IF(!jsapi.Init(self->mGlobalObject))) { 593 MOZ_LOG(gMlsLog, mozilla::LogLevel::Error, 594 ("Failed to initialize JSAPI")); 595 promise->MaybeReject(NS_ERROR_FAILURE); 596 return; 597 } 598 JSContext* cx = jsapi.cx(); 599 600 // Construct the Uint8Array objects based on the tag 601 ErrorResult error; 602 JS::Rooted<JSObject*> jsGroupId( 603 cx, Uint8Array::Create(cx, result->data(), error)); 604 error.WouldReportJSException(); 605 if (error.Failed()) { 606 promise->MaybeReject(std::move(error)); 607 return; 608 } 609 610 // Construct the MLSBytes object for the groupId 611 RootedDictionary<MLSBytes> rvalue(cx); 612 rvalue.mType = MLSObjectType::Group_identifier; 613 rvalue.mContent.Init(jsGroupId); 614 615 // Log if in debug mode 616 if (MOZ_LOG_TEST(gMlsLog, LogLevel::Debug)) { 617 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, 618 ("Successfully constructed MLSBytes")); 619 } 620 621 // Resolve the promise 622 promise->MaybeResolve(rvalue); 623 }, 624 [promise](::mozilla::ipc::ResponseRejectReason aReason) { 625 MOZ_LOG( 626 gMlsLog, mozilla::LogLevel::Error, 627 ("IPC call rejected with reason: %d", static_cast<int>(aReason))); 628 promise->MaybeRejectWithUnknownError("getGroupIdFromMessage failed"); 629 }); 630 631 return promise.forget(); 632 } 633 634 already_AddRefed<Promise> MLS::GetGroupEpochFromMessage( 635 const MLSBytesOrUint8Array& aJsMessage, ErrorResult& aRv) { 636 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, 637 ("MLS::GetGroupEpochFromMessage()")); 638 639 // Handle the message parameter 640 nsTArray<uint8_t> message = 641 ExtractMLSBytesOrUint8ArrayWithUnknownType(aJsMessage, aRv); 642 if (NS_WARN_IF(aRv.Failed())) { 643 return nullptr; 644 } 645 646 // Check if the message is empty 647 if (NS_WARN_IF(message.IsEmpty())) { 648 aRv.ThrowTypeError("The message must not be empty"); 649 return nullptr; 650 } 651 652 // Create a new Promise object for the result 653 RefPtr<Promise> promise = Promise::Create(mGlobalObject, aRv); 654 if (NS_WARN_IF(aRv.Failed())) { 655 return nullptr; 656 } 657 658 mTransactionChild->SendRequestGetGroupEpoch(message)->Then( 659 GetCurrentSerialEventTarget(), __func__, 660 [promise, self = RefPtr{this}, 661 message(std::move(message))](Maybe<RawBytes>&& result) { 662 // Check if the value is Nothing 663 if (result.isNothing()) { 664 promise->MaybeReject(NS_ERROR_FAILURE); 665 return; 666 } 667 668 // Get the context from the GlobalObject 669 AutoJSAPI jsapi; 670 if (NS_WARN_IF(!jsapi.Init(self->mGlobalObject))) { 671 MOZ_LOG(gMlsLog, mozilla::LogLevel::Error, 672 ("Failed to initialize JSAPI")); 673 promise->MaybeReject(NS_ERROR_FAILURE); 674 return; 675 } 676 JSContext* cx = jsapi.cx(); 677 678 // Construct the Uint8Array objects based on the tag 679 ErrorResult error; 680 JS::Rooted<JSObject*> jsGroupId( 681 cx, Uint8Array::Create(cx, result->data(), error)); 682 error.WouldReportJSException(); 683 if (error.Failed()) { 684 promise->MaybeReject(std::move(error)); 685 return; 686 } 687 688 // Construct the MLSBytes object for the groupId 689 RootedDictionary<MLSBytes> rvalue(cx); 690 rvalue.mType = MLSObjectType::Group_epoch; 691 rvalue.mContent.Init(jsGroupId); 692 693 // Log if in debug mode 694 if (MOZ_LOG_TEST(gMlsLog, LogLevel::Debug)) { 695 MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, 696 ("Successfully constructed MLSBytes")); 697 } 698 699 // Resolve the promise 700 promise->MaybeResolve(rvalue); 701 }, 702 [promise](::mozilla::ipc::ResponseRejectReason aReason) { 703 MOZ_LOG( 704 gMlsLog, mozilla::LogLevel::Error, 705 ("IPC call rejected with reason: %d", static_cast<int>(aReason))); 706 promise->MaybeRejectWithUnknownError("getGroupEpochFromMessage failed"); 707 }); 708 709 return promise.forget(); 710 } 711 712 } // namespace mozilla::dom