... |
... |
@@ -37,6 +37,7 @@ XPCOMUtils.defineLazyModuleGetters(lazy, { |
37
|
37
|
set_panic_hook: "resource://gre/modules/lox_wasm.jsm",
|
38
|
38
|
invitation_is_trusted: "resource://gre/modules/lox_wasm.jsm",
|
39
|
39
|
issue_invite: "resource://gre/modules/lox_wasm.jsm",
|
|
40
|
+ handle_issue_invite: "resource://gre/modules/lox_wasm.jsm",
|
40
|
41
|
prepare_invite: "resource://gre/modules/lox_wasm.jsm",
|
41
|
42
|
get_invites_remaining: "resource://gre/modules/lox_wasm.jsm",
|
42
|
43
|
get_trust_level: "resource://gre/modules/lox_wasm.jsm",
|
... |
... |
@@ -91,6 +92,7 @@ const LoxSettingsPrefs = Object.freeze({ |
91
|
92
|
export class LoxError extends Error {
|
92
|
93
|
static BadInvite = "BadInvite";
|
93
|
94
|
static LoxServerUnreachable = "LoxServerUnreachable";
|
|
95
|
+ static ErrorResponse = "ErrorResponse";
|
94
|
96
|
|
95
|
97
|
/**
|
96
|
98
|
* @param {string} message - The error message.
|
... |
... |
@@ -408,89 +410,47 @@ class LoxImpl { |
408
|
410
|
}
|
409
|
411
|
|
410
|
412
|
/**
|
411
|
|
- * Update Lox credential after Lox key rotation
|
412
|
|
- * Do not call directly, use #getPubKeys() instead to start the update only
|
413
|
|
- * once
|
|
413
|
+ * Update Lox credential after Lox key rotation.
|
414
|
414
|
*
|
415
|
|
- * @param {string} prevkeys The public keys we are replacing
|
|
415
|
+ * Do not call directly, use #getPubKeys() instead to start the update only
|
|
416
|
+ * once.
|
416
|
417
|
*/
|
417
|
|
- async #updatePubkeys(prevkeys) {
|
418
|
|
- let pubKeys;
|
419
|
|
- try {
|
420
|
|
- pubKeys = await this.#makeRequest("pubkeys", []);
|
421
|
|
- } catch (error) {
|
422
|
|
- lazy.logger.debug("Failed to get pubkeys", error);
|
423
|
|
- // Make the next call try again.
|
424
|
|
- this.#pubKeyPromise = null;
|
425
|
|
- if (!this.#pubKeys) {
|
426
|
|
- throw error;
|
427
|
|
- }
|
428
|
|
- return;
|
429
|
|
- }
|
|
418
|
+ async #updatePubkeys() {
|
|
419
|
+ let pubKeys = await this.#makeRequest("pubkeys", null);
|
430
|
420
|
const prevKeys = this.#pubKeys;
|
431
|
421
|
if (prevKeys !== null) {
|
432
|
422
|
// check if the lox pubkeys have changed and update the lox
|
433
|
|
- // credentials if so
|
434
|
|
- let lox_cred_req;
|
435
|
|
- try {
|
436
|
|
- lox_cred_req = JSON.parse(
|
437
|
|
- lazy.check_lox_pubkeys_update(
|
438
|
|
- JSON.stringify(pubKeys),
|
439
|
|
- prevkeys,
|
440
|
|
- this.#getCredentials(this.#activeLoxId)
|
441
|
|
- )
|
442
|
|
- );
|
443
|
|
- } catch (error) {
|
444
|
|
- lazy.logger.debug("Check lox pubkey update failed", error);
|
445
|
|
- // Make the next call try again.
|
446
|
|
- this.#pubKeyPromise = null;
|
447
|
|
- return;
|
448
|
|
- }
|
449
|
|
- if (lox_cred_req.updated) {
|
|
423
|
+ // credentials if so.
|
|
424
|
+ //
|
|
425
|
+ // The UpdateCredOption rust struct serializes to "req" rather than
|
|
426
|
+ // "request".
|
|
427
|
+ const { updated, req: request } = JSON.parse(
|
|
428
|
+ lazy.check_lox_pubkeys_update(
|
|
429
|
+ pubKeys,
|
|
430
|
+ prevKeys,
|
|
431
|
+ this.#getCredentials(this.#activeLoxId)
|
|
432
|
+ )
|
|
433
|
+ );
|
|
434
|
+ if (updated) {
|
|
435
|
+ // Try update credentials.
|
|
436
|
+ // NOTE: This should be re-callable if any step fails.
|
|
437
|
+ // TODO: Verify this.
|
450
|
438
|
lazy.logger.debug(
|
451
|
439
|
`Lox pubkey updated, update Lox credential "${this.#activeLoxId}"`
|
452
|
440
|
);
|
453
|
|
- let response;
|
454
|
|
- try {
|
455
|
|
- // TODO: If this call doesn't succeed due to a networking error, the Lox
|
456
|
|
- // credential may be in an unusable state (spent but not updated)
|
457
|
|
- // until this request can be completed successfully (and until Lox
|
458
|
|
- // is refactored to send repeat responses:
|
459
|
|
- // https://gitlab.torproject.org/tpo/anti-censorship/lox/-/issues/74)
|
460
|
|
- response = await this.#makeRequest("updatecred", lox_cred_req.req);
|
461
|
|
- } catch (error) {
|
462
|
|
- lazy.logger.debug("Lox cred update failed.", error);
|
463
|
|
- // Make the next call try again.
|
464
|
|
- this.#pubKeyPromise = null;
|
465
|
|
- return;
|
466
|
|
- }
|
467
|
|
- if (response.hasOwnProperty("error")) {
|
468
|
|
- lazy.logger.error(response.error);
|
469
|
|
- this.#pubKeyPromise = null;
|
470
|
|
- lazy.logger.debug(
|
471
|
|
- `Error response to Lox pubkey update request: "${response.error}", reverting to old pubkeys`
|
472
|
|
- );
|
473
|
|
- return;
|
474
|
|
- }
|
475
|
|
- let cred;
|
476
|
|
- try {
|
477
|
|
- cred = lazy.handle_update_cred(
|
478
|
|
- lox_cred_req.req,
|
479
|
|
- JSON.stringify(response),
|
480
|
|
- pubKeys
|
481
|
|
- );
|
482
|
|
- } catch (error) {
|
483
|
|
- lazy.logger.debug("Unable to handle updated Lox cred", error);
|
484
|
|
- // Make the next call try again.
|
485
|
|
- this.#pubKeyPromise = null;
|
486
|
|
- return;
|
487
|
|
- }
|
|
441
|
+ // TODO: If this call doesn't succeed due to a networking error, the Lox
|
|
442
|
+ // credential may be in an unusable state (spent but not updated)
|
|
443
|
+ // until this request can be completed successfully (and until Lox
|
|
444
|
+ // is refactored to send repeat responses:
|
|
445
|
+ // https://gitlab.torproject.org/tpo/anti-censorship/lox/-/issues/74)
|
|
446
|
+ let response = await this.#makeRequest("updatecred", request);
|
|
447
|
+ let cred = lazy.handle_update_cred(request, response, pubKeys);
|
488
|
448
|
this.#changeCredentials(this.#activeLoxId, cred);
|
489
|
449
|
}
|
490
|
450
|
}
|
491
|
451
|
// If we arrive here we haven't had other errors before, we can actually
|
492
|
452
|
// store the new public key.
|
493
|
|
- this.#pubKeys = JSON.stringify(pubKeys);
|
|
453
|
+ this.#pubKeys = pubKeys;
|
494
|
454
|
this.#store();
|
495
|
455
|
}
|
496
|
456
|
|
... |
... |
@@ -498,16 +458,24 @@ class LoxImpl { |
498
|
458
|
// FIXME: We are always refetching #pubKeys, #encTable and #constants once
|
499
|
459
|
// per session, but they may change more frequently. tor-browser#42502
|
500
|
460
|
if (this.#pubKeyPromise === null) {
|
501
|
|
- this.#pubKeyPromise = this.#updatePubkeys();
|
|
461
|
+ this.#pubKeyPromise = this.#updatePubkeys().catch(error => {
|
|
462
|
+ lazy.logger.debug("Failed to update pubKeys", error);
|
|
463
|
+ // Try again with the next call.
|
|
464
|
+ this.#pubKeyPromise = null;
|
|
465
|
+ if (!this.#pubKeys) {
|
|
466
|
+ // Re-throw if we have no pubKeys value for the caller.
|
|
467
|
+ throw error;
|
|
468
|
+ }
|
|
469
|
+ });
|
502
|
470
|
}
|
503
|
471
|
await this.#pubKeyPromise;
|
504
|
472
|
}
|
505
|
473
|
|
506
|
474
|
async #getEncTable() {
|
507
|
475
|
if (this.#encTablePromise === null) {
|
508
|
|
- this.#encTablePromise = this.#makeRequest("reachability", [])
|
|
476
|
+ this.#encTablePromise = this.#makeRequest("reachability", null)
|
509
|
477
|
.then(encTable => {
|
510
|
|
- this.#encTable = JSON.stringify(encTable);
|
|
478
|
+ this.#encTable = encTable;
|
511
|
479
|
this.#store();
|
512
|
480
|
})
|
513
|
481
|
.catch(error => {
|
... |
... |
@@ -526,10 +494,10 @@ class LoxImpl { |
526
|
494
|
async #getConstants() {
|
527
|
495
|
if (this.#constantsPromise === null) {
|
528
|
496
|
// Try to update first, but if that doesn't work fall back to stored data
|
529
|
|
- this.#constantsPromise = this.#makeRequest("constants", [])
|
|
497
|
+ this.#constantsPromise = this.#makeRequest("constants", null)
|
530
|
498
|
.then(constants => {
|
531
|
499
|
const prevValue = this.#constants;
|
532
|
|
- this.#constants = JSON.stringify(constants);
|
|
500
|
+ this.#constants = constants;
|
533
|
501
|
this.#store();
|
534
|
502
|
if (prevValue !== this.#constants) {
|
535
|
503
|
Services.obs.notifyObservers(null, LoxTopics.UpdateNextUnlock);
|
... |
... |
@@ -579,7 +547,6 @@ class LoxImpl { |
579
|
547
|
*/
|
580
|
548
|
async #backgroundTasks() {
|
581
|
549
|
this.#assertInitialized();
|
582
|
|
- let addedEvent = false;
|
583
|
550
|
// Only run background tasks for the active lox ID.
|
584
|
551
|
const loxId = this.#activeLoxId;
|
585
|
552
|
if (!loxId) {
|
... |
... |
@@ -591,37 +558,39 @@ class LoxImpl { |
591
|
558
|
// this should catch key rotations (ideally some days) prior to the next
|
592
|
559
|
// credential update
|
593
|
560
|
await this.#getPubKeys();
|
|
561
|
+ let levelup = false;
|
594
|
562
|
try {
|
595
|
|
- const levelup = await this.#attemptUpgrade(loxId);
|
596
|
|
- if (levelup) {
|
597
|
|
- const level = this.#getLevel(loxId);
|
598
|
|
- const newEvent = {
|
599
|
|
- type: "levelup",
|
600
|
|
- newlevel: level,
|
601
|
|
- };
|
602
|
|
- this.#events.push(newEvent);
|
603
|
|
- this.#store();
|
604
|
|
- addedEvent = true;
|
605
|
|
- }
|
606
|
|
- } catch (err) {
|
607
|
|
- lazy.logger.error(err);
|
|
563
|
+ levelup = await this.#attemptUpgrade(loxId);
|
|
564
|
+ } catch (error) {
|
|
565
|
+ lazy.logger.error(error);
|
608
|
566
|
}
|
|
567
|
+ if (levelup) {
|
|
568
|
+ const level = this.#getLevel(loxId);
|
|
569
|
+ const newEvent = {
|
|
570
|
+ type: "levelup",
|
|
571
|
+ newlevel: level,
|
|
572
|
+ };
|
|
573
|
+ this.#events.push(newEvent);
|
|
574
|
+ this.#store();
|
|
575
|
+ }
|
|
576
|
+
|
|
577
|
+ let leveldown = false;
|
609
|
578
|
try {
|
610
|
|
- const leveldown = await this.#blockageMigration(loxId);
|
611
|
|
- if (leveldown) {
|
612
|
|
- let level = this.#getLevel(loxId);
|
613
|
|
- const newEvent = {
|
614
|
|
- type: "blockage",
|
615
|
|
- newlevel: level,
|
616
|
|
- };
|
617
|
|
- this.#events.push(newEvent);
|
618
|
|
- this.#store();
|
619
|
|
- addedEvent = true;
|
620
|
|
- }
|
621
|
|
- } catch (err) {
|
622
|
|
- lazy.logger.error(err);
|
|
579
|
+ leveldown = await this.#blockageMigration(loxId);
|
|
580
|
+ } catch (error) {
|
|
581
|
+ lazy.logger.error(error);
|
|
582
|
+ }
|
|
583
|
+ if (leveldown) {
|
|
584
|
+ let level = this.#getLevel(loxId);
|
|
585
|
+ const newEvent = {
|
|
586
|
+ type: "blockage",
|
|
587
|
+ newlevel: level,
|
|
588
|
+ };
|
|
589
|
+ this.#events.push(newEvent);
|
|
590
|
+ this.#store();
|
623
|
591
|
}
|
624
|
|
- if (addedEvent) {
|
|
592
|
+
|
|
593
|
+ if (levelup || leveldown) {
|
625
|
594
|
Services.obs.notifyObservers(null, LoxTopics.UpdateEvents);
|
626
|
595
|
}
|
627
|
596
|
}
|
... |
... |
@@ -708,7 +677,7 @@ class LoxImpl { |
708
|
677
|
// to issue open invitations for Lox bridges.
|
709
|
678
|
async requestOpenInvite() {
|
710
|
679
|
this.#assertInitialized();
|
711
|
|
- let invite = await this.#makeRequest("invite", []);
|
|
680
|
+ let invite = JSON.parse(await this.#makeRequest("invite", null));
|
712
|
681
|
lazy.logger.debug(invite);
|
713
|
682
|
return invite;
|
714
|
683
|
}
|
... |
... |
@@ -725,23 +694,20 @@ class LoxImpl { |
725
|
694
|
// It's fine to get pubkey here without a delay since the user will not have a Lox
|
726
|
695
|
// credential yet
|
727
|
696
|
await this.#getPubKeys();
|
|
697
|
+ // NOTE: We currently only handle "open invites".
|
|
698
|
+ // "trusted invites" are not yet supported. tor-browser#42974.
|
728
|
699
|
let request = await lazy.open_invite(JSON.parse(invite).invite);
|
729
|
|
- let response = await this.#makeRequest(
|
730
|
|
- "openreq",
|
731
|
|
- JSON.parse(request).request
|
732
|
|
- );
|
733
|
|
- lazy.logger.debug("openreq response: ", response);
|
734
|
|
- if (response.hasOwnProperty("error")) {
|
735
|
|
- throw new LoxError(
|
736
|
|
- `Error response to "openreq": ${response.error}`,
|
737
|
|
- LoxError.BadInvite
|
738
|
|
- );
|
|
700
|
+ let response;
|
|
701
|
+ try {
|
|
702
|
+ response = await this.#makeRequest("openreq", request);
|
|
703
|
+ } catch (error) {
|
|
704
|
+ if (error instanceof LoxError && error.code === LoxError.ErrorResponse) {
|
|
705
|
+ throw new LoxError("Error response to openreq", LoxError.BadInvite);
|
|
706
|
+ } else {
|
|
707
|
+ throw error;
|
|
708
|
+ }
|
739
|
709
|
}
|
740
|
|
- let cred = lazy.handle_new_lox_credential(
|
741
|
|
- request,
|
742
|
|
- JSON.stringify(response),
|
743
|
|
- this.#pubKeys
|
744
|
|
- );
|
|
710
|
+ let cred = lazy.handle_new_lox_credential(request, response, this.#pubKeys);
|
745
|
711
|
// Generate an id that is not already in the #credentials map.
|
746
|
712
|
let loxId;
|
747
|
713
|
do {
|
... |
... |
@@ -795,31 +761,32 @@ class LoxImpl { |
795
|
761
|
throw new LoxError(`Cannot generate invites at level ${level}`);
|
796
|
762
|
}
|
797
|
763
|
let request = lazy.issue_invite(
|
798
|
|
- JSON.stringify(this.#getCredentials(loxId)),
|
|
764
|
+ this.#getCredentials(loxId),
|
799
|
765
|
this.#encTable,
|
800
|
766
|
this.#pubKeys
|
801
|
767
|
);
|
802
|
|
- let response = await this.#makeRequest(
|
803
|
|
- "issueinvite",
|
804
|
|
- JSON.parse(request).request
|
805
|
|
- );
|
806
|
|
- if (response.hasOwnProperty("error")) {
|
807
|
|
- lazy.logger.error(response.error);
|
808
|
|
- throw new LoxError(`Error response to "issueinvite": ${response.error}`);
|
809
|
|
- } else {
|
810
|
|
- const invite = lazy.prepare_invite(response);
|
811
|
|
- this.#invites.push(invite);
|
812
|
|
- // cap length of stored invites
|
813
|
|
- if (this.#invites.len > 50) {
|
814
|
|
- this.#invites.shift();
|
815
|
|
- }
|
816
|
|
- this.#store();
|
817
|
|
- this.#changeCredentials(loxId, response);
|
818
|
|
- Services.obs.notifyObservers(null, LoxTopics.NewInvite);
|
819
|
|
- // Return a copy.
|
820
|
|
- // Right now invite is just a string, but that might change in the future.
|
821
|
|
- return structuredClone(invite);
|
|
768
|
+ let response = await this.#makeRequest("issueinvite", request);
|
|
769
|
+ // TODO: Do we ever expect handle_issue_invite to fail (beyond
|
|
770
|
+ // implementation bugs)?
|
|
771
|
+ // TODO: What happens if #pubkeys for `issue_invite` differs from the value
|
|
772
|
+ // when calling `handle_issue_invite`? Should we cache the value at the
|
|
773
|
+ // start of this method?
|
|
774
|
+ let cred = lazy.handle_issue_invite(request, response, this.#pubKeys);
|
|
775
|
+
|
|
776
|
+ // Store the new credentials as a priority.
|
|
777
|
+ this.#changeCredentials(loxId, cred);
|
|
778
|
+
|
|
779
|
+ const invite = lazy.prepare_invite(cred);
|
|
780
|
+ this.#invites.push(invite);
|
|
781
|
+ // cap length of stored invites
|
|
782
|
+ if (this.#invites.len > 50) {
|
|
783
|
+ this.#invites.shift();
|
822
|
784
|
}
|
|
785
|
+ this.#store();
|
|
786
|
+ Services.obs.notifyObservers(null, LoxTopics.NewInvite);
|
|
787
|
+ // Return a copy.
|
|
788
|
+ // Right now invite is just a string, but that might change in the future.
|
|
789
|
+ return structuredClone(invite);
|
823
|
790
|
}
|
824
|
791
|
|
825
|
792
|
/**
|
... |
... |
@@ -845,15 +812,13 @@ class LoxImpl { |
845
|
812
|
return false;
|
846
|
813
|
}
|
847
|
814
|
let response = await this.#makeRequest("checkblockage", request);
|
848
|
|
- if (response.hasOwnProperty("error")) {
|
849
|
|
- lazy.logger.error(response.error);
|
850
|
|
- throw new LoxError(
|
851
|
|
- `Error response to "checkblockage": ${response.error}`
|
852
|
|
- );
|
853
|
|
- }
|
|
815
|
+ // NOTE: If a later method fails, we should be ok to re-call "checkblockage"
|
|
816
|
+ // from the Lox authority. So there shouldn't be any adverse side effects to
|
|
817
|
+ // loosing migrationCred.
|
|
818
|
+ // TODO: Confirm this is safe to lose.
|
854
|
819
|
const migrationCred = lazy.handle_check_blockage(
|
855
|
820
|
this.#getCredentials(loxId),
|
856
|
|
- JSON.stringify(response)
|
|
821
|
+ response
|
857
|
822
|
);
|
858
|
823
|
request = lazy.blockage_migration(
|
859
|
824
|
this.#getCredentials(loxId),
|
... |
... |
@@ -861,26 +826,21 @@ class LoxImpl { |
861
|
826
|
this.#pubKeys
|
862
|
827
|
);
|
863
|
828
|
response = await this.#makeRequest("blockagemigration", request);
|
864
|
|
- if (response.hasOwnProperty("error")) {
|
865
|
|
- lazy.logger.error(response.error);
|
866
|
|
- throw new LoxError(
|
867
|
|
- `Error response to "blockagemigration": ${response.error}`
|
868
|
|
- );
|
869
|
|
- }
|
870
|
829
|
const cred = lazy.handle_blockage_migration(
|
871
|
830
|
this.#getCredentials(loxId),
|
872
|
|
- JSON.stringify(response),
|
|
831
|
+ response,
|
873
|
832
|
this.#pubKeys
|
874
|
833
|
);
|
875
|
834
|
this.#changeCredentials(loxId, cred);
|
876
|
835
|
return true;
|
877
|
836
|
}
|
878
|
837
|
|
879
|
|
- /** Attempts to upgrade the currently saved Lox credential.
|
880
|
|
- * If an upgrade is available, save an event in the event list.
|
|
838
|
+ /**
|
|
839
|
+ * Attempts to upgrade the currently saved Lox credential.
|
|
840
|
+ * If an upgrade is available, save an event in the event list.
|
881
|
841
|
*
|
882
|
|
- * @param {string} loxId Lox ID
|
883
|
|
- * @returns {boolean} Whether a levelup event occurred.
|
|
842
|
+ * @param {string} loxId Lox ID
|
|
843
|
+ * @returns {boolean} Whether the credential was successfully migrated.
|
884
|
844
|
*/
|
885
|
845
|
async #attemptUpgrade(loxId) {
|
886
|
846
|
await this.#getEncTable();
|
... |
... |
@@ -895,16 +855,18 @@ class LoxImpl { |
895
|
855
|
this.#encTable,
|
896
|
856
|
this.#pubKeys
|
897
|
857
|
);
|
898
|
|
- const response = await this.#makeRequest("levelup", request);
|
899
|
|
- if (response.hasOwnProperty("error")) {
|
900
|
|
- lazy.logger.error(response.error);
|
901
|
|
- throw new LoxError(`Error response to "levelup": ${response.error}`);
|
|
858
|
+ let response;
|
|
859
|
+ try {
|
|
860
|
+ response = await this.#makeRequest("levelup", request);
|
|
861
|
+ } catch (error) {
|
|
862
|
+ if (error instanceof LoxError && error.code === LoxError.ErrorResponse) {
|
|
863
|
+ // Not an error.
|
|
864
|
+ lazy.logger.debug("Not ready for level up", error);
|
|
865
|
+ return false;
|
|
866
|
+ }
|
|
867
|
+ throw error;
|
902
|
868
|
}
|
903
|
|
- const cred = lazy.handle_level_up(
|
904
|
|
- request,
|
905
|
|
- JSON.stringify(response),
|
906
|
|
- this.#pubKeys
|
907
|
|
- );
|
|
869
|
+ const cred = lazy.handle_level_up(request, response, this.#pubKeys);
|
908
|
870
|
this.#changeCredentials(loxId, cred);
|
909
|
871
|
return true;
|
910
|
872
|
}
|
... |
... |
@@ -922,76 +884,40 @@ class LoxImpl { |
922
|
884
|
this.#getPubKeys();
|
923
|
885
|
return false;
|
924
|
886
|
}
|
925
|
|
- let request, response;
|
|
887
|
+ let request;
|
926
|
888
|
try {
|
927
|
889
|
request = lazy.trust_promotion(
|
928
|
890
|
this.#getCredentials(loxId),
|
929
|
891
|
this.#pubKeys
|
930
|
892
|
);
|
931
|
893
|
} catch (err) {
|
|
894
|
+ // This function is called routinely during the background tasks without
|
|
895
|
+ // previous checks on whether an upgrade is possible, so it is expected to
|
|
896
|
+ // fail with a certain frequency. Therefore, do not relay the error to the
|
|
897
|
+ // caller and just log the message for debugging.
|
932
|
898
|
lazy.logger.debug("Not ready to upgrade", err);
|
933
|
899
|
return false;
|
934
|
900
|
}
|
935
|
|
- try {
|
936
|
|
- response = await this.#makeRequest(
|
937
|
|
- "trustpromo",
|
938
|
|
- JSON.parse(request).request
|
939
|
|
- );
|
940
|
|
- } catch (err) {
|
941
|
|
- lazy.logger.error("Failed trust promotion", err);
|
942
|
|
- return false;
|
943
|
|
- }
|
944
|
|
- if (response.hasOwnProperty("error")) {
|
945
|
|
- lazy.logger.error("Error response from trustpromo", response.error);
|
946
|
|
- return false;
|
947
|
|
- }
|
948
|
|
- lazy.logger.debug("Got promotion cred", response, request);
|
949
|
|
- let promoCred;
|
950
|
|
- try {
|
951
|
|
- promoCred = lazy.handle_trust_promotion(
|
952
|
|
- request,
|
953
|
|
- JSON.stringify(response)
|
954
|
|
- );
|
955
|
|
- lazy.logger.debug("Formatted promotion cred");
|
956
|
|
- } catch (err) {
|
957
|
|
- lazy.logger.error(
|
958
|
|
- "Unable to handle trustpromo response properly",
|
959
|
|
- response.error
|
960
|
|
- );
|
961
|
|
- return false;
|
962
|
|
- }
|
963
|
|
- try {
|
964
|
|
- request = lazy.trust_migration(
|
965
|
|
- this.#getCredentials(loxId),
|
966
|
|
- promoCred,
|
967
|
|
- this.#pubKeys
|
968
|
|
- );
|
969
|
|
- lazy.logger.debug("Formatted migration request");
|
970
|
|
- } catch (err) {
|
971
|
|
- lazy.logger.error("Failed to generate trust migration request", err);
|
972
|
|
- return false;
|
973
|
|
- }
|
974
|
|
- try {
|
975
|
|
- response = await this.#makeRequest(
|
976
|
|
- "trustmig",
|
977
|
|
- JSON.parse(request).request
|
978
|
|
- );
|
979
|
|
- } catch (err) {
|
980
|
|
- lazy.logger.error("Failed trust migration", err);
|
981
|
|
- return false;
|
982
|
|
- }
|
983
|
|
- if (response.hasOwnProperty("error")) {
|
984
|
|
- lazy.logger.error("Error response from trustmig", response.error);
|
985
|
|
- return false;
|
986
|
|
- }
|
987
|
|
- lazy.logger.debug("Got new credential");
|
988
|
|
- let cred;
|
989
|
|
- try {
|
990
|
|
- cred = lazy.handle_trust_migration(request, response);
|
991
|
|
- } catch (err) {
|
992
|
|
- lazy.logger.error("Failed to handle response from trustmig", err);
|
993
|
|
- return false;
|
994
|
|
- }
|
|
901
|
+
|
|
902
|
+ let response = await this.#makeRequest("trustpromo", request);
|
|
903
|
+ // FIXME: Store response to "trustpromo" in case handle_trust_promotion
|
|
904
|
+ // or "trustmig" fails. The Lox authority will not accept a re-request
|
|
905
|
+ // to "trustpromo" with the same credentials.
|
|
906
|
+ let promoCred = lazy.handle_trust_promotion(request, response);
|
|
907
|
+ lazy.logger.debug("Formatted promotion cred: ", promoCred);
|
|
908
|
+
|
|
909
|
+ request = lazy.trust_migration(
|
|
910
|
+ this.#getCredentials(loxId),
|
|
911
|
+ promoCred,
|
|
912
|
+ this.#pubKeys
|
|
913
|
+ );
|
|
914
|
+ response = await this.#makeRequest("trustmig", request);
|
|
915
|
+ lazy.logger.debug("Got new credential: ", response);
|
|
916
|
+
|
|
917
|
+ // FIXME: Store response to "trustmig" in case handle_trust_migration
|
|
918
|
+ // fails. The Lox authority will not accept a re-request to "trustmig" with
|
|
919
|
+ // the same credentials.
|
|
920
|
+ let cred = lazy.handle_trust_migration(request, response);
|
995
|
921
|
this.#changeCredentials(loxId, cred);
|
996
|
922
|
return true;
|
997
|
923
|
}
|
... |
... |
@@ -1079,38 +1005,47 @@ class LoxImpl { |
1079
|
1005
|
};
|
1080
|
1006
|
}
|
1081
|
1007
|
|
1082
|
|
- async #makeRequest(procedure, args) {
|
|
1008
|
+ /**
|
|
1009
|
+ * Fetch from the Lox authority.
|
|
1010
|
+ *
|
|
1011
|
+ * @param {string} procedure - The request endpoint.
|
|
1012
|
+ * @param {string} body - The arguments to send in the body, if any.
|
|
1013
|
+ *
|
|
1014
|
+ * @returns {string} - The response body.
|
|
1015
|
+ */
|
|
1016
|
+ async #fetch(procedure, body) {
|
1083
|
1017
|
// TODO: Customize to for Lox
|
1084
|
|
- const serviceUrl = "https://lox.torproject.org";
|
1085
|
|
- const url = `${serviceUrl}/${procedure}`;
|
|
1018
|
+ const url = `https://lox.torproject.org/${procedure}`;
|
|
1019
|
+ const method = "POST";
|
|
1020
|
+ const contentType = "application/vnd.api+json";
|
1086
|
1021
|
|
1087
|
1022
|
if (lazy.TorConnect.state === lazy.TorConnectState.Bootstrapped) {
|
1088
|
1023
|
let request;
|
1089
|
1024
|
try {
|
1090
|
1025
|
request = await fetch(url, {
|
1091
|
|
- method: "POST",
|
1092
|
|
- headers: {
|
1093
|
|
- "Content-Type": "application/vnd.api+json",
|
1094
|
|
- },
|
1095
|
|
- body: JSON.stringify(args),
|
|
1026
|
+ method,
|
|
1027
|
+ headers: { "Content-Type": contentType },
|
|
1028
|
+ body,
|
1096
|
1029
|
});
|
1097
|
1030
|
} catch (error) {
|
1098
|
|
- lazy.logger.debug("fetch fail", url, args, error);
|
|
1031
|
+ lazy.logger.debug("fetch fail", url, body, error);
|
1099
|
1032
|
throw new LoxError(
|
1100
|
1033
|
`fetch "${procedure}" from Lox authority failed: ${error?.message}`,
|
1101
|
1034
|
LoxError.LoxServerUnreachable
|
1102
|
1035
|
);
|
1103
|
1036
|
}
|
1104
|
1037
|
if (!request.ok) {
|
1105
|
|
- lazy.logger.debug("fetch response", url, args, request);
|
|
1038
|
+ lazy.logger.debug("fetch response", url, body, request);
|
1106
|
1039
|
// Do not treat as a LoxServerUnreachable type.
|
1107
|
1040
|
throw new LoxError(
|
1108
|
1041
|
`Lox authority responded to "${procedure}" with ${request.status}: ${request.statusText}`
|
1109
|
1042
|
);
|
1110
|
1043
|
}
|
1111
|
|
- return request.json();
|
|
1044
|
+ return request.text();
|
1112
|
1045
|
}
|
1113
|
1046
|
|
|
1047
|
+ // TODO: Only make domain fronted requests with user permission.
|
|
1048
|
+ // tor-browser#42606.
|
1114
|
1049
|
if (this.#domainFrontedRequests === null) {
|
1115
|
1050
|
this.#domainFrontedRequests = new Promise((resolve, reject) => {
|
1116
|
1051
|
// TODO: Customize to the values for Lox
|
... |
... |
@@ -1129,9 +1064,9 @@ class LoxImpl { |
1129
|
1064
|
}
|
1130
|
1065
|
const builder = await this.#domainFrontedRequests;
|
1131
|
1066
|
try {
|
1132
|
|
- return await builder.buildPostRequest(url, args);
|
|
1067
|
+ return await builder.buildRequest(url, { method, contentType, body });
|
1133
|
1068
|
} catch (error) {
|
1134
|
|
- lazy.logger.debug("Domain front request fail", url, args, error);
|
|
1069
|
+ lazy.logger.debug("Domain front request fail", url, body, error);
|
1135
|
1070
|
if (error instanceof lazy.DomainFrontRequestNetworkError) {
|
1136
|
1071
|
throw new LoxError(
|
1137
|
1072
|
`Domain front fetch "${procedure}" from Lox authority failed: ${error?.message}`,
|
... |
... |
@@ -1149,6 +1084,37 @@ class LoxImpl { |
1149
|
1084
|
);
|
1150
|
1085
|
}
|
1151
|
1086
|
}
|
|
1087
|
+
|
|
1088
|
+ /**
|
|
1089
|
+ * Make a request to the lox authority, check for an error response, and
|
|
1090
|
+ * convert it to a string.
|
|
1091
|
+ *
|
|
1092
|
+ * @param {string} procedure - The request endpoint.
|
|
1093
|
+ * @param {?string} request - The request data, as a JSON string containing a
|
|
1094
|
+ * "request" field. Or `null` to send no data.
|
|
1095
|
+ *
|
|
1096
|
+ * @returns {string} - The stringified JSON response.
|
|
1097
|
+ */
|
|
1098
|
+ async #makeRequest(procedure, request) {
|
|
1099
|
+ // Verify that the response is valid json, by parsing.
|
|
1100
|
+ const jsonResponse = JSON.parse(
|
|
1101
|
+ await this.#fetch(
|
|
1102
|
+ procedure,
|
|
1103
|
+ request ? JSON.stringify(JSON.parse(request).request) : ""
|
|
1104
|
+ )
|
|
1105
|
+ );
|
|
1106
|
+ lazy.logger.debug(`${procedure} response:`, jsonResponse);
|
|
1107
|
+ if (Object.hasOwn(jsonResponse, "error")) {
|
|
1108
|
+ // TODO: Figure out if any of the "error" responses should be treated as
|
|
1109
|
+ // an error. I.e. which of the procedures have soft failures and hard
|
|
1110
|
+ // failures.
|
|
1111
|
+ throw LoxError(
|
|
1112
|
+ `Error response to ${procedure}: ${jsonResponse.error}`,
|
|
1113
|
+ LoxError.ErrorResponse
|
|
1114
|
+ );
|
|
1115
|
+ }
|
|
1116
|
+ return JSON.stringify(jsonResponse);
|
|
1117
|
+ }
|
1152
|
1118
|
}
|
1153
|
1119
|
|
1154
|
1120
|
export const Lox = new LoxImpl(); |