matrix-react-sdk
Version:
SDK for matrix.org using React
763 lines (758 loc) • 130 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _react = _interopRequireWildcard(require("react"));
var _fileSaver = _interopRequireDefault(require("file-saver"));
var _logger = require("matrix-js-sdk/src/logger");
var _matrix = require("matrix-js-sdk/src/matrix");
var _crypto = require("matrix-js-sdk/src/crypto");
var _classnames = _interopRequireDefault(require("classnames"));
var _check = _interopRequireDefault(require("@vector-im/compound-design-tokens/assets/web/icons/check"));
var _MatrixClientPeg = require("../../../../MatrixClientPeg");
var _languageHandler = require("../../../../languageHandler");
var _Modal = _interopRequireDefault(require("../../../../Modal"));
var _strings = require("../../../../utils/strings");
var _InteractiveAuthEntryComponents = require("../../../../components/views/auth/InteractiveAuthEntryComponents");
var _PassphraseField = _interopRequireDefault(require("../../../../components/views/auth/PassphraseField"));
var _StyledRadioButton = _interopRequireDefault(require("../../../../components/views/elements/StyledRadioButton"));
var _AccessibleButton = _interopRequireDefault(require("../../../../components/views/elements/AccessibleButton"));
var _DialogButtons = _interopRequireDefault(require("../../../../components/views/elements/DialogButtons"));
var _InlineSpinner = _interopRequireDefault(require("../../../../components/views/elements/InlineSpinner"));
var _RestoreKeyBackupDialog = _interopRequireDefault(require("../../../../components/views/dialogs/security/RestoreKeyBackupDialog"));
var _WellKnownUtils = require("../../../../utils/WellKnownUtils");
var _ModuleRunner = require("../../../../modules/ModuleRunner");
var _Field = _interopRequireDefault(require("../../../../components/views/elements/Field"));
var _BaseDialog = _interopRequireDefault(require("../../../../components/views/dialogs/BaseDialog"));
var _Spinner = _interopRequireDefault(require("../../../../components/views/elements/Spinner"));
var _InteractiveAuthDialog = _interopRequireDefault(require("../../../../components/views/dialogs/InteractiveAuthDialog"));
var _PassphraseConfirmField = _interopRequireDefault(require("../../../../components/views/auth/PassphraseConfirmField"));
var _dehydration = require("../../../../utils/device/dehydration");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
/*
Copyright 2024 New Vector Ltd.
Copyright 2019, 2020 , 2023 The Matrix.org Foundation C.I.C.
Copyright 2018, 2019 New Vector Ltd
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
// I made a mistake while converting this and it has to be fixed!
var Phase = /*#__PURE__*/function (Phase) {
Phase["Loading"] = "loading";
Phase["LoadError"] = "load_error";
Phase["ChooseKeyPassphrase"] = "choose_key_passphrase";
Phase["Migrate"] = "migrate";
Phase["Passphrase"] = "passphrase";
Phase["PassphraseConfirm"] = "passphrase_confirm";
Phase["ShowKey"] = "show_key";
Phase["Storing"] = "storing";
Phase["Stored"] = "stored";
Phase["ConfirmSkip"] = "confirm_skip";
return Phase;
}(Phase || {});
const PASSWORD_MIN_SCORE = 4; // So secure, many characters, much complex, wow, etc, etc.
/**
* Walks the user through the process of creating a 4S passphrase and bootstrapping secret storage.
*
* If the user already has a key backup, follows a "migration" flow (aka "Upgrade your encryption") which
* prompts the user to enter their backup decryption password (a Curve25519 private key, possibly derived
* from a passphrase), and uses that as the (AES) 4S encryption key.
*/
class CreateSecretStorageDialog extends _react.default.PureComponent {
constructor(props) {
super(props);
(0, _defineProperty2.default)(this, "recoveryKey", void 0);
(0, _defineProperty2.default)(this, "recoveryKeyNode", /*#__PURE__*/(0, _react.createRef)());
(0, _defineProperty2.default)(this, "passphraseField", /*#__PURE__*/(0, _react.createRef)());
(0, _defineProperty2.default)(this, "onKeyBackupStatusChange", () => {
if (this.state.phase === Phase.Migrate) this.fetchBackupInfo();
});
(0, _defineProperty2.default)(this, "onKeyPassphraseChange", e => {
this.setState({
passPhraseKeySelected: e.target.value
});
});
(0, _defineProperty2.default)(this, "onChooseKeyPassphraseFormSubmit", async () => {
if (this.state.passPhraseKeySelected === _WellKnownUtils.SecureBackupSetupMethod.Key) {
this.recoveryKey = await _MatrixClientPeg.MatrixClientPeg.safeGet().getCrypto().createRecoveryKeyFromPassphrase();
this.setState({
copied: false,
downloaded: false,
setPassphrase: false,
phase: Phase.ShowKey
});
} else {
this.setState({
copied: false,
downloaded: false,
phase: Phase.Passphrase
});
}
});
(0, _defineProperty2.default)(this, "onMigrateFormSubmit", e => {
e.preventDefault();
if (this.state.backupTrustInfo?.trusted) {
this.bootstrapSecretStorage();
} else {
this.restoreBackup();
}
});
(0, _defineProperty2.default)(this, "onCopyClick", () => {
const successful = (0, _strings.copyNode)(this.recoveryKeyNode.current);
if (successful) {
this.setState({
copied: true
});
}
});
(0, _defineProperty2.default)(this, "onDownloadClick", () => {
if (!this.recoveryKey) return;
const blob = new Blob([this.recoveryKey.encodedPrivateKey], {
type: "text/plain;charset=us-ascii"
});
_fileSaver.default.saveAs(blob, "security-key.txt");
this.setState({
downloaded: true
});
});
(0, _defineProperty2.default)(this, "doBootstrapUIAuth", async makeRequest => {
if (this.state.canUploadKeysWithPasswordOnly && this.state.accountPassword) {
await makeRequest({
type: "m.login.password",
identifier: {
type: "m.id.user",
user: _MatrixClientPeg.MatrixClientPeg.safeGet().getSafeUserId()
},
password: this.state.accountPassword
});
} else {
const dialogAesthetics = {
[_InteractiveAuthEntryComponents.SSOAuthEntry.PHASE_PREAUTH]: {
title: (0, _languageHandler._t)("auth|uia|sso_title"),
body: (0, _languageHandler._t)("auth|uia|sso_preauth_body"),
continueText: (0, _languageHandler._t)("auth|sso"),
continueKind: "primary"
},
[_InteractiveAuthEntryComponents.SSOAuthEntry.PHASE_POSTAUTH]: {
title: (0, _languageHandler._t)("encryption|confirm_encryption_setup_title"),
body: (0, _languageHandler._t)("encryption|confirm_encryption_setup_body"),
continueText: (0, _languageHandler._t)("action|confirm"),
continueKind: "primary"
}
};
const {
finished
} = _Modal.default.createDialog(_InteractiveAuthDialog.default, {
title: (0, _languageHandler._t)("encryption|bootstrap_title"),
matrixClient: _MatrixClientPeg.MatrixClientPeg.safeGet(),
makeRequest,
aestheticsForStagePhases: {
[_InteractiveAuthEntryComponents.SSOAuthEntry.LOGIN_TYPE]: dialogAesthetics,
[_InteractiveAuthEntryComponents.SSOAuthEntry.UNSTABLE_LOGIN_TYPE]: dialogAesthetics
}
});
const [confirmed] = await finished;
if (!confirmed) {
throw new Error("Cross-signing key upload auth canceled");
}
}
});
(0, _defineProperty2.default)(this, "bootstrapSecretStorage", async () => {
this.setState({
phase: Phase.Storing,
error: undefined
});
const cli = _MatrixClientPeg.MatrixClientPeg.safeGet();
const crypto = cli.getCrypto();
const {
forceReset
} = this.props;
try {
if (forceReset) {
_logger.logger.log("Forcing secret storage reset");
await crypto.bootstrapSecretStorage({
createSecretStorageKey: async () => this.recoveryKey,
setupNewKeyBackup: true,
setupNewSecretStorage: true
});
} else {
// For password authentication users after 2020-09, this cross-signing
// step will be a no-op since it is now setup during registration or login
// when needed. We should keep this here to cover other cases such as:
// * Users with existing sessions prior to 2020-09 changes
// * SSO authentication users which require interactive auth to upload
// keys (and also happen to skip all post-authentication flows at the
// moment via token login)
await crypto.bootstrapCrossSigning({
authUploadDeviceSigningKeys: this.doBootstrapUIAuth
});
await crypto.bootstrapSecretStorage({
createSecretStorageKey: async () => this.recoveryKey,
keyBackupInfo: this.state.backupInfo,
setupNewKeyBackup: !this.state.backupInfo
});
}
await (0, _dehydration.initialiseDehydration)(true);
this.setState({
phase: Phase.Stored
});
} catch (e) {
if (this.state.canUploadKeysWithPasswordOnly && e instanceof _matrix.MatrixError && e.httpStatus === 401 && e.data.flows) {
this.setState({
accountPassword: "",
accountPasswordCorrect: false,
phase: Phase.Migrate
});
} else {
this.setState({
error: true
});
}
_logger.logger.error("Error bootstrapping secret storage", e);
}
});
(0, _defineProperty2.default)(this, "onCancel", () => {
this.props.onFinished(false);
});
(0, _defineProperty2.default)(this, "restoreBackup", async () => {
const keyCallback = k => {};
const {
finished
} = _Modal.default.createDialog(_RestoreKeyBackupDialog.default, {
showSummary: false,
keyCallback
}, undefined, /* priority = */false, /* static = */false);
await finished;
const backupTrustInfo = await this.fetchBackupInfo();
if (backupTrustInfo?.trusted && this.state.canUploadKeysWithPasswordOnly && this.state.accountPassword) {
this.bootstrapSecretStorage();
}
});
(0, _defineProperty2.default)(this, "onLoadRetryClick", () => {
this.setState({
phase: Phase.Loading
});
this.fetchBackupInfo();
});
(0, _defineProperty2.default)(this, "onShowKeyContinueClick", () => {
this.bootstrapSecretStorage();
});
(0, _defineProperty2.default)(this, "onCancelClick", () => {
this.setState({
phase: Phase.ConfirmSkip
});
});
(0, _defineProperty2.default)(this, "onGoBackClick", () => {
this.setState({
phase: Phase.ChooseKeyPassphrase
});
});
(0, _defineProperty2.default)(this, "onPassPhraseNextClick", async e => {
e.preventDefault();
if (!this.passphraseField.current) return; // unmounting
await this.passphraseField.current.validate({
allowEmpty: false
});
if (!this.passphraseField.current.state.valid) {
this.passphraseField.current.focus();
this.passphraseField.current.validate({
allowEmpty: false,
focused: true
});
return;
}
this.setState({
phase: Phase.PassphraseConfirm
});
});
(0, _defineProperty2.default)(this, "onPassPhraseConfirmNextClick", async e => {
e.preventDefault();
if (this.state.passPhrase !== this.state.passPhraseConfirm) return;
this.recoveryKey = await _MatrixClientPeg.MatrixClientPeg.safeGet().getCrypto().createRecoveryKeyFromPassphrase(this.state.passPhrase);
this.setState({
copied: false,
downloaded: false,
setPassphrase: true,
phase: Phase.ShowKey
});
});
(0, _defineProperty2.default)(this, "onSetAgainClick", () => {
this.setState({
passPhrase: "",
passPhraseValid: false,
passPhraseConfirm: "",
phase: Phase.Passphrase
});
});
(0, _defineProperty2.default)(this, "onPassPhraseValidate", result => {
this.setState({
passPhraseValid: !!result.valid
});
});
(0, _defineProperty2.default)(this, "onPassPhraseChange", e => {
this.setState({
passPhrase: e.target.value
});
});
(0, _defineProperty2.default)(this, "onPassPhraseConfirmChange", e => {
this.setState({
passPhraseConfirm: e.target.value
});
});
(0, _defineProperty2.default)(this, "onAccountPasswordChange", e => {
this.setState({
accountPassword: e.target.value
});
});
const _cli = _MatrixClientPeg.MatrixClientPeg.safeGet();
let passPhraseKeySelected;
const setupMethods = (0, _WellKnownUtils.getSecureBackupSetupMethods)(_cli);
if (setupMethods.includes(_WellKnownUtils.SecureBackupSetupMethod.Key)) {
passPhraseKeySelected = _WellKnownUtils.SecureBackupSetupMethod.Key;
} else {
passPhraseKeySelected = _WellKnownUtils.SecureBackupSetupMethod.Passphrase;
}
const accountPassword = props.accountPassword || "";
let canUploadKeysWithPasswordOnly = null;
if (accountPassword) {
// If we have an account password in memory, let's simplify and
// assume it means password auth is also supported for device
// signing key upload as well. This avoids hitting the server to
// test auth flows, which may be slow under high load.
canUploadKeysWithPasswordOnly = true;
} else {
this.queryKeyUploadAuth();
}
this.state = {
phase: Phase.Loading,
passPhrase: "",
passPhraseValid: false,
passPhraseConfirm: "",
copied: false,
downloaded: false,
setPassphrase: false,
backupInfo: null,
backupTrustInfo: undefined,
// does the server offer a UI auth flow with just m.login.password
// for /keys/device_signing/upload?
accountPasswordCorrect: null,
canSkip: !(0, _WellKnownUtils.isSecureBackupRequired)(_cli),
canUploadKeysWithPasswordOnly,
passPhraseKeySelected,
accountPassword
};
_cli.on(_crypto.CryptoEvent.KeyBackupStatus, this.onKeyBackupStatusChange);
this.getInitialPhase();
}
componentWillUnmount() {
_MatrixClientPeg.MatrixClientPeg.get()?.removeListener(_crypto.CryptoEvent.KeyBackupStatus, this.onKeyBackupStatusChange);
}
getInitialPhase() {
const keyFromCustomisations = _ModuleRunner.ModuleRunner.instance.extensions.cryptoSetup.createSecretStorageKey();
if (keyFromCustomisations) {
_logger.logger.log("CryptoSetupExtension: Created key via extension, jumping to bootstrap step");
this.recoveryKey = {
privateKey: keyFromCustomisations
};
this.bootstrapSecretStorage();
return;
}
this.fetchBackupInfo();
}
/**
* Attempt to get information on the current backup from the server, and update the state.
*
* Updates {@link IState.backupInfo} and {@link IState.backupTrustInfo}, and picks an appropriate phase for
* {@link IState.phase}.
*
* @returns If the backup data was retrieved successfully, the trust info for the backup. Otherwise, undefined.
*/
async fetchBackupInfo() {
try {
const cli = _MatrixClientPeg.MatrixClientPeg.safeGet();
const backupInfo = await cli.getKeyBackupVersion();
const backupTrustInfo =
// we may not have started crypto yet, in which case we definitely don't trust the backup
backupInfo ? await cli.getCrypto()?.isKeyBackupTrusted(backupInfo) : undefined;
const {
forceReset
} = this.props;
const phase = backupInfo && !forceReset ? Phase.Migrate : Phase.ChooseKeyPassphrase;
this.setState({
phase,
backupInfo,
backupTrustInfo
});
return backupTrustInfo;
} catch (e) {
console.error("Error fetching backup data from server", e);
this.setState({
phase: Phase.LoadError
});
return undefined;
}
}
async queryKeyUploadAuth() {
try {
await _MatrixClientPeg.MatrixClientPeg.safeGet().uploadDeviceSigningKeys(undefined, {});
// We should never get here: the server should always require
// UI auth to upload device signing keys. If we do, we upload
// no keys which would be a no-op.
_logger.logger.log("uploadDeviceSigningKeys unexpectedly succeeded without UI auth!");
} catch (error) {
if (!(error instanceof _matrix.MatrixError) || !error.data || !error.data.flows) {
_logger.logger.log("uploadDeviceSigningKeys advertised no flows!");
return;
}
const canUploadKeysWithPasswordOnly = error.data.flows.some(f => {
return f.stages.length === 1 && f.stages[0] === "m.login.password";
});
this.setState({
canUploadKeysWithPasswordOnly
});
}
}
renderOptionKey() {
return /*#__PURE__*/_react.default.createElement(_StyledRadioButton.default, {
key: _WellKnownUtils.SecureBackupSetupMethod.Key,
value: _WellKnownUtils.SecureBackupSetupMethod.Key,
name: "keyPassphrase",
checked: this.state.passPhraseKeySelected === _WellKnownUtils.SecureBackupSetupMethod.Key,
onChange: this.onKeyPassphraseChange,
outlined: true
}, /*#__PURE__*/_react.default.createElement("div", {
className: "mx_CreateSecretStorageDialog_optionTitle"
}, /*#__PURE__*/_react.default.createElement("span", {
className: "mx_CreateSecretStorageDialog_optionIcon mx_CreateSecretStorageDialog_optionIcon_secureBackup"
}), (0, _languageHandler._t)("settings|key_backup|setup_secure_backup|generate_security_key_title")), /*#__PURE__*/_react.default.createElement("div", null, (0, _languageHandler._t)("settings|key_backup|setup_secure_backup|generate_security_key_description")));
}
renderOptionPassphrase() {
return /*#__PURE__*/_react.default.createElement(_StyledRadioButton.default, {
key: _WellKnownUtils.SecureBackupSetupMethod.Passphrase,
value: _WellKnownUtils.SecureBackupSetupMethod.Passphrase,
name: "keyPassphrase",
checked: this.state.passPhraseKeySelected === _WellKnownUtils.SecureBackupSetupMethod.Passphrase,
onChange: this.onKeyPassphraseChange,
outlined: true
}, /*#__PURE__*/_react.default.createElement("div", {
className: "mx_CreateSecretStorageDialog_optionTitle"
}, /*#__PURE__*/_react.default.createElement("span", {
className: "mx_CreateSecretStorageDialog_optionIcon mx_CreateSecretStorageDialog_optionIcon_securePhrase"
}), (0, _languageHandler._t)("settings|key_backup|setup_secure_backup|enter_phrase_title")), /*#__PURE__*/_react.default.createElement("div", null, (0, _languageHandler._t)("settings|key_backup|setup_secure_backup|use_phrase_only_you_know")));
}
renderPhaseChooseKeyPassphrase() {
const setupMethods = (0, _WellKnownUtils.getSecureBackupSetupMethods)(_MatrixClientPeg.MatrixClientPeg.safeGet());
const optionKey = setupMethods.includes(_WellKnownUtils.SecureBackupSetupMethod.Key) ? this.renderOptionKey() : null;
const optionPassphrase = setupMethods.includes(_WellKnownUtils.SecureBackupSetupMethod.Passphrase) ? this.renderOptionPassphrase() : null;
return /*#__PURE__*/_react.default.createElement("form", {
onSubmit: this.onChooseKeyPassphraseFormSubmit
}, /*#__PURE__*/_react.default.createElement("p", {
className: "mx_CreateSecretStorageDialog_centeredBody"
}, (0, _languageHandler._t)("settings|key_backup|setup_secure_backup|description")), /*#__PURE__*/_react.default.createElement("div", {
className: "mx_CreateSecretStorageDialog_primaryContainer",
role: "radiogroup"
}, optionKey, optionPassphrase), /*#__PURE__*/_react.default.createElement(_DialogButtons.default, {
primaryButton: (0, _languageHandler._t)("action|continue"),
onPrimaryButtonClick: this.onChooseKeyPassphraseFormSubmit,
onCancel: this.onCancelClick,
hasCancel: this.state.canSkip
}));
}
renderPhaseMigrate() {
let authPrompt;
let nextCaption = (0, _languageHandler._t)("action|next");
if (this.state.canUploadKeysWithPasswordOnly) {
authPrompt = /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("div", null, (0, _languageHandler._t)("settings|key_backup|setup_secure_backup|requires_password_confirmation")), /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement(_Field.default, {
id: "mx_CreateSecretStorageDialog_password",
type: "password",
label: (0, _languageHandler._t)("common|password"),
value: this.state.accountPassword,
onChange: this.onAccountPasswordChange,
forceValidity: this.state.accountPasswordCorrect === false ? false : undefined,
autoFocus: true
})));
} else if (!this.state.backupTrustInfo?.trusted) {
authPrompt = /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("div", null, (0, _languageHandler._t)("settings|key_backup|setup_secure_backup|requires_key_restore")));
nextCaption = (0, _languageHandler._t)("action|restore");
} else {
authPrompt = /*#__PURE__*/_react.default.createElement("p", null, (0, _languageHandler._t)("settings|key_backup|setup_secure_backup|requires_server_authentication"));
}
return /*#__PURE__*/_react.default.createElement("form", {
onSubmit: this.onMigrateFormSubmit
}, /*#__PURE__*/_react.default.createElement("p", null, (0, _languageHandler._t)("settings|key_backup|setup_secure_backup|session_upgrade_description")), /*#__PURE__*/_react.default.createElement("div", null, authPrompt), /*#__PURE__*/_react.default.createElement(_DialogButtons.default, {
primaryButton: nextCaption,
onPrimaryButtonClick: this.onMigrateFormSubmit,
hasCancel: false,
primaryDisabled: !!this.state.canUploadKeysWithPasswordOnly && !this.state.accountPassword
}, /*#__PURE__*/_react.default.createElement("button", {
type: "button",
className: "danger",
onClick: this.onCancelClick
}, (0, _languageHandler._t)("action|skip"))));
}
renderPhasePassPhrase() {
return /*#__PURE__*/_react.default.createElement("form", {
onSubmit: this.onPassPhraseNextClick
}, /*#__PURE__*/_react.default.createElement("p", null, (0, _languageHandler._t)("settings|key_backup|setup_secure_backup|enter_phrase_description")), /*#__PURE__*/_react.default.createElement("div", {
className: "mx_CreateSecretStorageDialog_passPhraseContainer"
}, /*#__PURE__*/_react.default.createElement(_PassphraseField.default, {
id: "mx_passPhraseInput",
className: "mx_CreateSecretStorageDialog_passPhraseField",
onChange: this.onPassPhraseChange,
minScore: PASSWORD_MIN_SCORE,
value: this.state.passPhrase,
onValidate: this.onPassPhraseValidate,
fieldRef: this.passphraseField,
autoFocus: true,
label: (0, _languageHandler._td)("settings|key_backup|setup_secure_backup|enter_phrase_title"),
labelEnterPassword: (0, _languageHandler._td)("settings|key_backup|setup_secure_backup|enter_phrase_title"),
labelStrongPassword: (0, _languageHandler._td)("settings|key_backup|setup_secure_backup|phrase_strong_enough"),
labelAllowedButUnsafe: (0, _languageHandler._td)("settings|key_backup|setup_secure_backup|phrase_strong_enough")
})), /*#__PURE__*/_react.default.createElement(_DialogButtons.default, {
primaryButton: (0, _languageHandler._t)("action|continue"),
onPrimaryButtonClick: this.onPassPhraseNextClick,
hasCancel: false,
disabled: !this.state.passPhraseValid
}, /*#__PURE__*/_react.default.createElement("button", {
type: "button",
onClick: this.onCancelClick,
className: "danger"
}, (0, _languageHandler._t)("action|cancel"))));
}
renderPhasePassPhraseConfirm() {
let matchText;
let changeText;
if (this.state.passPhraseConfirm === this.state.passPhrase) {
matchText = (0, _languageHandler._t)("settings|key_backup|setup_secure_backup|pass_phrase_match_success");
changeText = (0, _languageHandler._t)("settings|key_backup|setup_secure_backup|use_different_passphrase");
} else if (!this.state.passPhrase.startsWith(this.state.passPhraseConfirm)) {
// only tell them they're wrong if they've actually gone wrong.
// Security conscious readers will note that if you left element-web unattended
// on this screen, this would make it easy for a malicious person to guess
// your passphrase one letter at a time, but they could get this faster by
// just opening the browser's developer tools and reading it.
// Note that not having typed anything at all will not hit this clause and
// fall through so empty box === no hint.
matchText = (0, _languageHandler._t)("settings|key_backup|setup_secure_backup|pass_phrase_match_failed");
changeText = (0, _languageHandler._t)("settings|key_backup|setup_secure_backup|set_phrase_again");
}
let passPhraseMatch;
if (matchText) {
passPhraseMatch = /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("div", null, matchText), /*#__PURE__*/_react.default.createElement(_AccessibleButton.default, {
kind: "link",
onClick: this.onSetAgainClick
}, changeText));
}
return /*#__PURE__*/_react.default.createElement("form", {
onSubmit: this.onPassPhraseConfirmNextClick
}, /*#__PURE__*/_react.default.createElement("p", null, (0, _languageHandler._t)("settings|key_backup|setup_secure_backup|enter_phrase_to_confirm")), /*#__PURE__*/_react.default.createElement("div", {
className: "mx_CreateSecretStorageDialog_passPhraseContainer"
}, /*#__PURE__*/_react.default.createElement(_PassphraseConfirmField.default, {
id: "mx_passPhraseInput",
onChange: this.onPassPhraseConfirmChange,
value: this.state.passPhraseConfirm,
className: "mx_CreateSecretStorageDialog_passPhraseField",
label: (0, _languageHandler._td)("settings|key_backup|setup_secure_backup|confirm_security_phrase"),
labelRequired: (0, _languageHandler._td)("settings|key_backup|setup_secure_backup|confirm_security_phrase"),
labelInvalid: (0, _languageHandler._td)("settings|key_backup|setup_secure_backup|pass_phrase_match_failed"),
autoFocus: true,
password: this.state.passPhrase
}), /*#__PURE__*/_react.default.createElement("div", {
className: "mx_CreateSecretStorageDialog_passPhraseMatch"
}, passPhraseMatch)), /*#__PURE__*/_react.default.createElement(_DialogButtons.default, {
primaryButton: (0, _languageHandler._t)("action|continue"),
onPrimaryButtonClick: this.onPassPhraseConfirmNextClick,
hasCancel: false,
disabled: this.state.passPhrase !== this.state.passPhraseConfirm
}, /*#__PURE__*/_react.default.createElement("button", {
type: "button",
onClick: this.onCancelClick,
className: "danger"
}, (0, _languageHandler._t)("action|skip"))));
}
renderPhaseShowKey() {
let continueButton;
if (this.state.phase === Phase.ShowKey) {
continueButton = /*#__PURE__*/_react.default.createElement(_DialogButtons.default, {
primaryButton: (0, _languageHandler._t)("action|continue"),
disabled: !this.state.downloaded && !this.state.copied && !this.state.setPassphrase,
onPrimaryButtonClick: this.onShowKeyContinueClick,
hasCancel: false
});
} else {
continueButton = /*#__PURE__*/_react.default.createElement("div", {
className: "mx_CreateSecretStorageDialog_continueSpinner"
}, /*#__PURE__*/_react.default.createElement(_InlineSpinner.default, null));
}
return /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("p", null, (0, _languageHandler._t)("settings|key_backup|setup_secure_backup|security_key_safety_reminder")), /*#__PURE__*/_react.default.createElement("div", {
className: "mx_CreateSecretStorageDialog_primaryContainer mx_CreateSecretStorageDialog_recoveryKeyPrimarycontainer"
}, /*#__PURE__*/_react.default.createElement("div", {
className: "mx_CreateSecretStorageDialog_recoveryKeyContainer"
}, /*#__PURE__*/_react.default.createElement("div", {
className: "mx_CreateSecretStorageDialog_recoveryKey"
}, /*#__PURE__*/_react.default.createElement("code", {
ref: this.recoveryKeyNode
}, this.recoveryKey?.encodedPrivateKey)), /*#__PURE__*/_react.default.createElement("div", {
className: "mx_CreateSecretStorageDialog_recoveryKeyButtons"
}, /*#__PURE__*/_react.default.createElement(_AccessibleButton.default, {
kind: "primary",
className: "mx_Dialog_primary",
onClick: this.onDownloadClick,
disabled: this.state.phase === Phase.Storing
}, (0, _languageHandler._t)("action|download")), /*#__PURE__*/_react.default.createElement("span", null, (0, _languageHandler._t)("settings|key_backup|setup_secure_backup|download_or_copy", {
downloadButton: "",
copyButton: ""
})), /*#__PURE__*/_react.default.createElement(_AccessibleButton.default, {
kind: "primary",
className: "mx_Dialog_primary mx_CreateSecretStorageDialog_recoveryKeyButtons_copyBtn",
onClick: this.onCopyClick,
disabled: this.state.phase === Phase.Storing
}, this.state.copied ? (0, _languageHandler._t)("common|copied") : (0, _languageHandler._t)("action|copy"))))), continueButton);
}
renderBusyPhase() {
return /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement(_Spinner.default, null));
}
renderStoredPhase() {
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("p", {
className: "mx_Dialog_content"
}, (0, _languageHandler._t)("settings|key_backup|setup_secure_backup|backup_setup_success_description")), /*#__PURE__*/_react.default.createElement(_DialogButtons.default, {
primaryButton: (0, _languageHandler._t)("action|done"),
onPrimaryButtonClick: () => this.props.onFinished(true),
hasCancel: false
}));
}
renderPhaseLoadError() {
return /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("p", null, (0, _languageHandler._t)("settings|key_backup|setup_secure_backup|secret_storage_query_failure")), /*#__PURE__*/_react.default.createElement("div", {
className: "mx_Dialog_buttons"
}, /*#__PURE__*/_react.default.createElement(_DialogButtons.default, {
primaryButton: (0, _languageHandler._t)("action|retry"),
onPrimaryButtonClick: this.onLoadRetryClick,
hasCancel: this.state.canSkip,
onCancel: this.onCancel
})));
}
renderPhaseSkipConfirm() {
return /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("p", null, (0, _languageHandler._t)("settings|key_backup|setup_secure_backup|cancel_warning")), /*#__PURE__*/_react.default.createElement("p", null, (0, _languageHandler._t)("settings|key_backup|setup_secure_backup|settings_reminder")), /*#__PURE__*/_react.default.createElement(_DialogButtons.default, {
primaryButton: (0, _languageHandler._t)("action|go_back"),
onPrimaryButtonClick: this.onGoBackClick,
hasCancel: false
}, /*#__PURE__*/_react.default.createElement("button", {
type: "button",
className: "danger",
onClick: this.onCancel
}, (0, _languageHandler._t)("action|cancel"))));
}
titleForPhase(phase) {
switch (phase) {
case Phase.ChooseKeyPassphrase:
return (0, _languageHandler._t)("encryption|set_up_toast_title");
case Phase.Migrate:
return (0, _languageHandler._t)("settings|key_backup|setup_secure_backup|title_upgrade_encryption");
case Phase.Passphrase:
return (0, _languageHandler._t)("settings|key_backup|setup_secure_backup|title_set_phrase");
case Phase.PassphraseConfirm:
return (0, _languageHandler._t)("settings|key_backup|setup_secure_backup|title_confirm_phrase");
case Phase.ConfirmSkip:
return (0, _languageHandler._t)("common|are_you_sure");
case Phase.ShowKey:
return (0, _languageHandler._t)("settings|key_backup|setup_secure_backup|title_save_key");
case Phase.Storing:
return (0, _languageHandler._t)("encryption|bootstrap_title");
case Phase.Stored:
return (0, _languageHandler._t)("settings|key_backup|setup_secure_backup|backup_setup_success_title");
default:
return "";
}
}
get topComponent() {
if (this.state.phase === Phase.Stored) {
return /*#__PURE__*/_react.default.createElement(_check.default, {
className: "mx_Icon mx_Icon_circle-40 mx_Icon_accent mx_Icon_bg-accent-light"
});
}
return null;
}
get classNames() {
return (0, _classnames.default)("mx_CreateSecretStorageDialog", {
mx_SuccessDialog: this.state.phase === Phase.Stored
});
}
render() {
let content;
if (this.state.error) {
content = /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("p", null, (0, _languageHandler._t)("settings|key_backup|setup_secure_backup|unable_to_setup")), /*#__PURE__*/_react.default.createElement("div", {
className: "mx_Dialog_buttons"
}, /*#__PURE__*/_react.default.createElement(_DialogButtons.default, {
primaryButton: (0, _languageHandler._t)("action|retry"),
onPrimaryButtonClick: this.bootstrapSecretStorage,
hasCancel: this.state.canSkip,
onCancel: this.onCancel
})));
} else {
switch (this.state.phase) {
case Phase.Loading:
content = this.renderBusyPhase();
break;
case Phase.LoadError:
content = this.renderPhaseLoadError();
break;
case Phase.ChooseKeyPassphrase:
content = this.renderPhaseChooseKeyPassphrase();
break;
case Phase.Migrate:
content = this.renderPhaseMigrate();
break;
case Phase.Passphrase:
content = this.renderPhasePassPhrase();
break;
case Phase.PassphraseConfirm:
content = this.renderPhasePassPhraseConfirm();
break;
case Phase.ShowKey:
content = this.renderPhaseShowKey();
break;
case Phase.Storing:
content = this.renderBusyPhase();
break;
case Phase.Stored:
content = this.renderStoredPhase();
break;
case Phase.ConfirmSkip:
content = this.renderPhaseSkipConfirm();
break;
}
}
let titleClass;
switch (this.state.phase) {
case Phase.Passphrase:
case Phase.PassphraseConfirm:
titleClass = ["mx_CreateSecretStorageDialog_titleWithIcon", "mx_CreateSecretStorageDialog_securePhraseTitle"];
break;
case Phase.ShowKey:
titleClass = ["mx_CreateSecretStorageDialog_titleWithIcon", "mx_CreateSecretStorageDialog_secureBackupTitle"];
break;
case Phase.ChooseKeyPassphrase:
titleClass = "mx_CreateSecretStorageDialog_centeredTitle";
break;
}
return /*#__PURE__*/_react.default.createElement(_BaseDialog.default, {
className: this.classNames,
onFinished: this.props.onFinished,
top: this.topComponent,
title: this.titleForPhase(this.state.phase),
titleClass: titleClass,
hasCancel: this.props.hasCancel && [Phase.Passphrase].includes(this.state.phase),
fixedWidth: false
}, /*#__PURE__*/_react.default.createElement("div", null, content));
}
}
exports.default = CreateSecretStorageDialog;
(0, _defineProperty2.default)(CreateSecretStorageDialog, "defaultProps", {
hasCancel: true,
forceReset: false
});
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfcmVhY3QiLCJfaW50ZXJvcFJlcXVpcmVXaWxkY2FyZCIsInJlcXVpcmUiLCJfZmlsZVNhdmVyIiwiX2ludGVyb3BSZXF1aXJlRGVmYXVsdCIsIl9sb2dnZXIiLCJfbWF0cml4IiwiX2NyeXB0byIsIl9jbGFzc25hbWVzIiwiX2NoZWNrIiwiX01hdHJpeENsaWVudFBlZyIsIl9sYW5ndWFnZUhhbmRsZXIiLCJfTW9kYWwiLCJfc3RyaW5ncyIsIl9JbnRlcmFjdGl2ZUF1dGhFbnRyeUNvbXBvbmVudHMiLCJfUGFzc3BocmFzZUZpZWxkIiwiX1N0eWxlZFJhZGlvQnV0dG9uIiwiX0FjY2Vzc2libGVCdXR0b24iLCJfRGlhbG9nQnV0dG9ucyIsIl9JbmxpbmVTcGlubmVyIiwiX1Jlc3RvcmVLZXlCYWNrdXBEaWFsb2ciLCJfV2VsbEtub3duVXRpbHMiLCJfTW9kdWxlUnVubmVyIiwiX0ZpZWxkIiwiX0Jhc2VEaWFsb2ciLCJfU3Bpbm5lciIsIl9JbnRlcmFjdGl2ZUF1dGhEaWFsb2ciLCJfUGFzc3BocmFzZUNvbmZpcm1GaWVsZCIsIl9kZWh5ZHJhdGlvbiIsIl9nZXRSZXF1aXJlV2lsZGNhcmRDYWNoZSIsImUiLCJXZWFrTWFwIiwiciIsInQiLCJfX2VzTW9kdWxlIiwiZGVmYXVsdCIsImhhcyIsImdldCIsIm4iLCJfX3Byb3RvX18iLCJhIiwiT2JqZWN0IiwiZGVmaW5lUHJvcGVydHkiLCJnZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IiLCJ1IiwiaGFzT3duUHJvcGVydHkiLCJjYWxsIiwiaSIsInNldCIsIlBoYXNlIiwiUEFTU1dPUkRfTUlOX1NDT1JFIiwiQ3JlYXRlU2VjcmV0U3RvcmFnZURpYWxvZyIsIlJlYWN0IiwiUHVyZUNvbXBvbmVudCIsImNvbnN0cnVjdG9yIiwicHJvcHMiLCJfZGVmaW5lUHJvcGVydHkyIiwiY3JlYXRlUmVmIiwic3RhdGUiLCJwaGFzZSIsIk1pZ3JhdGUiLCJmZXRjaEJhY2t1cEluZm8iLCJzZXRTdGF0ZSIsInBhc3NQaHJhc2VLZXlTZWxlY3RlZCIsInRhcmdldCIsInZhbHVlIiwiU2VjdXJlQmFja3VwU2V0dXBNZXRob2QiLCJLZXkiLCJyZWNvdmVyeUtleSIsIk1hdHJpeENsaWVudFBlZyIsInNhZmVHZXQiLCJnZXRDcnlwdG8iLCJjcmVhdGVSZWNvdmVyeUtleUZyb21QYXNzcGhyYXNlIiwiY29waWVkIiwiZG93bmxvYWRlZCIsInNldFBhc3NwaHJhc2UiLCJTaG93S2V5IiwiUGFzc3BocmFzZSIsInByZXZlbnREZWZhdWx0IiwiYmFja3VwVHJ1c3RJbmZvIiwidHJ1c3RlZCIsImJvb3RzdHJhcFNlY3JldFN0b3JhZ2UiLCJyZXN0b3JlQmFja3VwIiwic3VjY2Vzc2Z1bCIsImNvcHlOb2RlIiwicmVjb3ZlcnlLZXlOb2RlIiwiY3VycmVudCIsImJsb2IiLCJCbG9iIiwiZW5jb2RlZFByaXZhdGVLZXkiLCJ0eXBlIiwiRmlsZVNhdmVyIiwic2F2ZUFzIiwibWFrZVJlcXVlc3QiLCJjYW5VcGxvYWRLZXlzV2l0aFBhc3N3b3JkT25seSIsImFjY291bnRQYXNzd29yZCIsImlkZW50aWZpZXIiLCJ1c2VyIiwiZ2V0U2FmZVVzZXJJZCIsInBhc3N3b3JkIiwiZGlhbG9nQWVzdGhldGljcyIsIlNTT0F1dGhFbnRyeSIsIlBIQVNFX1BSRUFVVEgiLCJ0aXRsZSIsIl90IiwiYm9keSIsImNvbnRpbnVlVGV4dCIsImNvbnRpbnVlS2luZCIsIlBIQVNFX1BPU1RBVVRIIiwiZmluaXNoZWQiLCJNb2RhbCIsImNyZWF0ZURpYWxvZyIsIkludGVyYWN0aXZlQXV0aERpYWxvZyIsIm1hdHJpeENsaWVudCIsImFlc3RoZXRpY3NGb3JTdGFnZVBoYXNlcyIsIkxPR0lOX1RZUEUiLCJVTlNUQUJMRV9MT0dJTl9UWVBFIiwiY29uZmlybWVkIiwiRXJyb3IiLCJTdG9yaW5nIiwiZXJyb3IiLCJ1bmRlZmluZWQiLCJjbGkiLCJjcnlwdG8iLCJmb3JjZVJlc2V0IiwibG9nZ2VyIiwibG9nIiwiY3JlYXRlU2VjcmV0U3RvcmFnZUtleSIsInNldHVwTmV3S2V5QmFja3VwIiwic2V0dXBOZXdTZWNyZXRTdG9yYWdlIiwiYm9vdHN0cmFwQ3Jvc3NTaWduaW5nIiwiYXV0aFVwbG9hZERldmljZVNpZ25pbmdLZXlzIiwiZG9Cb290c3RyYXBVSUF1dGgiLCJrZXlCYWNrdXBJbmZvIiwiYmFja3VwSW5mbyIsImluaXRpYWxpc2VEZWh5ZHJhdGlvbiIsIlN0b3JlZCIsIk1hdHJpeEVycm9yIiwiaHR0cFN0YXR1cyIsImRhdGEiLCJmbG93cyIsImFjY291bnRQYXNzd29yZENvcnJlY3QiLCJvbkZpbmlzaGVkIiwia2V5Q2FsbGJhY2siLCJrIiwiUmVzdG9yZUtleUJhY2t1cERpYWxvZyIsInNob3dTdW1tYXJ5IiwiTG9hZGluZyIsIkNvbmZpcm1Ta2lwIiwiQ2hvb3NlS2V5UGFzc3BocmFzZSIsInBhc3NwaHJhc2VGaWVsZCIsInZhbGlkYXRlIiwiYWxsb3dFbXB0eSIsInZhbGlkIiwiZm9jdXMiLCJmb2N1c2VkIiwiUGFzc3BocmFzZUNvbmZpcm0iLCJwYXNzUGhyYXNlIiwicGFzc1BocmFzZUNvbmZpcm0iLCJwYXNzUGhyYXNlVmFsaWQiLCJyZXN1bHQiLCJzZXR1cE1ldGhvZHMiLCJnZXRTZWN1cmVCYWNrdXBTZXR1cE1ldGhvZHMiLCJpbmNsdWRlcyIsInF1ZXJ5S2V5VXBsb2FkQXV0aCIsImNhblNraXAiLCJpc1NlY3VyZUJhY2t1cFJlcXVpcmVkIiwib24iLCJDcnlwdG9FdmVudCIsIktleUJhY2t1cFN0YXR1cyIsIm9uS2V5QmFja3VwU3RhdHVzQ2hhbmdlIiwiZ2V0SW5pdGlhbFBoYXNlIiwiY29tcG9uZW50V2lsbFVubW91bnQiLCJyZW1vdmVMaXN0ZW5lciIsImtleUZyb21DdXN0b21pc2F0aW9ucyIsIk1vZHVsZVJ1bm5lciIsImluc3RhbmNlIiwiZXh0ZW5zaW9ucyIsImNyeXB0b1NldHVwIiwicHJpdmF0ZUtleSIsImdldEtleUJhY2t1cFZlcnNpb24iLCJpc0tleUJhY2t1cFRydXN0ZWQiLCJjb25zb2xlIiwiTG9hZEVycm9yIiwidXBsb2FkRGV2aWNlU2lnbmluZ0tleXMiLCJzb21lIiwiZiIsInN0YWdlcyIsImxlbmd0aCIsInJlbmRlck9wdGlvbktleSIsImNyZWF0ZUVsZW1lbnQiLCJrZXkiLCJuYW1lIiwiY2hlY2tlZCIsIm9uQ2hhbmdlIiwib25LZXlQYXNzcGhyYXNlQ2hhbmdlIiwib3V0bGluZWQiLCJjbGFzc05hbWUiLCJyZW5kZXJPcHRpb25QYXNzcGhyYXNlIiwicmVuZGVyUGhhc2VDaG9vc2VLZXlQYXNzcGhyYXNlIiwib3B0aW9uS2V5Iiwib3B0aW9uUGFzc3BocmFzZSIsIm9uU3VibWl0Iiwib25DaG9vc2VLZXlQYXNzcGhyYXNlRm9ybVN1Ym1pdCIsInJvbGUiLCJwcmltYXJ5QnV0dG9uIiwib25QcmltYXJ5QnV0dG9uQ2xpY2siLCJvbkNhbmNlbCIsIm9uQ2FuY2VsQ2xpY2siLCJoYXNDYW5jZWwiLCJyZW5kZXJQaGFzZU1pZ3JhdGUiLCJhdXRoUHJvbXB0IiwibmV4dENhcHRpb24iLCJpZCIsImxhYmVsIiwib25BY2NvdW50UGFzc3dvcmRDaGFuZ2UiLCJmb3JjZVZhbGlkaXR5IiwiYXV0b0ZvY3VzIiwib25NaWdyYXRlRm9ybVN1Ym1pdCIsInByaW1hcnlEaXNhYmxlZCIsIm9uQ2xpY2siLCJyZW5kZXJQaGFzZVBhc3NQaHJhc2UiLCJvblBhc3NQaHJhc2VOZXh0Q2xpY2siLCJvblBhc3NQaHJhc2VDaGFuZ2UiLCJtaW5TY29yZSIsIm9uVmFsaWRhdGUiLCJvblBhc3NQaHJhc2VWYWxpZGF0ZSIsImZpZWxkUmVmIiwiX3RkIiwibGFiZWxFbnRlclBhc3N3b3JkIiwibGFiZWxTdHJvbmdQYXNzd29yZCIsImxhYmVsQWxsb3dlZEJ1dFVuc2FmZSIsImRpc2FibGVkIiwicmVuZGVyUGhhc2VQYXNzUGhyYXNlQ29uZmlybSIsIm1hdGNoVGV4dCIsImNoYW5nZVRleHQiLCJzdGFydHNXaXRoIiwicGFzc1BocmFzZU1hdGNoIiwia2luZCIsIm9uU2V0QWdhaW5DbGljayIsIm9uUGFzc1BocmFzZUNvbmZpcm1OZXh0Q2xpY2siLCJvblBhc3NQaHJhc2VDb25maXJtQ2hhbmdlIiwibGFiZWxSZXF1aXJlZCIsImxhYmVsSW52YWxpZCIsInJlbmRlclBoYXNlU2hvd0tleSIsImNvbnRpbnVlQnV0dG9uIiwib25TaG93S2V5Q29udGludWVDbGljayIsInJlZiIsIm9uRG93bmxvYWRDbGljayIsImRvd25sb2FkQnV0dG9uIiwiY29weUJ1dHRvbiIsIm9uQ29weUNsaWNrIiwicmVuZGVyQnVzeVBoYXNlIiwicmVuZGVyU3RvcmVkUGhhc2UiLCJGcmFnbWVudCIsInJlbmRlclBoYXNlTG9hZEVycm9yIiwib25Mb2FkUmV0cnlDbGljayIsInJlbmRlclBoYXNlU2tpcENvbmZpcm0iLCJvbkdvQmFja0NsaWNrIiwidGl0bGVGb3JQaGFzZSIsInRvcENvbXBvbmVudCIsImNsYXNzTmFtZXMiLCJteF9TdWNjZXNzRGlhbG9nIiwicmVuZGVyIiwiY29udGVudCIsInRpdGxlQ2xhc3MiLCJ0b3AiLCJmaXhlZFdpZHRoIiwiZXhwb3J0cyJdLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9hc3luYy1jb21wb25lbnRzL3ZpZXdzL2RpYWxvZ3Mvc2VjdXJpdHkvQ3JlYXRlU2VjcmV0U3RvcmFnZURpYWxvZy50c3giXSwic291cmNlc0NvbnRlbnQiOlsiLypcbkNvcHlyaWdodCAyMDI0IE5ldyBWZWN0b3IgTHRkLlxuQ29weXJpZ2h0IDIwMTksIDIwMjAgLCAyMDIzIFRoZSBNYXRyaXgub3JnIEZvdW5kYXRpb24gQy5JLkMuXG5Db3B5cmlnaHQgMjAxOCwgMjAxOSBOZXcgVmVjdG9yIEx0ZFxuXG5TUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQUdQTC0zLjAtb25seSBPUiBHUEwtMy4wLW9ubHlcblBsZWFzZSBzZWUgTElDRU5TRSBmaWxlcyBpbiB0aGUgcmVwb3NpdG9yeSByb290IGZvciBmdWxsIGRldGFpbHMuXG4qL1xuXG5pbXBvcnQgUmVhY3QsIHsgY3JlYXRlUmVmIH0gZnJvbSBcInJlYWN0XCI7XG5pbXBvcnQgRmlsZVNhdmVyIGZyb20gXCJmaWxlLXNhdmVyXCI7XG5pbXBvcnQgeyBsb2dnZXIgfSBmcm9tIFwibWF0cml4LWpzLXNkay9zcmMvbG9nZ2VyXCI7XG5pbXBvcnQgeyBBdXRoRGljdCwgQ3Jvc3NTaWduaW5nS2V5cywgTWF0cml4RXJyb3IsIFVJQUZsb3csIFVJQVJlc3BvbnNlIH0gZnJvbSBcIm1hdHJpeC1qcy1zZGsvc3JjL21hdHJpeFwiO1xuaW1wb3J0IHsgQ3J5cHRvRXZlbnQgfSBmcm9tIFwibWF0cml4LWpzLXNkay9zcmMvY3J5cHRvXCI7XG5pbXBvcnQgY2xhc3NOYW1lcyBmcm9tIFwiY2xhc3NuYW1lc1wiO1xuaW1wb3J0IHsgQmFja3VwVHJ1c3RJbmZvLCBHZW5lcmF0ZWRTZWNyZXRTdG9yYWdlS2V5LCBLZXlCYWNrdXBJbmZvIH0gZnJvbSBcIm1hdHJpeC1qcy1zZGsvc3JjL2NyeXB0by1hcGlcIjtcbmltcG9ydCBDaGVja21hcmtJY29uIGZyb20gXCJAdmVjdG9yLWltL2NvbXBvdW5kLWRlc2lnbi10b2tlbnMvYXNzZXRzL3dlYi9pY29ucy9jaGVja1wiO1xuXG5pbXBvcnQgeyBNYXRyaXhDbGllbnRQZWcgfSBmcm9tIFwiLi4vLi4vLi4vLi4vTWF0cml4Q2xpZW50UGVnXCI7XG5pbXBvcnQgeyBfdCwgX3RkIH0gZnJvbSBcIi4uLy4uLy4uLy4uL2xhbmd1YWdlSGFuZGxlclwiO1xuaW1wb3J0IE1vZGFsIGZyb20gXCIuLi8uLi8uLi8uLi9Nb2RhbFwiO1xuaW1wb3J0IHsgY29weU5vZGUgfSBmcm9tIFwiLi4vLi4vLi4vLi4vdXRpbHMvc3RyaW5nc1wiO1xuaW1wb3J0IHsgU1NPQXV0aEVudHJ5IH0gZnJvbSBcIi4uLy4uLy4uLy4uL2NvbXBvbmVudHMvdmlld3MvYXV0aC9JbnRlcmFjdGl2ZUF1dGhFbnRyeUNvbXBvbmVudHNcIjtcbmltcG9ydCBQYXNzcGhyYXNlRmllbGQgZnJvbSBcIi4uLy4uLy4uLy4uL2NvbXBvbmVudHMvdmlld3MvYXV0aC9QYXNzcGhyYXNlRmllbGRcIjtcbmltcG9ydCBTdHlsZWRSYWRpb0J1dHRvbiBmcm9tIFwiLi4vLi4vLi4vLi4vY29tcG9uZW50cy92aWV3cy9lbGVtZW50cy9TdHlsZWRSYWRpb0J1dHRvblwiO1xuaW1wb3J0IEFjY2Vzc2libGVCdXR0b24gZnJvbSBcIi4uLy4uLy4uLy4uL2NvbXBvbmVudHMvdmlld3MvZWxlbWVudHMvQWNjZXNzaWJsZUJ1dHRvblwiO1xuaW1wb3J0IERpYWxvZ0J1dHRvbnMgZnJvbSBcIi4uLy4uLy4uLy4uL2NvbXBvbmVudHMvdmlld3MvZWxlbWVudHMvRGlhbG9nQnV0dG9uc1wiO1xuaW1wb3J0IElubGluZVNwaW5uZXIgZnJvbSBcIi4uLy4uLy4uLy4uL2NvbXBvbmVudHMvdmlld3MvZWxlbWVudHMvSW5saW5lU3Bpbm5lclwiO1xuaW1wb3J0IFJlc3RvcmVLZXlCYWNrdXBEaWFsb2cgZnJvbSBcIi4uLy4uLy4uLy4uL2NvbXBvbmVudHMvdmlld3MvZGlhbG9ncy9zZWN1cml0eS9SZXN0b3JlS2V5QmFja3VwRGlhbG9nXCI7XG5pbXBvcnQge1xuICAgIGdldFNlY3VyZUJhY2t1cFNldHVwTWV0aG9kcyxcbiAgICBpc1NlY3VyZUJhY2t1cFJlcXVpcmVkLFxuICAgIFNlY3VyZUJhY2t1cFNldHVwTWV0aG9kLFxufSBmcm9tIFwiLi4vLi4vLi4vLi4vdXRpbHMvV2VsbEtub3duVXRpbHNcIjtcbmltcG9ydCB7IE1vZHVsZVJ1bm5lciB9IGZyb20gXCIuLi8uLi8uLi8uLi9tb2R1bGVzL01vZHVsZVJ1bm5lclwiO1xuaW1wb3J0IEZpZWxkIGZyb20gXCIuLi8uLi8uLi8uLi9jb21wb25lbnRzL3ZpZXdzL2VsZW1lbnRzL0ZpZWxkXCI7XG5pbXBvcnQgQmFzZURpYWxvZyBmcm9tIFwiLi4vLi4vLi4vLi4vY29tcG9uZW50cy92aWV3cy9kaWFsb2dzL0Jhc2VEaWFsb2dcIjtcbmltcG9ydCBTcGlubmVyIGZyb20gXCIuLi8uLi8uLi8uLi9jb21wb25lbnRzL3ZpZXdzL2VsZW1lbnRzL1NwaW5uZXJcIjtcbmltcG9ydCBJbnRlcmFjdGl2ZUF1dGhEaWFsb2cgZnJvbSBcIi4uLy4uLy4uLy4uL2NvbXBvbmVudHMvdmlld3MvZGlhbG9ncy9JbnRlcmFjdGl2ZUF1dGhEaWFsb2dcIjtcbmltcG9ydCB7IElWYWxpZGF0aW9uUmVzdWx0IH0gZnJvbSBcIi4uLy4uLy4uLy4uL2NvbXBvbmVudHMvdmlld3MvZWxlbWVudHMvVmFsaWRhdGlvblwiO1xuaW1wb3J0IFBhc3NwaHJhc2VDb25maXJtRmllbGQgZnJvbSBcIi4uLy4uLy4uLy4uL2NvbXBvbmVudHMvdmlld3MvYXV0aC9QYXNzcGhyYXNlQ29uZmlybUZpZWxkXCI7XG5pbXBvcnQgeyBpbml0aWFsaXNlRGVoeWRyYXRpb24gfSBmcm9tIFwiLi4vLi4vLi4vLi4vdXRpbHMvZGV2aWNlL2RlaHlkcmF0aW9uXCI7XG5cbi8vIEkgbWFkZSBhIG1pc3Rha2Ugd2hpbGUgY29udmVydGluZyB0aGlzIGFuZCBpdCBoYXMgdG8gYmUgZml4ZWQhXG5lbnVtIFBoYXNlIHtcbiAgICBMb2FkaW5nID0gXCJsb2FkaW5nXCIsXG4gICAgTG9hZEVycm9yID0gXCJsb2FkX2Vycm9yXCIsXG4gICAgQ2hvb3NlS2V5UGFzc3BocmFzZSA9IFwiY2hvb3NlX2tleV9wYXNzcGhyYXNlXCIsXG4gICAgTWlncmF0ZSA9IFwibWlncmF0ZVwiLFxuICAgIFBhc3NwaHJhc2UgPSBcInBhc3NwaHJhc2VcIixcbiAgICBQYXNzcGhyYXNlQ29uZmlybSA9IFwicGFzc3BocmFzZV9jb25maXJtXCIsXG4gICAgU2hvd0tleSA9IFwic2hvd19rZXlcIixcbiAgICBTdG9yaW5nID0gXCJzdG9yaW5nXCIsXG4gICAgU3RvcmVkID0gXCJzdG9yZWRcIixcbiAgICBDb25maXJtU2tpcCA9IFwiY29uZmlybV9za2lwXCIsXG59XG5cbmNvbnN0IFBBU1NXT1JEX01JTl9TQ09SRSA9IDQ7IC8vIFNvIHNlY3VyZSwgbWFueSBjaGFyYWN0ZXJzLCBtdWNoIGNvbXBsZXgsIHdvdywgZXRjLCBldGMuXG5cbmludGVyZmFjZSBJUHJvcHMge1xuICAgIGhhc0NhbmNlbD86IGJvb2xlYW47XG4gICAgYWNjb3VudFBhc3N3b3JkPzogc3RyaW5nO1xuICAgIGZvcmNlUmVzZXQ/OiBib29sZWFuO1xuICAgIG9uRmluaXNoZWQob2s/OiBib29sZWFuKTogdm9pZDtcbn1cblxuaW50ZXJmYWNlIElTdGF0ZSB7XG4gICAgcGhhc2U6IFBoYXNlO1xuICAgIHBhc3NQaHJhc2U6IHN0cmluZztcbiAgICBwYXNzUGhyYXNlVmFsaWQ6IGJvb2xlYW47XG4gICAgcGFzc1BocmFzZUNvbmZpcm06IHN0cmluZztcbiAgICBjb3BpZWQ6IGJvb2xlYW47XG4gICAgZG93bmxvYWRlZDogYm9vbGVhbjtcbiAgICBzZXRQYXNzcGhyYXNlOiBib29sZWFuO1xuXG4gICAgLyoqIEluZm9ybWF0aW9uIG9uIHRoZSBjdXJyZW50IGtleSBiYWNrdXAgdmVyc2lvbiwgYXMgcmV0dXJuZWQgYnkgdGhlIHNlcnZlci5cbiAgICAgKlxuICAgICAqIGBudWxsYCBjb3VsZCBtZWFuIGFueSBvZjpcbiAgICAgKiAgICAqIHdlIGhhdmVuJ3QgeWV0IHJlcXVlc3RlZCB0aGUgZGF0YSBmcm9tIHRoZSBzZXJ2ZXIuXG4gICAgICogICAgKiB3ZSB3ZXJlIHVuYWJsZSB0byByZWFjaCB0aGUgc2VydmVyLlxuICAgICAqICAgICogdGhlIHNlcnZlciByZXR1cm5lZCBrZXkgYmFja3VwIHZlcnNpb24gZGF0YSB3ZSBkaWRuJ3QgdW5kZXJzdGFuZCBvciB3YXMgbWFsZm9ybWVkLlxuICAgICAqICAgICogdGhlcmUgaXMgYWN0dWFsbHkgbm8gYmFja3VwIG9uIHRoZSBzZXJ2ZXIuXG4gICAgICovXG4gICAgYmFja3VwSW5mbzogS2V5QmFja3VwSW5mbyB8IG51bGw7XG5cbiAgICAvKipcbiAgICAgKiBJbmZvcm1hdGlvbiBvbiB3aGV0aGVyIHRoZSBiYWNrdXAgaW4gYGJhY2t1cEluZm9gIGlzIGNvcnJlY3RseSBzaWduZWQsIGFuZCB3aGV0aGVyIHdlIGhhdmUgdGhlIHJpZ2h0IGtleSB0b1xuICAgICAqIGRlY3J5cHQgaXQuXG4gICAgICpcbiAgICAgKiBgdW5kZWZpbmVkYCBpZiBgYmFja3VwSW5mb2AgaXMgbnVsbCwgb3IgaWYgY3J5cHRvIGlzIG5vdCBlbmFibGVkIGluIHRoZSBjbGllbnQuXG4gICAgICovXG4gICAgYmFja3VwVHJ1c3RJbmZvOiBCYWNrdXBUcnVzdEluZm8gfCB1bmRlZmluZWQ7XG5cbiAgICAvLyBkb2VzIHRoZSBzZXJ2ZXIgb2ZmZXIgYSBVSSBhdXRoIGZsb3cgd2l0aCBqdXN0IG0ubG9naW4ucGFzc3dvcmRcbiAgICAvLyBmb3IgL2tleXMvZGV2aWNlX3NpZ25pbmcvdXBsb2FkP1xuICAgIGNhblVwbG9hZEtleXNXaXRoUGFzc3dvcmRPbmx5OiBib29sZWFuIHwgbnVsbDtcbiAgICBhY2NvdW50UGFzc3dvcmQ6IHN0cmluZztcbiAgICBhY2NvdW50UGFzc3dvcmRDb3JyZWN0OiBib29sZWFuIHwgbnVsbDtcbiAgICBjYW5Ta2lwOiBib29sZWFuO1xuICAgIHBhc3NQaHJhc2VLZXlTZWxlY3RlZDogc3RyaW5nO1xuICAgIGVycm9yPzogYm9vbGVhbjtcbn1cblxuLyoqXG4gKiBXYWxrcyB0aGUgdXNlciB0aHJvdWdoIHRoZSBwcm9jZXNzIG9mIGNyZWF0aW5nIGEgNFMgcGFzc3BocmFzZSBhbmQgYm9vdHN0cmFwcGluZyBzZWNyZXQgc3RvcmFnZS5cbiAqXG4gKiBJZiB0aGUgdXNlciBhbHJlYWR5IGhhcyBhIGtleSBiYWNrdXAsIGZvbGxvd3MgYSBcIm1pZ3JhdGlvblwiIGZsb3cgKGFrYSBcIlVwZ3JhZGUgeW91ciBlbmNyeXB0aW9uXCIpIHdoaWNoXG4gKiBwcm9tcHRzIHRoZSB1c2VyIHRvIGVudGVyIHRoZWlyIGJhY2t1cCBkZWNyeXB0aW9uIHBhc3N3b3JkIChhIEN1cnZlMjU1MTkgcHJpdmF0ZSBrZXksIHBvc3NpYmx5IGRlcml2ZWRcbiAqIGZyb20gYSBwYXNzcGhyYXNlKSwgYW5kIHVzZXMgdGhhdCBhcyB0aGUgKEFFUykgNFMgZW5jcnlwdGlvbiBrZXkuXG4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIENyZWF0ZVNlY3JldFN0b3JhZ2VEaWFsb2cgZXh0ZW5kcyBSZWFjdC5QdXJlQ29tcG9uZW50PElQcm9wcywgSVN0YXRlPiB7XG4gICAgcHVibGljIHN0YXRpYyBkZWZhdWx0UHJvcHM6IFBhcnRpYWw8SVByb3BzPiA9IHtcbiAgICAgICAgaGFzQ2FuY2VsOiB0cnVlLFxuICAgICAgICBmb3JjZVJlc2V0OiBmYWxzZSxcbiAgICB9O1xuICAgIHByaXZhdGUgcmVjb3ZlcnlLZXk/OiBHZW5lcmF0ZWRTZWNyZXRTdG9yYWdlS2V5O1xuICAgIHByaXZhdGUgcmVjb3ZlcnlLZXlOb2RlID0gY3JlYXRlUmVmPEhUTUxFbGVtZW50PigpO1xuICAgIHByaXZhdGUgcGFzc3BocmFzZUZpZWxkID0gY3JlYXRlUmVmPEZpZWxkPigpO1xuXG4gICAgcHVibGljIGNvbnN0cnVjdG9yKHByb3BzOiBJUHJvcHMpIHtcbiAgICAgICAgc3VwZXIocHJvcHMpO1xuXG4gICAgICAgIGNvbnN0IGNsaSA9IE1hdHJpeENsaWVudFBlZy5zYWZlR2V0KCk7XG5cbiAgICAgICAg