VerifyMasterPasswordForm.tsx 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. import log from "@/next/log";
  2. import type { SRPAttributes } from "@ente/accounts/types/srp";
  3. import { Input, type ButtonProps } from "@mui/material";
  4. import { t } from "i18next";
  5. import SingleInputForm, {
  6. type SingleInputFormProps,
  7. } from "../components/SingleInputForm";
  8. import ComlinkCryptoWorker from "../crypto";
  9. import { CustomError } from "../error";
  10. import type { KeyAttributes, User } from "../user/types";
  11. export interface VerifyMasterPasswordFormProps {
  12. user: User;
  13. keyAttributes: KeyAttributes | undefined;
  14. callback: (
  15. key: string,
  16. kek: string,
  17. keyAttributes: KeyAttributes,
  18. passphrase?: string,
  19. ) => void;
  20. buttonText: string;
  21. submitButtonProps?: ButtonProps;
  22. getKeyAttributes?: (kek: string) => Promise<KeyAttributes>;
  23. srpAttributes?: SRPAttributes;
  24. }
  25. export default function VerifyMasterPasswordForm({
  26. user,
  27. keyAttributes,
  28. srpAttributes,
  29. callback,
  30. buttonText,
  31. submitButtonProps,
  32. getKeyAttributes,
  33. }: VerifyMasterPasswordFormProps) {
  34. const verifyPassphrase: SingleInputFormProps["callback"] = async (
  35. passphrase,
  36. setFieldError,
  37. ) => {
  38. try {
  39. const cryptoWorker = await ComlinkCryptoWorker.getInstance();
  40. let kek: string;
  41. try {
  42. if (srpAttributes) {
  43. kek = await cryptoWorker.deriveKey(
  44. passphrase,
  45. srpAttributes.kekSalt,
  46. srpAttributes.opsLimit,
  47. srpAttributes.memLimit,
  48. );
  49. } else if (keyAttributes) {
  50. kek = await cryptoWorker.deriveKey(
  51. passphrase,
  52. keyAttributes.kekSalt,
  53. keyAttributes.opsLimit,
  54. keyAttributes.memLimit,
  55. );
  56. } else
  57. throw new Error("Both SRP and key attributes are missing");
  58. } catch (e) {
  59. log.error("failed to derive key", e);
  60. throw Error(CustomError.WEAK_DEVICE);
  61. }
  62. if (!keyAttributes && typeof getKeyAttributes === "function") {
  63. keyAttributes = await getKeyAttributes(kek);
  64. }
  65. if (!keyAttributes) {
  66. throw Error("couldn't get key attributes");
  67. }
  68. try {
  69. const key = await cryptoWorker.decryptB64(
  70. keyAttributes.encryptedKey,
  71. keyAttributes.keyDecryptionNonce,
  72. kek,
  73. );
  74. callback(key, kek, keyAttributes, passphrase);
  75. } catch (e) {
  76. log.error("user entered a wrong password", e);
  77. throw Error(CustomError.INCORRECT_PASSWORD);
  78. }
  79. } catch (e) {
  80. if (e instanceof Error) {
  81. if (e.message === CustomError.TWO_FACTOR_ENABLED) {
  82. // two factor enabled, user has been redirected to two factor page
  83. return;
  84. }
  85. log.error("failed to verify passphrase", e);
  86. switch (e.message) {
  87. case CustomError.WEAK_DEVICE:
  88. setFieldError(t("WEAK_DEVICE"));
  89. break;
  90. case CustomError.INCORRECT_PASSWORD:
  91. setFieldError(t("INCORRECT_PASSPHRASE"));
  92. break;
  93. default:
  94. setFieldError(`${t("UNKNOWN_ERROR")} ${e.message}`);
  95. }
  96. }
  97. }
  98. };
  99. return (
  100. <SingleInputForm
  101. callback={verifyPassphrase}
  102. placeholder={t("RETURN_PASSPHRASE_HINT")}
  103. buttonText={buttonText}
  104. submitButtonProps={submitButtonProps}
  105. hiddenPreInput={
  106. <Input
  107. sx={{ display: "none" }}
  108. id="email"
  109. name="email"
  110. autoComplete="username"
  111. type="email"
  112. value={user?.email}
  113. />
  114. }
  115. autoComplete={"current-password"}
  116. fieldType="password"
  117. />
  118. );
  119. }