VerifyMasterPasswordForm.tsx 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  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;
  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 {
  50. kek = await cryptoWorker.deriveKey(
  51. passphrase,
  52. keyAttributes.kekSalt,
  53. keyAttributes.opsLimit,
  54. keyAttributes.memLimit,
  55. );
  56. }
  57. } catch (e) {
  58. log.error("failed to derive key", e);
  59. throw Error(CustomError.WEAK_DEVICE);
  60. }
  61. if (!keyAttributes && typeof getKeyAttributes === "function") {
  62. keyAttributes = await getKeyAttributes(kek);
  63. }
  64. if (!keyAttributes) {
  65. throw Error("couldn't get key attributes");
  66. }
  67. try {
  68. const key = await cryptoWorker.decryptB64(
  69. keyAttributes.encryptedKey,
  70. keyAttributes.keyDecryptionNonce,
  71. kek,
  72. );
  73. callback(key, kek, keyAttributes, passphrase);
  74. } catch (e) {
  75. log.error("user entered a wrong password", e);
  76. throw Error(CustomError.INCORRECT_PASSWORD);
  77. }
  78. } catch (e) {
  79. if (e instanceof Error) {
  80. if (e.message === CustomError.TWO_FACTOR_ENABLED) {
  81. // two factor enabled, user has been redirected to two factor page
  82. return;
  83. }
  84. log.error("failed to verify passphrase", e);
  85. switch (e.message) {
  86. case CustomError.WEAK_DEVICE:
  87. setFieldError(t("WEAK_DEVICE"));
  88. break;
  89. case CustomError.INCORRECT_PASSWORD:
  90. setFieldError(t("INCORRECT_PASSPHRASE"));
  91. break;
  92. default:
  93. setFieldError(`${t("UNKNOWN_ERROR")} ${e.message}`);
  94. }
  95. }
  96. }
  97. };
  98. return (
  99. <SingleInputForm
  100. callback={verifyPassphrase}
  101. placeholder={t("RETURN_PASSPHRASE_HINT")}
  102. buttonText={buttonText}
  103. submitButtonProps={submitButtonProps}
  104. hiddenPreInput={
  105. <Input
  106. sx={{ display: "none" }}
  107. id="email"
  108. name="email"
  109. autoComplete="username"
  110. type="email"
  111. value={user?.email}
  112. />
  113. }
  114. autoComplete={"current-password"}
  115. fieldType="password"
  116. />
  117. );
  118. }