@rapidmx/web-client 0.20.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +415 -381
- package/apps/admin/_layout.tsx +2 -2
- package/apps/admin/audit-log/index.tsx +4 -4
- package/apps/admin/branding/index.tsx +39 -39
- package/apps/admin/data-requests/index.tsx +482 -482
- package/apps/admin/distribution-lists/[uid].tsx +3 -3
- package/apps/admin/distribution-lists/index.tsx +4 -4
- package/apps/admin/distribution-lists/new/index.tsx +5 -5
- package/apps/admin/domains/[uid].tsx +166 -166
- package/apps/admin/domains/index.tsx +4 -4
- package/apps/admin/domains/new/index.tsx +5 -5
- package/apps/admin/encryption-policy/index.tsx +1 -1
- package/apps/admin/escrow-scopes/[uid].tsx +350 -350
- package/apps/admin/escrow-scopes/index.tsx +4 -4
- package/apps/admin/escrow-scopes/new/index.tsx +5 -5
- package/apps/admin/index.tsx +129 -129
- package/apps/admin/ingest-queue/index.tsx +4 -4
- package/apps/admin/mailbox-policy/index.tsx +1 -1
- package/apps/admin/mailboxes/[uid].tsx +271 -271
- package/apps/admin/mailboxes/new/index.tsx +30 -30
- package/apps/admin/plugins/index.tsx +15 -15
- package/apps/admin/quarantine/index.tsx +5 -5
- package/apps/admin/retention-policy/index.tsx +39 -39
- package/apps/admin/signing-certificates/index.tsx +343 -343
- package/apps/admin/transport-rules/[uid].tsx +5 -5
- package/apps/admin/transport-rules/_transportRuleConfig.tsx +3 -3
- package/apps/admin/transport-rules/index.tsx +4 -4
- package/apps/admin/transport-rules/new/index.tsx +5 -5
- package/apps/escrow/_layout.tsx +1 -1
- package/apps/escrow/audit-log/index.tsx +196 -196
- package/apps/escrow/index.tsx +4 -4
- package/apps/escrow/matters/[uid].tsx +621 -621
- package/apps/escrow/matters/new/index.tsx +6 -6
- package/apps/shared/appearance/AppearanceForm.tsx +3 -3
- package/apps/shared/appearance/AppearanceHead.tsx +1 -1
- package/apps/shared/appearance/AppearanceProvider.tsx +3 -3
- package/apps/shared/appearance/appearanceCache.ts +1 -1
- package/apps/shared/appearance/appearanceContext.ts +1 -1
- package/apps/shared/appearance/theme.ts +1 -1
- package/apps/shared/auth/adminAccess.ts +99 -99
- package/apps/shared/calendar/bookingPlugin.ts +1 -1
- package/apps/shared/calendar/calendarLiveUpdates.ts +1 -1
- package/apps/shared/calendar/calendarReminders.ts +1 -1
- package/apps/shared/calendar/useCalendarReminders.ts +1 -1
- package/apps/shared/components/admin/diagnostics/DiagnosticsManager.tsx +1 -1
- package/apps/shared/components/admin/diagnostics/InstalledPlugins.tsx +2 -2
- package/apps/shared/components/admin/diagnostics/LogsPanel.tsx +2 -2
- package/apps/shared/components/admin/diagnostics/PackagesTable.tsx +1 -1
- package/apps/shared/components/admin/diagnostics/RuntimePanel.tsx +1 -1
- package/apps/shared/components/admin/diagnostics/SystemPanel.tsx +2 -2
- package/apps/shared/components/admin/diagnostics/VersionsPanel.tsx +1 -1
- package/apps/shared/components/admin/diagnostics/diagnosticsApi.ts +1 -1
- package/apps/shared/components/admin/diagnostics/format.ts +1 -1
- package/apps/shared/components/admin/diagnostics/logClient.ts +1 -1
- package/apps/shared/components/admin/distributionLists/MemberListCard.tsx +4 -4
- package/apps/shared/components/admin/elevation.ts +1 -1
- package/apps/shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.tsx +3 -3
- package/apps/shared/components/admin/layout/AdminShell.tsx +384 -384
- package/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.tsx +238 -238
- package/apps/shared/components/admin/mailboxes/EscrowScopeCard.tsx +154 -152
- package/apps/shared/components/admin/mailboxes/LeftoverMailboxesSection.tsx +194 -194
- package/apps/shared/components/admin/mailboxes/MailboxTable.tsx +1 -1
- package/apps/shared/components/admin/mailboxes/ResourceSettingsCard.tsx +19 -14
- package/apps/shared/components/admin/mailboxes/ShareAccessCard.tsx +5 -5
- package/apps/shared/components/admin/settings/BrandingForm.tsx +423 -423
- package/apps/shared/components/admin/settings/DomainDnsSetup.tsx +5 -5
- package/apps/shared/components/admin/settings/EncryptionPolicyForm.tsx +119 -119
- package/apps/shared/components/admin/settings/LoadedSettingsForm.tsx +2 -2
- package/apps/shared/components/admin/settings/MailboxCreateForm.tsx +32 -27
- package/apps/shared/components/admin/settings/MailboxPolicyForm.tsx +198 -198
- package/apps/shared/components/admin/settings/PluginsManager.tsx +2093 -2093
- package/apps/shared/components/admin/settings/RetentionPolicyForm.tsx +182 -182
- package/apps/shared/components/admin/setup/EscrowSetupStep.tsx +288 -288
- package/apps/shared/components/admin/setup/SetupWizard.tsx +450 -446
- package/apps/shared/components/admin/signOut.ts +1 -1
- package/apps/shared/components/admin/usePagedList.tsx +129 -129
- package/apps/shared/components/calendar/AppointmentQuickForm.tsx +3 -3
- package/apps/shared/components/calendar/CalendarListSidebar.tsx +3 -3
- package/apps/shared/components/calendar/DescriptionEditor.tsx +1 -1
- package/apps/shared/components/calendar/EventDescriptionView.tsx +1 -1
- package/apps/shared/components/calendar/EventDetails.tsx +16 -14
- package/apps/shared/components/calendar/EventEditor.tsx +54 -39
- package/apps/shared/components/calendar/EventExpandedForm.tsx +3 -3
- package/apps/shared/components/calendar/EventFormParts.tsx +2 -2
- package/apps/shared/components/calendar/EventModal.tsx +206 -206
- package/apps/shared/components/calendar/EventQuickForm.tsx +3 -3
- package/apps/shared/components/calendar/EventShell.tsx +1 -1
- package/apps/shared/components/calendar/FindATime.tsx +6 -4
- package/apps/shared/components/calendar/GuestInput.tsx +2 -2
- package/apps/shared/components/calendar/MonthView.tsx +185 -185
- package/apps/shared/components/calendar/QuickCreateFaces.tsx +1 -1
- package/apps/shared/components/calendar/QuickFaceFrame.tsx +1 -1
- package/apps/shared/components/calendar/RecurrenceEditor.tsx +227 -227
- package/apps/shared/components/calendar/RequestChangeForm.tsx +8 -6
- package/apps/shared/components/calendar/ResourcePicker.tsx +7 -5
- package/apps/shared/components/calendar/SplitDayView.tsx +144 -144
- package/apps/shared/components/calendar/TaskQuickForm.tsx +23 -18
- package/apps/shared/components/calendar/TimeGridView.tsx +246 -246
- package/apps/shared/components/calendar/allDay.ts +124 -124
- package/apps/shared/components/calendar/eventDialogFields.ts +3 -3
- package/apps/shared/components/calendar/eventForm.ts +3 -3
- package/apps/shared/components/calendar/eventFormat.ts +3 -3
- package/apps/shared/components/calendar/findTimeGrid.ts +2 -2
- package/apps/shared/components/calendar/layout/CalendarShell.tsx +11 -9
- package/apps/shared/components/contacts/ContactDetailPane.tsx +4 -4
- package/apps/shared/components/contacts/ContactForm.tsx +393 -383
- package/apps/shared/components/contacts/ContactsSidebar.tsx +7 -5
- package/apps/shared/components/contacts/ContactsToolbar.tsx +103 -103
- package/apps/shared/components/contacts/KeyChangeReview.tsx +5 -5
- package/apps/shared/components/contacts/contactKeys.ts +3 -3
- package/apps/shared/components/contacts/layout/ContactsShell.tsx +9 -7
- package/apps/shared/components/escrow/layout/EscrowShell.tsx +155 -155
- package/apps/shared/components/forms/StringListField.tsx +1 -1
- package/apps/shared/components/layout/AppShell.tsx +465 -461
- package/apps/shared/components/layout/BrandingChrome.tsx +1 -1
- package/apps/shared/components/layout/KeyEnrollmentGate.tsx +398 -398
- package/apps/shared/components/layout/MailboxProvisioning.tsx +170 -168
- package/apps/shared/components/layout/RecoveryCodeUnlock.tsx +9 -9
- package/apps/shared/components/layout/ResponsiveToolbar.tsx +315 -315
- package/apps/shared/components/layout/ThemeSwitch.tsx +84 -84
- package/apps/shared/components/layout/UnlockPromptProvider.tsx +6 -6
- package/apps/shared/components/layout/UserMenu.tsx +441 -439
- package/apps/shared/components/mail/ConversationList.tsx +334 -332
- package/apps/shared/components/mail/ConversationThreadPane.tsx +624 -613
- package/apps/shared/components/mail/InviteCard.tsx +11 -9
- package/apps/shared/components/mail/LazyReadingPane.tsx +2 -2
- package/apps/shared/components/mail/MailAddress.tsx +1 -1
- package/apps/shared/components/mail/MailListToolbar.tsx +2 -2
- package/apps/shared/components/mail/MailSelectionBar.tsx +240 -240
- package/apps/shared/components/mail/MenuButton.tsx +404 -404
- package/apps/shared/components/mail/MessageDetailPane.tsx +1759 -1757
- package/apps/shared/components/mail/MoveToFolderDialog.tsx +8 -6
- package/apps/shared/components/mail/OutboxRowStatus.tsx +1 -1
- package/apps/shared/components/mail/PermanentDeleteDialog.tsx +2 -2
- package/apps/shared/components/mail/compose/ComposeContext.tsx +323 -312
- package/apps/shared/components/mail/compose/ComposeToolbar.tsx +422 -422
- package/apps/shared/components/mail/compose/ComposeWindow.tsx +1709 -1681
- package/apps/shared/components/mail/compose/ComposeWindowPlaceholder.tsx +4 -4
- package/apps/shared/components/mail/compose/EmojiPicker.tsx +2 -2
- package/apps/shared/components/mail/compose/GifPicker.tsx +3 -3
- package/apps/shared/components/mail/compose/RecipientInput.tsx +1 -1
- package/apps/shared/components/mail/compose/RichTextEditor.tsx +147 -147
- package/apps/shared/components/mail/compose/ScheduleSendPicker.tsx +1 -1
- package/apps/shared/components/mail/compose/composeFlushRegistry.ts +60 -60
- package/apps/shared/components/mail/compose/encryptionRequirement.ts +2 -2
- package/apps/shared/components/mail/compose/quotedBody.ts +161 -161
- package/apps/shared/components/mail/compose/recipients.ts +1 -1
- package/apps/shared/components/mail/invite/InviteRowChip.tsx +2 -2
- package/apps/shared/components/mail/invite/InviteRsvpPopover.tsx +9 -7
- package/apps/shared/components/mail/invite/InviteTimeline.tsx +1 -1
- package/apps/shared/components/mail/invite/ProposeTimeForm.tsx +2 -2
- package/apps/shared/components/mail/invite/inviteFormat.ts +2 -2
- package/apps/shared/components/mail/invite/inviteStore.ts +14 -6
- package/apps/shared/components/mail/labelMenu.tsx +9 -7
- package/apps/shared/components/mail/layout/MailShell.tsx +4 -4
- package/apps/shared/components/mail/listPreferences.ts +2 -2
- package/apps/shared/components/mail/pinnedSigners.ts +45 -19
- package/apps/shared/components/mail/reading/EncryptedBody.tsx +2 -2
- package/apps/shared/components/mail/reading/MessageBody.tsx +6 -6
- package/apps/shared/components/mail/reading/MessageCard.tsx +3 -3
- package/apps/shared/components/mail/reading/MessageMoreMenu.tsx +258 -258
- package/apps/shared/components/mail/reading/MessageSourceDialog.tsx +79 -79
- package/apps/shared/components/mail/reading/PendingMessageCard.tsx +1 -1
- package/apps/shared/components/mail/reading/bodyContent.ts +3 -3
- package/apps/shared/components/mail/reading/bodyHtml.ts +1 -1
- package/apps/shared/components/mail/reading/messageExport.ts +59 -59
- package/apps/shared/components/mail/reading/printMessage.ts +99 -99
- package/apps/shared/components/mail/reading/useMessageActions.ts +450 -443
- package/apps/shared/components/mail/unreadStyle.tsx +1 -1
- package/apps/shared/components/mail/verificationSeals.ts +129 -125
- package/apps/shared/components/mail/writableMailboxes.ts +14 -7
- package/apps/shared/components/rules/RuleBuilder.tsx +311 -311
- package/apps/shared/components/settings/SigningCertificateCard.tsx +344 -344
- package/apps/shared/components/settings/layout/SettingsShell.tsx +8 -6
- package/apps/shared/components/sharing/PrincipalListField.tsx +1 -1
- package/apps/shared/components/sharing/PrincipalPicker.tsx +1 -1
- package/apps/shared/components/sharing/PrincipalResolver.tsx +4 -4
- package/apps/shared/components/tasks/TasksSidebar.tsx +7 -5
- package/apps/shared/components/tasks/layout/TasksShell.tsx +9 -7
- package/apps/shared/keyboard/GlobalShortcuts.tsx +51 -51
- package/apps/shared/keyboard/ShortcutProvider.tsx +62 -62
- package/apps/shared/keyboard/ShortcutsDialog.tsx +84 -84
- package/apps/shared/keyboard/dispatch.ts +124 -124
- package/apps/shared/keyboard/format.ts +89 -89
- package/apps/shared/keyboard/keymap.ts +114 -114
- package/apps/shared/keyboard/registry.ts +65 -65
- package/apps/shared/keyboard/targets.ts +79 -79
- package/apps/shared/mail/findWellKnownFolderUid.ts +6 -4
- package/apps/shared/mail/folderCounts.ts +8 -4
- package/apps/shared/mail/folderOfType.ts +51 -49
- package/apps/shared/mail/folderTree.ts +143 -143
- package/apps/shared/mail/listAllPages.ts +39 -39
- package/apps/shared/mail/listSnapshots.ts +2 -2
- package/apps/shared/mail/messageReadState.ts +6 -5
- package/apps/shared/mail/newMailNotifications.ts +171 -171
- package/apps/shared/mail/outbox/composeBridge.ts +2 -2
- package/apps/shared/mail/outbox/outboxState.ts +1 -1
- package/apps/shared/mail/outbox/outgoingReplies.ts +2 -2
- package/apps/shared/mail/outbox/sendDecision.ts +2 -2
- package/apps/shared/mail/outbox/sendJob.ts +450 -445
- package/apps/shared/mail/outbox/sendOutcomes.ts +160 -155
- package/apps/shared/mail/outbox/useOutboxStatus.ts +5 -3
- package/apps/shared/mail/permanentDelete.ts +8 -7
- package/apps/shared/mail/primaryMailbox.ts +1 -1
- package/apps/shared/mail/reportNotices.ts +66 -66
- package/apps/shared/mail/senderBlocking.ts +157 -141
- package/apps/shared/mail/useMailConnection.ts +207 -205
- package/apps/shared/mail/useMailLiveUpdates.ts +277 -277
- package/apps/shared/mail/useMailboxUpdateAccess.ts +84 -60
- package/apps/shared/mail/useMarkMessageRead.ts +54 -47
- package/apps/shared/mail/useNewMailNotifications.ts +178 -178
- package/apps/shared/mail/usePermanentDelete.tsx +5 -3
- package/apps/shared/navigation/pageTitle.ts +1 -1
- package/apps/shared/notifications/NotificationCenter.tsx +1 -1
- package/apps/shared/notifications/NotificationHistoryDialog.tsx +2 -2
- package/apps/shared/notifications/apiErrors.ts +2 -2
- package/apps/shared/notifications/pushStatus.ts +1 -1
- package/apps/shared/notifications/store.ts +560 -560
- package/apps/shared/notifications/systemErrors.ts +1 -1
- package/apps/shared/search/LocalIndexLifecycle.tsx +120 -114
- package/apps/shared/search/crossMailboxSearch.ts +3 -3
- package/apps/shared/search/localIndexBuilder.ts +493 -481
- package/apps/shared/search/localIndexKey.ts +1 -1
- package/apps/shared/search/localIndexRpcClient.ts +150 -30
- package/apps/shared/search/localIndexTransport.ts +84 -0
- package/apps/shared/search/localIndexWorker.ts +1 -1
- package/apps/shared/search/searchTier2.ts +3 -3
- package/apps/shared/signing/enrollmentStorage.ts +33 -33
- package/apps/shared/signing/enrollmentTracker.ts +385 -385
- package/apps/shared/signing/enrollmentView.ts +251 -251
- package/apps/shared/signing/signingInfo.ts +1 -1
- package/apps/shared/signing/useNow.ts +19 -19
- package/apps/shared/signing/useSigningEnrollmentWatcher.ts +92 -90
- package/apps/shared/styles/app.css +387 -396
- package/apps/www/_layout.tsx +2 -2
- package/apps/www/calendar/index.tsx +583 -581
- package/apps/www/contacts/[uid].tsx +114 -112
- package/apps/www/contacts/index.tsx +19 -17
- package/apps/www/index.tsx +3021 -3012
- package/apps/www/messages/[uid].tsx +141 -139
- package/apps/www/settings/auto-reply/index.tsx +138 -136
- package/apps/www/settings/blocked-senders/index.tsx +305 -303
- package/apps/www/settings/encryption/index.tsx +1296 -1290
- package/apps/www/settings/filters/[uid].tsx +184 -179
- package/apps/www/settings/filters/_mailFilterRuleConfig.tsx +2 -2
- package/apps/www/settings/filters/index.tsx +105 -105
- package/apps/www/settings/filters/new/index.tsx +167 -165
- package/apps/www/settings/labels/index.tsx +209 -207
- package/apps/www/settings/privacy/index.tsx +499 -495
- package/apps/www/settings/profile/index.tsx +256 -251
- package/apps/www/settings/read-receipts/index.tsx +155 -150
- package/apps/www/settings/sharing/index.tsx +259 -259
- package/apps/www/settings/signatures/[uid].tsx +175 -175
- package/apps/www/settings/signatures/index.tsx +91 -91
- package/apps/www/settings/signatures/new/index.tsx +138 -138
- package/apps/www/settings/signatures/signatureDefaults.ts +1 -1
- package/apps/www/tasks/index.tsx +659 -654
- package/dist/apps/admin/_layout.d.ts +1 -1
- package/dist/apps/admin/_layout.js +1 -1
- package/dist/apps/admin/audit-log/index.d.ts +1 -1
- package/dist/apps/admin/audit-log/index.js +4 -4
- package/dist/apps/admin/branding/index.js +3 -3
- package/dist/apps/admin/data-requests/index.js +8 -8
- package/dist/apps/admin/distribution-lists/[uid].js +3 -3
- package/dist/apps/admin/distribution-lists/index.js +4 -4
- package/dist/apps/admin/distribution-lists/new/index.js +5 -5
- package/dist/apps/admin/domains/[uid].js +6 -6
- package/dist/apps/admin/domains/index.js +4 -4
- package/dist/apps/admin/domains/new/index.js +5 -5
- package/dist/apps/admin/encryption-policy/index.js +1 -1
- package/dist/apps/admin/escrow-scopes/[uid].d.ts +1 -1
- package/dist/apps/admin/escrow-scopes/[uid].js +7 -7
- package/dist/apps/admin/escrow-scopes/index.js +4 -4
- package/dist/apps/admin/escrow-scopes/new/index.js +5 -5
- package/dist/apps/admin/index.js +6 -6
- package/dist/apps/admin/ingest-queue/index.js +4 -4
- package/dist/apps/admin/mailbox-policy/index.js +1 -1
- package/dist/apps/admin/mailboxes/[uid].js +5 -5
- package/dist/apps/admin/quarantine/index.js +5 -5
- package/dist/apps/admin/retention-policy/index.js +3 -3
- package/dist/apps/admin/signing-certificates/index.js +5 -5
- package/dist/apps/admin/transport-rules/[uid].js +5 -5
- package/dist/apps/admin/transport-rules/_transportRuleConfig.d.ts +1 -1
- package/dist/apps/admin/transport-rules/_transportRuleConfig.js +2 -2
- package/dist/apps/admin/transport-rules/index.js +4 -4
- package/dist/apps/admin/transport-rules/new/index.js +5 -5
- package/dist/apps/escrow/_layout.d.ts +1 -1
- package/dist/apps/escrow/audit-log/index.d.ts +1 -1
- package/dist/apps/escrow/audit-log/index.js +4 -4
- package/dist/apps/escrow/index.js +4 -4
- package/dist/apps/escrow/matters/[uid].js +10 -10
- package/dist/apps/escrow/matters/new/index.js +6 -6
- package/dist/apps/shared/appearance/AppearanceForm.js +3 -3
- package/dist/apps/shared/appearance/AppearanceHead.js +1 -1
- package/dist/apps/shared/appearance/AppearanceProvider.d.ts +1 -1
- package/dist/apps/shared/appearance/AppearanceProvider.js +3 -3
- package/dist/apps/shared/appearance/appearanceCache.d.ts +1 -1
- package/dist/apps/shared/appearance/appearanceCache.js +1 -1
- package/dist/apps/shared/appearance/appearanceContext.d.ts +1 -1
- package/dist/apps/shared/appearance/theme.d.ts +1 -1
- package/dist/apps/shared/auth/adminAccess.js +1 -1
- package/dist/apps/shared/calendar/bookingPlugin.js +1 -1
- package/dist/apps/shared/calendar/calendarLiveUpdates.d.ts +1 -1
- package/dist/apps/shared/calendar/calendarReminders.d.ts +1 -1
- package/dist/apps/shared/calendar/useCalendarReminders.js +1 -1
- package/dist/apps/shared/components/admin/diagnostics/DiagnosticsManager.js +1 -1
- package/dist/apps/shared/components/admin/diagnostics/InstalledPlugins.d.ts +1 -1
- package/dist/apps/shared/components/admin/diagnostics/InstalledPlugins.js +2 -2
- package/dist/apps/shared/components/admin/diagnostics/LogsPanel.js +2 -2
- package/dist/apps/shared/components/admin/diagnostics/PackagesTable.js +1 -1
- package/dist/apps/shared/components/admin/diagnostics/RuntimePanel.js +1 -1
- package/dist/apps/shared/components/admin/diagnostics/SystemPanel.js +2 -2
- package/dist/apps/shared/components/admin/diagnostics/VersionsPanel.js +1 -1
- package/dist/apps/shared/components/admin/diagnostics/diagnosticsApi.js +1 -1
- package/dist/apps/shared/components/admin/diagnostics/format.js +1 -1
- package/dist/apps/shared/components/admin/diagnostics/logClient.js +1 -1
- package/dist/apps/shared/components/admin/distributionLists/MemberListCard.d.ts +1 -1
- package/dist/apps/shared/components/admin/distributionLists/MemberListCard.js +4 -4
- package/dist/apps/shared/components/admin/elevation.js +1 -1
- package/dist/apps/shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.js +3 -3
- package/dist/apps/shared/components/admin/layout/AdminShell.d.ts +1 -1
- package/dist/apps/shared/components/admin/layout/AdminShell.js +8 -8
- package/dist/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.d.ts +1 -1
- package/dist/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.js +6 -6
- package/dist/apps/shared/components/admin/mailboxes/EscrowScopeCard.d.ts +1 -1
- package/dist/apps/shared/components/admin/mailboxes/EscrowScopeCard.js +10 -8
- package/dist/apps/shared/components/admin/mailboxes/LeftoverMailboxesSection.js +4 -4
- package/dist/apps/shared/components/admin/mailboxes/MailboxTable.d.ts +1 -1
- package/dist/apps/shared/components/admin/mailboxes/ResourceSettingsCard.d.ts +1 -1
- package/dist/apps/shared/components/admin/mailboxes/ResourceSettingsCard.js +7 -5
- package/dist/apps/shared/components/admin/mailboxes/ShareAccessCard.js +5 -5
- package/dist/apps/shared/components/admin/settings/BrandingForm.d.ts +1 -1
- package/dist/apps/shared/components/admin/settings/BrandingForm.js +8 -8
- package/dist/apps/shared/components/admin/settings/DomainDnsSetup.d.ts +1 -1
- package/dist/apps/shared/components/admin/settings/DomainDnsSetup.js +5 -5
- package/dist/apps/shared/components/admin/settings/EncryptionPolicyForm.d.ts +1 -1
- package/dist/apps/shared/components/admin/settings/EncryptionPolicyForm.js +4 -4
- package/dist/apps/shared/components/admin/settings/LoadedSettingsForm.js +2 -2
- package/dist/apps/shared/components/admin/settings/MailboxCreateForm.d.ts +1 -1
- package/dist/apps/shared/components/admin/settings/MailboxCreateForm.js +14 -12
- package/dist/apps/shared/components/admin/settings/MailboxPolicyForm.d.ts +1 -1
- package/dist/apps/shared/components/admin/settings/MailboxPolicyForm.js +4 -4
- package/dist/apps/shared/components/admin/settings/PluginsManager.js +5 -5
- package/dist/apps/shared/components/admin/settings/RetentionPolicyForm.d.ts +1 -1
- package/dist/apps/shared/components/admin/settings/RetentionPolicyForm.js +5 -5
- package/dist/apps/shared/components/admin/setup/EscrowSetupStep.js +7 -7
- package/dist/apps/shared/components/admin/setup/SetupWizard.js +26 -22
- package/dist/apps/shared/components/admin/signOut.js +1 -1
- package/dist/apps/shared/components/admin/usePagedList.js +2 -2
- package/dist/apps/shared/components/calendar/AppointmentQuickForm.js +3 -3
- package/dist/apps/shared/components/calendar/CalendarListSidebar.d.ts +1 -1
- package/dist/apps/shared/components/calendar/CalendarListSidebar.js +2 -2
- package/dist/apps/shared/components/calendar/DescriptionEditor.js +1 -1
- package/dist/apps/shared/components/calendar/EventDescriptionView.js +1 -1
- package/dist/apps/shared/components/calendar/EventDetails.d.ts +2 -2
- package/dist/apps/shared/components/calendar/EventDetails.js +16 -14
- package/dist/apps/shared/components/calendar/EventEditor.d.ts +2 -2
- package/dist/apps/shared/components/calendar/EventEditor.js +22 -20
- package/dist/apps/shared/components/calendar/EventExpandedForm.js +2 -2
- package/dist/apps/shared/components/calendar/EventFormParts.d.ts +1 -1
- package/dist/apps/shared/components/calendar/EventFormParts.js +1 -1
- package/dist/apps/shared/components/calendar/EventModal.d.ts +2 -2
- package/dist/apps/shared/components/calendar/EventModal.js +3 -3
- package/dist/apps/shared/components/calendar/EventQuickForm.js +3 -3
- package/dist/apps/shared/components/calendar/EventShell.js +1 -1
- package/dist/apps/shared/components/calendar/FindATime.js +6 -4
- package/dist/apps/shared/components/calendar/GuestInput.d.ts +2 -2
- package/dist/apps/shared/components/calendar/MonthView.d.ts +1 -1
- package/dist/apps/shared/components/calendar/MonthView.js +1 -1
- package/dist/apps/shared/components/calendar/QuickCreateFaces.js +1 -1
- package/dist/apps/shared/components/calendar/QuickFaceFrame.js +1 -1
- package/dist/apps/shared/components/calendar/RecurrenceEditor.d.ts +1 -1
- package/dist/apps/shared/components/calendar/RecurrenceEditor.js +1 -1
- package/dist/apps/shared/components/calendar/RequestChangeForm.js +8 -6
- package/dist/apps/shared/components/calendar/ResourcePicker.d.ts +1 -1
- package/dist/apps/shared/components/calendar/ResourcePicker.js +7 -5
- package/dist/apps/shared/components/calendar/SplitDayView.d.ts +1 -1
- package/dist/apps/shared/components/calendar/TaskQuickForm.d.ts +1 -1
- package/dist/apps/shared/components/calendar/TaskQuickForm.js +9 -7
- package/dist/apps/shared/components/calendar/TimeGridView.d.ts +1 -1
- package/dist/apps/shared/components/calendar/TimeGridView.js +1 -1
- package/dist/apps/shared/components/calendar/allDay.d.ts +2 -2
- package/dist/apps/shared/components/calendar/allDay.js +1 -1
- package/dist/apps/shared/components/calendar/eventDialogFields.d.ts +2 -2
- package/dist/apps/shared/components/calendar/eventDialogFields.js +2 -2
- package/dist/apps/shared/components/calendar/eventForm.d.ts +3 -3
- package/dist/apps/shared/components/calendar/eventFormat.d.ts +1 -1
- package/dist/apps/shared/components/calendar/eventFormat.js +2 -2
- package/dist/apps/shared/components/calendar/findTimeGrid.d.ts +1 -1
- package/dist/apps/shared/components/calendar/findTimeGrid.js +1 -1
- package/dist/apps/shared/components/calendar/layout/CalendarShell.d.ts +1 -1
- package/dist/apps/shared/components/calendar/layout/CalendarShell.js +11 -9
- package/dist/apps/shared/components/contacts/ContactDetailPane.d.ts +1 -1
- package/dist/apps/shared/components/contacts/ContactDetailPane.js +2 -2
- package/dist/apps/shared/components/contacts/ContactForm.d.ts +2 -2
- package/dist/apps/shared/components/contacts/ContactForm.js +12 -8
- package/dist/apps/shared/components/contacts/ContactsSidebar.d.ts +1 -1
- package/dist/apps/shared/components/contacts/ContactsSidebar.js +7 -5
- package/dist/apps/shared/components/contacts/KeyChangeReview.d.ts +1 -1
- package/dist/apps/shared/components/contacts/KeyChangeReview.js +5 -5
- package/dist/apps/shared/components/contacts/contactKeys.d.ts +2 -2
- package/dist/apps/shared/components/contacts/contactKeys.js +2 -2
- package/dist/apps/shared/components/contacts/layout/ContactsShell.d.ts +1 -1
- package/dist/apps/shared/components/contacts/layout/ContactsShell.js +9 -7
- package/dist/apps/shared/components/escrow/layout/EscrowShell.js +7 -7
- package/dist/apps/shared/components/forms/StringListField.js +1 -1
- package/dist/apps/shared/components/layout/AppShell.d.ts +2 -2
- package/dist/apps/shared/components/layout/AppShell.js +14 -10
- package/dist/apps/shared/components/layout/BrandingChrome.d.ts +1 -1
- package/dist/apps/shared/components/layout/KeyEnrollmentGate.d.ts +1 -1
- package/dist/apps/shared/components/layout/KeyEnrollmentGate.js +9 -9
- package/dist/apps/shared/components/layout/MailboxProvisioning.js +8 -6
- package/dist/apps/shared/components/layout/RecoveryCodeUnlock.d.ts +1 -1
- package/dist/apps/shared/components/layout/RecoveryCodeUnlock.js +9 -9
- package/dist/apps/shared/components/layout/UnlockPromptProvider.d.ts +2 -2
- package/dist/apps/shared/components/layout/UnlockPromptProvider.js +5 -5
- package/dist/apps/shared/components/layout/UserMenu.js +6 -4
- package/dist/apps/shared/components/mail/ConversationList.d.ts +2 -2
- package/dist/apps/shared/components/mail/ConversationList.js +6 -4
- package/dist/apps/shared/components/mail/ConversationThreadPane.d.ts +3 -3
- package/dist/apps/shared/components/mail/ConversationThreadPane.js +13 -11
- package/dist/apps/shared/components/mail/InviteCard.js +10 -8
- package/dist/apps/shared/components/mail/LazyReadingPane.js +2 -2
- package/dist/apps/shared/components/mail/MailAddress.d.ts +1 -1
- package/dist/apps/shared/components/mail/MailAddress.js +1 -1
- package/dist/apps/shared/components/mail/MailListToolbar.d.ts +2 -2
- package/dist/apps/shared/components/mail/MailSelectionBar.d.ts +2 -2
- package/dist/apps/shared/components/mail/MenuButton.js +1 -1
- package/dist/apps/shared/components/mail/MessageDetailPane.d.ts +3 -3
- package/dist/apps/shared/components/mail/MessageDetailPane.js +30 -28
- package/dist/apps/shared/components/mail/MoveToFolderDialog.d.ts +1 -1
- package/dist/apps/shared/components/mail/MoveToFolderDialog.js +8 -6
- package/dist/apps/shared/components/mail/OutboxRowStatus.d.ts +1 -1
- package/dist/apps/shared/components/mail/PermanentDeleteDialog.js +2 -2
- package/dist/apps/shared/components/mail/compose/ComposeContext.d.ts +10 -1
- package/dist/apps/shared/components/mail/compose/ComposeContext.js +4 -2
- package/dist/apps/shared/components/mail/compose/ComposeWindow.d.ts +7 -1
- package/dist/apps/shared/components/mail/compose/ComposeWindow.js +54 -31
- package/dist/apps/shared/components/mail/compose/ComposeWindowPlaceholder.js +4 -4
- package/dist/apps/shared/components/mail/compose/EmojiPicker.js +2 -2
- package/dist/apps/shared/components/mail/compose/GifPicker.js +3 -3
- package/dist/apps/shared/components/mail/compose/RecipientInput.d.ts +1 -1
- package/dist/apps/shared/components/mail/compose/RecipientInput.js +1 -1
- package/dist/apps/shared/components/mail/compose/ScheduleSendPicker.js +1 -1
- package/dist/apps/shared/components/mail/compose/encryptionRequirement.d.ts +2 -2
- package/dist/apps/shared/components/mail/compose/encryptionRequirement.js +1 -1
- package/dist/apps/shared/components/mail/compose/quotedBody.d.ts +3 -3
- package/dist/apps/shared/components/mail/compose/quotedBody.js +3 -3
- package/dist/apps/shared/components/mail/compose/recipients.d.ts +1 -1
- package/dist/apps/shared/components/mail/invite/InviteRowChip.d.ts +1 -1
- package/dist/apps/shared/components/mail/invite/InviteRsvpPopover.d.ts +1 -1
- package/dist/apps/shared/components/mail/invite/InviteRsvpPopover.js +9 -7
- package/dist/apps/shared/components/mail/invite/InviteTimeline.d.ts +1 -1
- package/dist/apps/shared/components/mail/invite/ProposeTimeForm.d.ts +1 -1
- package/dist/apps/shared/components/mail/invite/ProposeTimeForm.js +1 -1
- package/dist/apps/shared/components/mail/invite/inviteFormat.d.ts +2 -2
- package/dist/apps/shared/components/mail/invite/inviteStore.d.ts +9 -3
- package/dist/apps/shared/components/mail/invite/inviteStore.js +13 -6
- package/dist/apps/shared/components/mail/labelMenu.d.ts +1 -1
- package/dist/apps/shared/components/mail/labelMenu.js +9 -7
- package/dist/apps/shared/components/mail/layout/MailShell.d.ts +1 -1
- package/dist/apps/shared/components/mail/layout/MailShell.js +3 -3
- package/dist/apps/shared/components/mail/listPreferences.d.ts +2 -2
- package/dist/apps/shared/components/mail/listPreferences.js +1 -1
- package/dist/apps/shared/components/mail/pinnedSigners.d.ts +12 -5
- package/dist/apps/shared/components/mail/pinnedSigners.js +37 -18
- package/dist/apps/shared/components/mail/reading/EncryptedBody.js +2 -2
- package/dist/apps/shared/components/mail/reading/MessageBody.d.ts +2 -2
- package/dist/apps/shared/components/mail/reading/MessageBody.js +4 -4
- package/dist/apps/shared/components/mail/reading/MessageCard.d.ts +1 -1
- package/dist/apps/shared/components/mail/reading/MessageCard.js +2 -2
- package/dist/apps/shared/components/mail/reading/MessageSourceDialog.js +3 -3
- package/dist/apps/shared/components/mail/reading/PendingMessageCard.js +1 -1
- package/dist/apps/shared/components/mail/reading/bodyContent.d.ts +2 -2
- package/dist/apps/shared/components/mail/reading/bodyContent.js +2 -2
- package/dist/apps/shared/components/mail/reading/bodyHtml.js +1 -1
- package/dist/apps/shared/components/mail/reading/messageExport.js +1 -1
- package/dist/apps/shared/components/mail/reading/printMessage.d.ts +2 -2
- package/dist/apps/shared/components/mail/reading/printMessage.js +11 -11
- package/dist/apps/shared/components/mail/reading/useMessageActions.d.ts +1 -1
- package/dist/apps/shared/components/mail/reading/useMessageActions.js +18 -16
- package/dist/apps/shared/components/mail/unreadStyle.d.ts +1 -1
- package/dist/apps/shared/components/mail/verificationSeals.d.ts +27 -6
- package/dist/apps/shared/components/mail/verificationSeals.js +15 -11
- package/dist/apps/shared/components/mail/writableMailboxes.d.ts +8 -3
- package/dist/apps/shared/components/mail/writableMailboxes.js +12 -6
- package/dist/apps/shared/components/rules/RuleBuilder.js +1 -1
- package/dist/apps/shared/components/settings/SigningCertificateCard.d.ts +1 -1
- package/dist/apps/shared/components/settings/SigningCertificateCard.js +3 -3
- package/dist/apps/shared/components/settings/layout/SettingsShell.d.ts +1 -1
- package/dist/apps/shared/components/settings/layout/SettingsShell.js +8 -6
- package/dist/apps/shared/components/sharing/PrincipalListField.d.ts +1 -1
- package/dist/apps/shared/components/sharing/PrincipalPicker.d.ts +1 -1
- package/dist/apps/shared/components/sharing/PrincipalPicker.js +1 -1
- package/dist/apps/shared/components/sharing/PrincipalResolver.d.ts +1 -1
- package/dist/apps/shared/components/sharing/PrincipalResolver.js +3 -3
- package/dist/apps/shared/components/tasks/TasksSidebar.d.ts +1 -1
- package/dist/apps/shared/components/tasks/TasksSidebar.js +7 -5
- package/dist/apps/shared/components/tasks/layout/TasksShell.d.ts +1 -1
- package/dist/apps/shared/components/tasks/layout/TasksShell.js +9 -7
- package/dist/apps/shared/keyboard/ShortcutsDialog.js +1 -1
- package/dist/apps/shared/mail/findWellKnownFolderUid.d.ts +5 -3
- package/dist/apps/shared/mail/findWellKnownFolderUid.js +5 -4
- package/dist/apps/shared/mail/folderCounts.d.ts +1 -1
- package/dist/apps/shared/mail/folderCounts.js +6 -4
- package/dist/apps/shared/mail/folderOfType.d.ts +3 -2
- package/dist/apps/shared/mail/folderOfType.js +4 -4
- package/dist/apps/shared/mail/folderTree.d.ts +1 -1
- package/dist/apps/shared/mail/listSnapshots.d.ts +2 -2
- package/dist/apps/shared/mail/messageReadState.d.ts +4 -3
- package/dist/apps/shared/mail/messageReadState.js +5 -5
- package/dist/apps/shared/mail/newMailNotifications.d.ts +1 -1
- package/dist/apps/shared/mail/newMailNotifications.js +1 -1
- package/dist/apps/shared/mail/outbox/composeBridge.d.ts +2 -2
- package/dist/apps/shared/mail/outbox/outboxState.d.ts +1 -1
- package/dist/apps/shared/mail/outbox/outgoingReplies.d.ts +2 -2
- package/dist/apps/shared/mail/outbox/sendDecision.d.ts +2 -2
- package/dist/apps/shared/mail/outbox/sendDecision.js +1 -1
- package/dist/apps/shared/mail/outbox/sendJob.d.ts +10 -6
- package/dist/apps/shared/mail/outbox/sendJob.js +39 -35
- package/dist/apps/shared/mail/outbox/sendOutcomes.d.ts +6 -3
- package/dist/apps/shared/mail/outbox/sendOutcomes.js +19 -15
- package/dist/apps/shared/mail/outbox/useOutboxStatus.js +5 -3
- package/dist/apps/shared/mail/permanentDelete.d.ts +4 -3
- package/dist/apps/shared/mail/permanentDelete.js +8 -8
- package/dist/apps/shared/mail/primaryMailbox.d.ts +1 -1
- package/dist/apps/shared/mail/primaryMailbox.js +1 -1
- package/dist/apps/shared/mail/reportNotices.d.ts +1 -1
- package/dist/apps/shared/mail/senderBlocking.d.ts +5 -4
- package/dist/apps/shared/mail/senderBlocking.js +19 -16
- package/dist/apps/shared/mail/useMailConnection.d.ts +1 -1
- package/dist/apps/shared/mail/useMailConnection.js +12 -10
- package/dist/apps/shared/mail/useMailLiveUpdates.d.ts +2 -2
- package/dist/apps/shared/mail/useMailLiveUpdates.js +2 -2
- package/dist/apps/shared/mail/useMailboxUpdateAccess.d.ts +3 -2
- package/dist/apps/shared/mail/useMailboxUpdateAccess.js +31 -10
- package/dist/apps/shared/mail/useMarkMessageRead.d.ts +1 -1
- package/dist/apps/shared/mail/useMarkMessageRead.js +5 -3
- package/dist/apps/shared/mail/useNewMailNotifications.d.ts +1 -1
- package/dist/apps/shared/mail/usePermanentDelete.d.ts +1 -1
- package/dist/apps/shared/mail/usePermanentDelete.js +4 -2
- package/dist/apps/shared/navigation/pageTitle.d.ts +1 -1
- package/dist/apps/shared/notifications/NotificationCenter.js +1 -1
- package/dist/apps/shared/notifications/NotificationHistoryDialog.js +2 -2
- package/dist/apps/shared/notifications/apiErrors.js +2 -2
- package/dist/apps/shared/notifications/pushStatus.js +1 -1
- package/dist/apps/shared/notifications/systemErrors.js +1 -1
- package/dist/apps/shared/search/LocalIndexLifecycle.d.ts +2 -2
- package/dist/apps/shared/search/LocalIndexLifecycle.js +15 -10
- package/dist/apps/shared/search/crossMailboxSearch.d.ts +1 -1
- package/dist/apps/shared/search/crossMailboxSearch.js +2 -2
- package/dist/apps/shared/search/localIndexBuilder.d.ts +4 -3
- package/dist/apps/shared/search/localIndexBuilder.js +15 -12
- package/dist/apps/shared/search/localIndexKey.js +1 -1
- package/dist/apps/shared/search/localIndexRpcClient.d.ts +43 -31
- package/dist/apps/shared/search/localIndexRpcClient.js +113 -20
- package/dist/apps/shared/search/localIndexTransport.d.ts +77 -0
- package/dist/apps/shared/search/localIndexTransport.js +1 -0
- package/dist/apps/shared/search/localIndexWorker.d.ts +1 -1
- package/dist/apps/shared/search/searchTier2.d.ts +3 -3
- package/dist/apps/shared/signing/enrollmentTracker.d.ts +1 -1
- package/dist/apps/shared/signing/enrollmentTracker.js +3 -3
- package/dist/apps/shared/signing/enrollmentView.d.ts +1 -1
- package/dist/apps/shared/signing/signingInfo.d.ts +1 -1
- package/dist/apps/shared/signing/signingInfo.js +1 -1
- package/dist/apps/shared/signing/useSigningEnrollmentWatcher.d.ts +1 -1
- package/dist/apps/shared/signing/useSigningEnrollmentWatcher.js +5 -3
- package/dist/apps/shared/styles/app.css +387 -396
- package/dist/apps/www/_layout.d.ts +1 -1
- package/dist/apps/www/_layout.js +1 -1
- package/dist/apps/www/calendar/index.d.ts +1 -1
- package/dist/apps/www/calendar/index.js +20 -18
- package/dist/apps/www/contacts/[uid].d.ts +1 -1
- package/dist/apps/www/contacts/[uid].js +8 -6
- package/dist/apps/www/contacts/index.d.ts +1 -1
- package/dist/apps/www/contacts/index.js +19 -17
- package/dist/apps/www/index.d.ts +1 -1
- package/dist/apps/www/index.js +54 -50
- package/dist/apps/www/messages/[uid].d.ts +1 -1
- package/dist/apps/www/messages/[uid].js +11 -9
- package/dist/apps/www/settings/appearance/index.d.ts +1 -1
- package/dist/apps/www/settings/auto-reply/index.d.ts +1 -1
- package/dist/apps/www/settings/auto-reply/index.js +6 -4
- package/dist/apps/www/settings/blocked-senders/index.d.ts +1 -1
- package/dist/apps/www/settings/blocked-senders/index.js +6 -4
- package/dist/apps/www/settings/encryption/index.d.ts +1 -1
- package/dist/apps/www/settings/encryption/index.js +31 -29
- package/dist/apps/www/settings/filters/[uid].d.ts +1 -1
- package/dist/apps/www/settings/filters/[uid].js +11 -9
- package/dist/apps/www/settings/filters/_mailFilterRuleConfig.d.ts +2 -2
- package/dist/apps/www/settings/filters/index.d.ts +1 -1
- package/dist/apps/www/settings/filters/index.js +4 -4
- package/dist/apps/www/settings/filters/new/index.d.ts +1 -1
- package/dist/apps/www/settings/filters/new/index.js +10 -8
- package/dist/apps/www/settings/labels/index.d.ts +1 -1
- package/dist/apps/www/settings/labels/index.js +12 -10
- package/dist/apps/www/settings/privacy/index.d.ts +1 -1
- package/dist/apps/www/settings/privacy/index.js +18 -14
- package/dist/apps/www/settings/profile/index.d.ts +1 -1
- package/dist/apps/www/settings/profile/index.js +7 -5
- package/dist/apps/www/settings/read-receipts/index.d.ts +1 -1
- package/dist/apps/www/settings/read-receipts/index.js +5 -3
- package/dist/apps/www/settings/sharing/index.d.ts +1 -1
- package/dist/apps/www/settings/sharing/index.js +6 -6
- package/dist/apps/www/settings/signatures/[uid].d.ts +1 -1
- package/dist/apps/www/settings/signatures/[uid].js +5 -5
- package/dist/apps/www/settings/signatures/index.d.ts +1 -1
- package/dist/apps/www/settings/signatures/index.js +4 -4
- package/dist/apps/www/settings/signatures/new/index.d.ts +1 -1
- package/dist/apps/www/settings/signatures/new/index.js +4 -4
- package/dist/apps/www/settings/signatures/signatureDefaults.d.ts +1 -1
- package/dist/apps/www/settings/signatures/signatureDefaults.js +1 -1
- package/dist/apps/www/tasks/index.d.ts +1 -1
- package/dist/apps/www/tasks/index.js +17 -15
- package/dist/lib/admin/auditLogApi.d.ts +32 -0
- package/dist/lib/admin/auditLogApi.js +27 -0
- package/dist/lib/admin/distributionListsApi.d.ts +47 -0
- package/dist/lib/admin/distributionListsApi.js +34 -0
- package/dist/lib/admin/domainsApi.d.ts +75 -0
- package/dist/lib/admin/domainsApi.js +44 -0
- package/dist/lib/admin/escrowAccessRequestsApi.d.ts +79 -0
- package/dist/lib/admin/escrowAccessRequestsApi.js +46 -0
- package/dist/lib/admin/escrowAuditLogApi.d.ts +66 -0
- package/dist/lib/admin/escrowAuditLogApi.js +31 -0
- package/dist/lib/admin/escrowScopesApi.d.ts +78 -0
- package/dist/lib/admin/escrowScopesApi.js +50 -0
- package/dist/lib/admin/leftoverMailboxApi.d.ts +73 -0
- package/dist/lib/admin/leftoverMailboxApi.js +67 -0
- package/dist/lib/admin/mailboxPolicyApi.d.ts +27 -0
- package/dist/lib/admin/mailboxPolicyApi.js +21 -0
- package/dist/lib/admin/matterExportApi.d.ts +40 -0
- package/dist/lib/admin/matterExportApi.js +39 -0
- package/dist/lib/admin/matterSearchApi.d.ts +20 -0
- package/dist/lib/admin/matterSearchApi.js +27 -0
- package/dist/lib/admin/mattersApi.d.ts +56 -0
- package/dist/lib/admin/mattersApi.js +40 -0
- package/dist/lib/admin/pluginsApi.d.ts +265 -0
- package/dist/lib/admin/pluginsApi.js +102 -0
- package/dist/lib/admin/retentionPolicyApi.d.ts +32 -0
- package/dist/lib/admin/retentionPolicyApi.js +29 -0
- package/dist/lib/admin/setupApi.d.ts +23 -0
- package/dist/lib/admin/setupApi.js +26 -0
- package/dist/lib/admin/transportRulesApi.d.ts +66 -0
- package/dist/lib/admin/transportRulesApi.js +43 -0
- package/dist/lib/appearance/preferencesApi.d.ts +128 -0
- package/dist/lib/appearance/preferencesApi.js +257 -0
- package/dist/lib/auth/profileApi.d.ts +33 -0
- package/dist/lib/auth/profileApi.js +52 -0
- package/dist/lib/auth/session.d.ts +70 -0
- package/dist/lib/auth/session.js +210 -0
- package/dist/lib/branding/brandingApi.d.ts +47 -0
- package/dist/lib/branding/brandingApi.js +61 -0
- package/dist/lib/branding/useBranding.d.ts +23 -0
- package/dist/lib/branding/useBranding.js +65 -0
- package/dist/lib/calendar/calendarApi.d.ts +230 -0
- package/dist/lib/calendar/calendarApi.js +106 -0
- package/dist/lib/calendar/calendarColors.d.ts +18 -0
- package/dist/lib/calendar/calendarColors.js +39 -0
- package/dist/lib/calendar/calendarDragIds.d.ts +42 -0
- package/dist/lib/calendar/calendarDragIds.js +92 -0
- package/dist/lib/calendar/calendarMutations.d.ts +68 -0
- package/dist/lib/calendar/calendarMutations.js +190 -0
- package/dist/lib/calendar/eventDescription.d.ts +43 -0
- package/dist/lib/calendar/eventDescription.js +195 -0
- package/dist/lib/calendar/freeBusyApi.d.ts +100 -0
- package/dist/lib/calendar/freeBusyApi.js +119 -0
- package/dist/lib/calendar/inviteApi.d.ts +121 -0
- package/dist/lib/calendar/inviteApi.js +54 -0
- package/dist/lib/calendar/recurrence.d.ts +60 -0
- package/dist/lib/calendar/recurrence.js +225 -0
- package/dist/lib/components/avatar/ContactAvatar.d.ts +8 -0
- package/dist/lib/components/avatar/ContactAvatar.js +26 -0
- package/dist/lib/components/buttons/Button.d.ts +9 -0
- package/dist/lib/components/buttons/Button.js +14 -0
- package/dist/lib/components/buttons/CopyButton.d.ts +18 -0
- package/dist/lib/components/buttons/CopyButton.js +14 -0
- package/dist/lib/components/feedback/Alert.d.ts +4 -0
- package/dist/lib/components/feedback/Alert.js +4 -0
- package/dist/lib/components/feedback/Skeleton.d.ts +21 -0
- package/dist/lib/components/feedback/Skeleton.js +14 -0
- package/dist/lib/components/forms/FormField.d.ts +8 -0
- package/dist/lib/components/forms/FormField.js +5 -0
- package/dist/lib/components/navigation/BottomTabBar.d.ts +27 -0
- package/dist/lib/components/navigation/BottomTabBar.js +32 -0
- package/dist/lib/components/overlays/Drawer.d.ts +20 -0
- package/dist/lib/components/overlays/Drawer.js +27 -0
- package/dist/lib/components/overlays/Modal.d.ts +17 -0
- package/dist/lib/components/overlays/Modal.js +25 -0
- package/dist/lib/components/overlays/PopoverPortal.d.ts +23 -0
- package/dist/lib/components/overlays/PopoverPortal.js +53 -0
- package/dist/lib/components/overlays/overlayStack.d.ts +31 -0
- package/dist/lib/components/overlays/overlayStack.js +143 -0
- package/dist/lib/components/pickers/MiniDatePicker.d.ts +13 -0
- package/dist/lib/components/pickers/MiniDatePicker.js +42 -0
- package/dist/lib/contacts/contactsApi.d.ts +174 -0
- package/dist/lib/contacts/contactsApi.js +139 -0
- package/dist/lib/contacts/vcard.d.ts +23 -0
- package/dist/lib/contacts/vcard.js +179 -0
- package/dist/lib/crypto/composeSecurity.d.ts +67 -0
- package/dist/lib/crypto/composeSecurity.js +93 -0
- package/dist/lib/crypto/encoding.d.ts +9 -0
- package/dist/lib/crypto/encoding.js +32 -0
- package/dist/lib/crypto/escrowKeys.d.ts +25 -0
- package/dist/lib/crypto/escrowKeys.js +59 -0
- package/dist/lib/crypto/idleTimeout.d.ts +16 -0
- package/dist/lib/crypto/idleTimeout.js +47 -0
- package/dist/lib/crypto/keySession.d.ts +159 -0
- package/dist/lib/crypto/keySession.js +367 -0
- package/dist/lib/crypto/keys.d.ts +50 -0
- package/dist/lib/crypto/keys.js +89 -0
- package/dist/lib/crypto/keyvaultApi.d.ts +394 -0
- package/dist/lib/crypto/keyvaultApi.js +350 -0
- package/dist/lib/crypto/masterKey.d.ts +59 -0
- package/dist/lib/crypto/masterKey.js +104 -0
- package/dist/lib/crypto/masterKeyWraps.d.ts +108 -0
- package/dist/lib/crypto/masterKeyWraps.js +228 -0
- package/dist/lib/crypto/messageSecurity.d.ts +248 -0
- package/dist/lib/crypto/messageSecurity.js +367 -0
- package/dist/lib/crypto/mime.d.ts +143 -0
- package/dist/lib/crypto/mime.js +504 -0
- package/dist/lib/crypto/passkeyUnlock.d.ts +20 -0
- package/dist/lib/crypto/passkeyUnlock.js +79 -0
- package/dist/lib/crypto/passwordUnlock.d.ts +39 -0
- package/dist/lib/crypto/passwordUnlock.js +73 -0
- package/dist/lib/crypto/recoveryCode.d.ts +28 -0
- package/dist/lib/crypto/recoveryCode.js +86 -0
- package/dist/lib/crypto/signingProviderApi.d.ts +95 -0
- package/dist/lib/crypto/signingProviderApi.js +60 -0
- package/dist/lib/crypto/smime.d.ts +128 -0
- package/dist/lib/crypto/smime.js +402 -0
- package/dist/lib/crypto/smimeMessage.d.ts +132 -0
- package/dist/lib/crypto/smimeMessage.js +341 -0
- package/dist/lib/crypto/useIdleKeyTimeout.d.ts +10 -0
- package/dist/lib/crypto/useIdleKeyTimeout.js +163 -0
- package/dist/lib/crypto/verificationSeal.d.ts +56 -0
- package/dist/lib/crypto/verificationSeal.js +220 -0
- package/dist/lib/mail/compose/composeQuoting.d.ts +88 -0
- package/dist/lib/mail/compose/composeQuoting.js +204 -0
- package/dist/lib/mail/compose/emojiData.d.ts +9 -0
- package/dist/lib/mail/compose/emojiData.js +18 -0
- package/dist/lib/mail/compose/giphyApi.d.ts +17 -0
- package/dist/lib/mail/compose/giphyApi.js +20 -0
- package/dist/lib/mail/conversationsApi.d.ts +80 -0
- package/dist/lib/mail/conversationsApi.js +44 -0
- package/dist/lib/mail/dataExportApi.d.ts +50 -0
- package/dist/lib/mail/dataExportApi.js +40 -0
- package/dist/lib/mail/directoryApi.d.ts +49 -0
- package/dist/lib/mail/directoryApi.js +101 -0
- package/dist/lib/mail/erasureRequestApi.d.ts +45 -0
- package/dist/lib/mail/erasureRequestApi.js +43 -0
- package/dist/lib/mail/flaggedMessages.d.ts +22 -0
- package/dist/lib/mail/flaggedMessages.js +73 -0
- package/dist/lib/mail/focusedInboxOverridesApi.d.ts +37 -0
- package/dist/lib/mail/focusedInboxOverridesApi.js +34 -0
- package/dist/lib/mail/labelsApi.d.ts +47 -0
- package/dist/lib/mail/labelsApi.js +38 -0
- package/dist/lib/mail/mailAddress.d.ts +27 -0
- package/dist/lib/mail/mailAddress.js +54 -0
- package/dist/lib/mail/mailApi.d.ts +850 -0
- package/dist/lib/mail/mailApi.js +602 -0
- package/dist/lib/mail/mailDetailHooks.d.ts +10 -0
- package/dist/lib/mail/mailDetailHooks.js +53 -0
- package/dist/lib/mail/mailFilterRulesApi.d.ts +76 -0
- package/dist/lib/mail/mailFilterRulesApi.js +45 -0
- package/dist/lib/mail/mailSignaturesApi.d.ts +49 -0
- package/dist/lib/mail/mailSignaturesApi.js +42 -0
- package/dist/lib/mail/mailboxAccessApi.d.ts +74 -0
- package/dist/lib/mail/mailboxAccessApi.js +56 -0
- package/dist/lib/mail/mailboxImportApi.d.ts +53 -0
- package/dist/lib/mail/mailboxImportApi.js +51 -0
- package/dist/lib/mail/messageBodySanitizer.d.ts +16 -0
- package/dist/lib/mail/messageBodySanitizer.js +118 -0
- package/dist/lib/mail/pushClient.d.ts +106 -0
- package/dist/lib/mail/pushClient.js +317 -0
- package/dist/lib/mail/sendEvents.d.ts +33 -0
- package/dist/lib/mail/sendEvents.js +44 -0
- package/dist/lib/mail/sendFailure.d.ts +16 -0
- package/dist/lib/mail/sendFailure.js +86 -0
- package/dist/lib/mail/senderListsApi.d.ts +69 -0
- package/dist/lib/mail/senderListsApi.js +126 -0
- package/dist/lib/search/queryGrammar.d.ts +51 -0
- package/dist/lib/search/queryGrammar.js +111 -0
- package/dist/lib/search/searchApi.d.ts +100 -0
- package/dist/lib/search/searchApi.js +107 -0
- package/dist/lib/search/searchScoring.d.ts +34 -0
- package/dist/lib/search/searchScoring.js +44 -0
- package/dist/lib/search/searchTier3.d.ts +64 -0
- package/dist/lib/search/searchTier3.js +335 -0
- package/dist/lib/tasks/tasksApi.d.ts +85 -0
- package/dist/lib/tasks/tasksApi.js +55 -0
- package/dist/lib/util/api.d.ts +160 -0
- package/dist/lib/util/api.js +251 -0
- package/dist/lib/util/apiClientContext.d.ts +7 -0
- package/dist/lib/util/apiClientContext.js +32 -0
- package/dist/lib/util/apiQuery.d.ts +32 -0
- package/dist/lib/util/apiQuery.js +42 -0
- package/dist/lib/util/clipboard.d.ts +7 -0
- package/dist/lib/util/clipboard.js +56 -0
- package/dist/lib/util/dateInput.d.ts +6 -0
- package/dist/lib/util/dateInput.js +14 -0
- package/dist/lib/util/timeZone.d.ts +12 -0
- package/dist/lib/util/timeZone.js +32 -0
- package/dist/lib/util/useCopyToClipboard.d.ts +12 -0
- package/dist/lib/util/useCopyToClipboard.js +34 -0
- package/dist/lib/util/useIsMobile.d.ts +10 -0
- package/dist/lib/util/useIsMobile.js +29 -0
- package/dist/lib/videoconf/videoMeetingsApi.d.ts +116 -0
- package/dist/lib/videoconf/videoMeetingsApi.js +49 -0
- package/lib/admin/auditLogApi.ts +48 -0
- package/lib/admin/distributionListsApi.ts +77 -0
- package/lib/admin/domainsApi.ts +113 -0
- package/lib/admin/escrowAccessRequestsApi.ts +110 -0
- package/lib/admin/escrowAuditLogApi.ts +101 -0
- package/lib/admin/escrowScopesApi.ts +112 -0
- package/lib/admin/leftoverMailboxApi.ts +120 -0
- package/lib/admin/mailboxPolicyApi.ts +38 -0
- package/lib/admin/matterExportApi.ts +62 -0
- package/lib/admin/matterSearchApi.ts +35 -0
- package/lib/admin/mattersApi.ts +86 -0
- package/lib/admin/pluginsApi.ts +339 -0
- package/lib/admin/retentionPolicyApi.ts +46 -0
- package/lib/admin/setupApi.ts +40 -0
- package/lib/admin/transportRulesApi.ts +106 -0
- package/lib/appearance/preferencesApi.ts +335 -0
- package/lib/auth/profileApi.ts +77 -0
- package/lib/auth/session.ts +238 -0
- package/lib/branding/brandingApi.ts +97 -0
- package/lib/branding/useBranding.ts +81 -0
- package/lib/calendar/calendarApi.ts +309 -0
- package/lib/calendar/calendarColors.ts +45 -0
- package/lib/calendar/calendarDragIds.ts +112 -0
- package/lib/calendar/calendarMutations.ts +236 -0
- package/lib/calendar/eventDescription.ts +211 -0
- package/lib/calendar/freeBusyApi.ts +197 -0
- package/lib/calendar/inviteApi.ts +154 -0
- package/lib/calendar/recurrence.ts +285 -0
- package/lib/components/avatar/ContactAvatar.tsx +47 -0
- package/lib/components/buttons/Button.tsx +40 -0
- package/lib/components/buttons/CopyButton.tsx +45 -0
- package/lib/components/feedback/Alert.tsx +13 -0
- package/lib/components/feedback/Skeleton.tsx +38 -0
- package/lib/components/forms/FormField.tsx +23 -0
- package/lib/components/navigation/BottomTabBar.tsx +66 -0
- package/lib/components/overlays/Drawer.tsx +72 -0
- package/lib/components/overlays/Modal.tsx +64 -0
- package/lib/components/overlays/PopoverPortal.tsx +74 -0
- package/lib/components/overlays/overlayStack.ts +165 -0
- package/lib/components/pickers/MiniDatePicker.tsx +90 -0
- package/lib/contacts/contactsApi.ts +289 -0
- package/lib/contacts/vcard.ts +197 -0
- package/lib/crypto/composeSecurity.ts +140 -0
- package/lib/crypto/encoding.ts +36 -0
- package/lib/crypto/escrowKeys.ts +74 -0
- package/lib/crypto/idleTimeout.ts +49 -0
- package/lib/crypto/keySession.ts +483 -0
- package/lib/crypto/keys.ts +105 -0
- package/lib/crypto/keyvaultApi.ts +674 -0
- package/lib/crypto/masterKey.ts +134 -0
- package/lib/crypto/masterKeyWraps.ts +269 -0
- package/lib/crypto/messageSecurity.ts +600 -0
- package/lib/crypto/mime.ts +568 -0
- package/lib/crypto/passkeyUnlock.ts +103 -0
- package/lib/crypto/passwordUnlock.ts +98 -0
- package/lib/crypto/recoveryCode.ts +93 -0
- package/lib/crypto/signingProviderApi.ts +135 -0
- package/lib/crypto/smime.ts +459 -0
- package/lib/crypto/smimeMessage.ts +491 -0
- package/lib/crypto/useIdleKeyTimeout.ts +176 -0
- package/lib/crypto/verificationSeal.ts +288 -0
- package/lib/mail/compose/composeQuoting.ts +243 -0
- package/lib/mail/compose/emojiData.ts +34 -0
- package/lib/mail/compose/giphyApi.ts +31 -0
- package/lib/mail/conversationsApi.ts +123 -0
- package/lib/mail/dataExportApi.ts +73 -0
- package/lib/mail/directoryApi.ts +161 -0
- package/lib/mail/erasureRequestApi.ts +71 -0
- package/lib/mail/flaggedMessages.ts +80 -0
- package/lib/mail/focusedInboxOverridesApi.ts +64 -0
- package/lib/mail/labelsApi.ts +71 -0
- package/lib/mail/mailAddress.ts +76 -0
- package/lib/mail/mailApi.ts +1286 -0
- package/lib/mail/mailDetailHooks.ts +57 -0
- package/lib/mail/mailFilterRulesApi.ts +116 -0
- package/lib/mail/mailSignaturesApi.ts +84 -0
- package/lib/mail/mailboxAccessApi.ts +106 -0
- package/lib/mail/mailboxImportApi.ts +87 -0
- package/lib/mail/messageBodySanitizer.ts +129 -0
- package/lib/mail/pushClient.ts +375 -0
- package/lib/mail/sendEvents.ts +72 -0
- package/lib/mail/sendFailure.ts +98 -0
- package/lib/mail/senderListsApi.ts +161 -0
- package/lib/search/queryGrammar.ts +179 -0
- package/lib/search/searchApi.ts +199 -0
- package/lib/search/searchScoring.ts +46 -0
- package/lib/search/searchTier3.ts +406 -0
- package/lib/tasks/tasksApi.ts +128 -0
- package/lib/util/api.ts +318 -0
- package/lib/util/apiClientContext.ts +35 -0
- package/lib/util/apiQuery.ts +61 -0
- package/lib/util/clipboard.ts +55 -0
- package/lib/util/dateInput.ts +15 -0
- package/lib/util/timeZone.ts +33 -0
- package/lib/util/useCopyToClipboard.ts +48 -0
- package/lib/util/useIsMobile.ts +34 -0
- package/lib/videoconf/videoMeetingsApi.ts +144 -0
- package/package.json +10 -4
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
2
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz
|
|
3
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
5
|
+
/**
|
|
6
|
+
* Generates an escrow scope's key pair in the browser, for an administrator setting up escrow without an existing
|
|
7
|
+
* PKI. The certificate goes to the server (as the scope's public key, which mailbox master keys are wrapped to);
|
|
8
|
+
* the private key never does - the administrator must download it and hand it to the scope's holders, whose
|
|
9
|
+
* offline tooling is the only thing that can ever unwrap an escrowed master key. Losing it makes every escrow
|
|
10
|
+
* wrap made to this scope permanently unusable.
|
|
11
|
+
*
|
|
12
|
+
* The key is P-256, like every other key in this app: the certificate is signed with it as ECDSA, and holders
|
|
13
|
+
* re-import the same key material as ECDH to decrypt (see `keys.ts`'s `importPrivateKeyPkcs8()`).
|
|
14
|
+
*/
|
|
15
|
+
import "reflect-metadata";
|
|
16
|
+
import * as x509 from "@peculiar/x509";
|
|
17
|
+
import type { EscrowScopePublicKey } from "../admin/escrowScopesApi.js";
|
|
18
|
+
import { toBase64 } from "./encoding.js";
|
|
19
|
+
import { computeCertFingerprint } from "./smime.js";
|
|
20
|
+
|
|
21
|
+
x509.cryptoProvider.set(crypto);
|
|
22
|
+
|
|
23
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
24
|
+
|
|
25
|
+
export interface GeneratedEscrowKeys {
|
|
26
|
+
certificateDer: Uint8Array;
|
|
27
|
+
certificatePem: string;
|
|
28
|
+
/** PKCS#8, PEM encoded. */
|
|
29
|
+
privateKeyPem: string;
|
|
30
|
+
/** Ready to pass as `createEscrowScope()`'s `publicKey`. */
|
|
31
|
+
publicKey: EscrowScopePublicKey;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function toPem(label: string, der: Uint8Array): string {
|
|
35
|
+
const lines = toBase64(der).match(/.{1,64}/g) ?? [];
|
|
36
|
+
return `-----BEGIN ${label}-----\n${lines.join("\n")}\n-----END ${label}-----\n`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** A random, positive certificate serial number, hex encoded. */
|
|
40
|
+
function randomSerial(): string {
|
|
41
|
+
const bytes = crypto.getRandomValues(new Uint8Array(16));
|
|
42
|
+
bytes[0] &= 0x7f;
|
|
43
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function generateEscrowKeyPair(options: { name: string; validDays: number; now?: Date }): Promise<GeneratedEscrowKeys> {
|
|
47
|
+
const notBefore = options.now ?? new Date();
|
|
48
|
+
const notAfter = new Date(notBefore.getTime() + options.validDays * DAY_MS);
|
|
49
|
+
const keys = (await crypto.subtle.generateKey({ name: "ECDSA", namedCurve: "P-256" }, true, ["sign", "verify"]));
|
|
50
|
+
const certificate = await x509.X509CertificateGenerator.createSelfSigned({
|
|
51
|
+
serialNumber: randomSerial(),
|
|
52
|
+
// Structural name, never string-interpolated - see keys.ts's identical note on RDN injection.
|
|
53
|
+
name: [{ CN: [options.name] }],
|
|
54
|
+
notBefore,
|
|
55
|
+
notAfter,
|
|
56
|
+
signingAlgorithm: { name: "ECDSA", hash: "SHA-256" },
|
|
57
|
+
keys,
|
|
58
|
+
extensions: [new x509.KeyUsagesExtension(x509.KeyUsageFlags.keyAgreement | x509.KeyUsageFlags.digitalSignature, true)],
|
|
59
|
+
});
|
|
60
|
+
const certificateDer = new Uint8Array(certificate.rawData);
|
|
61
|
+
const privateKeyDer = new Uint8Array(await crypto.subtle.exportKey("pkcs8", keys.privateKey));
|
|
62
|
+
return {
|
|
63
|
+
certificateDer,
|
|
64
|
+
certificatePem: toPem("CERTIFICATE", certificateDer),
|
|
65
|
+
privateKeyPem: toPem("PRIVATE KEY", privateKeyDer),
|
|
66
|
+
publicKey: {
|
|
67
|
+
publicKey: toBase64(certificateDer),
|
|
68
|
+
type: "x509",
|
|
69
|
+
fingerprint: await computeCertFingerprint(certificateDer),
|
|
70
|
+
notBefore: notBefore.getTime(),
|
|
71
|
+
notAfter: notAfter.getTime(),
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
2
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz
|
|
3
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
5
|
+
/**
|
|
6
|
+
* The configured idle-timeout duration behind `useIdleKeyTimeout()` — the spec's own "configurable
|
|
7
|
+
* idle period" trigger for `keySession.ts`'s `destroyUnlockedKeys()`. Stored in `localStorage`, not
|
|
8
|
+
* synced to the server: it's a per-device preference about *this browser's* own in-memory key cache,
|
|
9
|
+
* the same "never persisted anywhere durable" posture `keySession.ts` itself takes for the keys this
|
|
10
|
+
* setting protects.
|
|
11
|
+
*/
|
|
12
|
+
const STORAGE_KEY = "rapidmx:idle-timeout-minutes";
|
|
13
|
+
|
|
14
|
+
/** The default for a device that has never set a preference - conservative enough for a shared/public
|
|
15
|
+
* machine without being disruptive on a personal one. */
|
|
16
|
+
export const DEFAULT_IDLE_TIMEOUT_MINUTES = 30;
|
|
17
|
+
|
|
18
|
+
/** `0` means "never" - the idle timer is disabled entirely. */
|
|
19
|
+
export const IDLE_TIMEOUT_OPTIONS_MINUTES = [5, 15, 30, 60, 0] as const;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Reads this device's configured idle-timeout duration, in minutes. Falls back to
|
|
23
|
+
* `DEFAULT_IDLE_TIMEOUT_MINUTES` for a never-configured device, a corrupted/non-numeric stored value,
|
|
24
|
+
* or a `localStorage` access that throws (private-browsing/storage-blocked contexts) - never throws
|
|
25
|
+
* itself, since a broken read here must not be able to prevent the app from rendering at all.
|
|
26
|
+
*/
|
|
27
|
+
export function getIdleTimeoutMinutes(): number {
|
|
28
|
+
try {
|
|
29
|
+
const stored = localStorage.getItem(STORAGE_KEY);
|
|
30
|
+
if (stored === null) {
|
|
31
|
+
return DEFAULT_IDLE_TIMEOUT_MINUTES;
|
|
32
|
+
}
|
|
33
|
+
const parsed = Number(stored);
|
|
34
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : DEFAULT_IDLE_TIMEOUT_MINUTES;
|
|
35
|
+
} catch {
|
|
36
|
+
return DEFAULT_IDLE_TIMEOUT_MINUTES;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Persists this device's idle-timeout preference. A `localStorage` write failure is swallowed, not
|
|
41
|
+
* thrown - the setting just doesn't survive a reload in that case, same fallback-to-default behavior
|
|
42
|
+
* `getIdleTimeoutMinutes()` already has for a storage-blocked context. */
|
|
43
|
+
export function setIdleTimeoutMinutes(minutes: number): void {
|
|
44
|
+
try {
|
|
45
|
+
localStorage.setItem(STORAGE_KEY, String(minutes));
|
|
46
|
+
} catch {
|
|
47
|
+
// Best-effort - see this function's own doc comment.
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
2
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz
|
|
3
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
5
|
+
/**
|
|
6
|
+
* The in-memory session key store `specs/end-to-end_encryption.md`'s "Keypair Generation & Storage"
|
|
7
|
+
* requires: "When a user logs in ... the private keys MUST be downloaded from the server, decrypted,
|
|
8
|
+
* and stored locally on the device in a secure location. When the user explicitly logs out ..., the
|
|
9
|
+
* private keys MUST be destroyed." This module is that store — a plain module-level `Map`, deliberately
|
|
10
|
+
* never `localStorage`/IndexedDB (a page reload or new tab correctly starts with no unlocked keys,
|
|
11
|
+
* requiring the unlock credential again, matching "destroyed on explicit logout" while never
|
|
12
|
+
* persisting unwrapped key material anywhere durable).
|
|
13
|
+
*
|
|
14
|
+
* `KeyEnrollmentGate` (in `web-client`) is what actually prompts for the unlock credential and calls
|
|
15
|
+
* `unlockWithPassword()` below, once per mailbox per session, before rendering the mailbox's real
|
|
16
|
+
* content — see that component's own doc comment.
|
|
17
|
+
*
|
|
18
|
+
* **Password and recovery code are the unlock methods implemented so far** (`unlockWithPassword()`,
|
|
19
|
+
* `unlockWithRecoveryCode()`, which share one master-key-to-session step). Passkey (`passkeyUnlock.ts`)
|
|
20
|
+
* derivation exists and could unwrap the same `MasterKeyWrap` shape, but nothing calls it yet — a real
|
|
21
|
+
* follow-up, not a silent gap: a mailbox enrolled *only* with a passkey has no way to unlock through this
|
|
22
|
+
* module today.
|
|
23
|
+
*/
|
|
24
|
+
import { type KeyVault, type PublicKey, type WrappedPrivateKey, findActivePublicKey, getKeyVault } from "./keyvaultApi.js";
|
|
25
|
+
import { fromBase64 } from "./encoding.js";
|
|
26
|
+
import { importPrivateKeyPkcs8 } from "./keys.js";
|
|
27
|
+
import { KeysLockedError, buildAad, openWithKey } from "./masterKey.js";
|
|
28
|
+
import { deriveFromPassword, parseArgon2idKdfLabel } from "./passwordUnlock.js";
|
|
29
|
+
import { RECOVERY_KDF_LABEL, deriveFromRecoveryCode } from "./recoveryCode.js";
|
|
30
|
+
|
|
31
|
+
/** AAD purpose labels — MUST exactly match what `KeyEnrollmentGate.tsx` used when it originally
|
|
32
|
+
* sealed each of these values, or `openWithKey()` fails (GCM authenticates the AAD, not just the
|
|
33
|
+
* ciphertext). Centralized here as the one place both wrapping and unwrapping refer to. */
|
|
34
|
+
export const MASTER_KEY_AAD_PURPOSE = "master-key";
|
|
35
|
+
export const SIGNING_PRIVATE_KEY_AAD_PURPOSE = "sign-private-key";
|
|
36
|
+
export const ENCRYPTION_PRIVATE_KEY_AAD_PURPOSE = "encrypt-private-key";
|
|
37
|
+
|
|
38
|
+
export { KeysLockedError };
|
|
39
|
+
|
|
40
|
+
/** How many retained (non-active) encryption keys an unlock opens at most: the most recently issued ones by
|
|
41
|
+
* `notBefore`. Each costs an AEAD open and a key import at unlock, and `smime.ts`'s `decryptEnvelopedDataWithKeys()`
|
|
42
|
+
* considers the active key plus this many (`MAX_DECRYPTION_KEYS`). Mail encrypted to a key older than the 20 most
|
|
43
|
+
* recent retained ones doesn't decrypt on this device; its wrapped key stays in the vault. */
|
|
44
|
+
export const MAX_RETAINED_ENCRYPTION_KEYS = 20;
|
|
45
|
+
|
|
46
|
+
/** An older encryption key an unlock opened besides the active one - see `UnlockedKeys.retainedEncryptionKeys`. */
|
|
47
|
+
export interface RetainedEncryptionKey {
|
|
48
|
+
fingerprint: string;
|
|
49
|
+
/** Base64-decoded DER certificate, from the mailbox's published `PublicKey.publicKey`. */
|
|
50
|
+
certDer: Uint8Array;
|
|
51
|
+
/** Non-extractable ECDH private key. */
|
|
52
|
+
privateKey: CryptoKey;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface UnlockedKeys {
|
|
56
|
+
/** `true` once `destroyUnlockedKeys()` has destroyed this object's keys (master key zeroed, private key
|
|
57
|
+
* handles dropped). A caller holding an `UnlockedKeys` across an `await` or a user action must treat a
|
|
58
|
+
* destroyed object as locked and re-read `getUnlockedKeys()` instead - `masterKey.ts`'s helpers throw
|
|
59
|
+
* `KeysLockedError` when handed its zeroed master key. Never set on an object still in the store. */
|
|
60
|
+
destroyed?: boolean;
|
|
61
|
+
masterKey: Uint8Array;
|
|
62
|
+
signingPrivateKey?: CryptoKey;
|
|
63
|
+
signingCertDer?: Uint8Array;
|
|
64
|
+
signingFingerprint?: string;
|
|
65
|
+
encryptionPrivateKey?: CryptoKey;
|
|
66
|
+
encryptionCertDer?: Uint8Array;
|
|
67
|
+
encryptionFingerprint?: string;
|
|
68
|
+
/** Every non-active encryption key the mailbox still has a wrapped private key for and a published `encrypt` key
|
|
69
|
+
* listed in its keys - superseded, expired *and* compromised ones - newest first, at most
|
|
70
|
+
* `MAX_RETAINED_ENCRYPTION_KEYS`. The spec retains an old encryption private key indefinitely on rotation, because
|
|
71
|
+
* discarding it makes old mail unreadable. **Decryption only:** `evaluateMessageSecurity()` tries these after the
|
|
72
|
+
* active key; nothing encrypts to or signs with them (`findActivePublicKey()` still picks the only key used for
|
|
73
|
+
* that). A compromised key is kept because reading one's own mail encrypted to it is still needed. Absent when there
|
|
74
|
+
* are none. */
|
|
75
|
+
retainedEncryptionKeys?: RetainedEncryptionKey[];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const sessions = new Map<string, UnlockedKeys>();
|
|
79
|
+
|
|
80
|
+
/** The one method of `WeakRef` this module uses - typed locally because the build's `ES2020` lib predates it. */
|
|
81
|
+
interface WeakHandle<T> {
|
|
82
|
+
deref(): T | undefined;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** A weak reference to `target` where the runtime has `WeakRef` (every supported browser and Node), otherwise a
|
|
86
|
+
* strong one - a missing `WeakRef` must never make a lock skip an object. */
|
|
87
|
+
function weakHandle<T extends object>(target: T): WeakHandle<T> {
|
|
88
|
+
const WeakRefCtor = (globalThis as { WeakRef?: new (target: T) => WeakHandle<T> }).WeakRef;
|
|
89
|
+
return WeakRefCtor ? new WeakRefCtor(target) : { deref: () => target };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Every `UnlockedKeys` object this store has handed out per mailbox and not yet destroyed - a re-unlock
|
|
93
|
+
* replaces the store entry but leaves the previous object usable for in-flight consumers, so a lock must
|
|
94
|
+
* reach all of them, not only the newest. Held *weakly*: the current object is kept alive by `sessions`, and a
|
|
95
|
+
* replaced one (e.g. holding a pre-rotation master key) only for as long as some consumer still references it -
|
|
96
|
+
* once nothing does, it can be collected instead of lingering until the next lock. A lock destroys every one
|
|
97
|
+
* still reachable, which is every one anybody could still use. */
|
|
98
|
+
const issued = new Map<string, Set<WeakHandle<UnlockedKeys>>>();
|
|
99
|
+
/** Bumped by every `destroyUnlockedKeys()` call for that mailbox (`lockAllGeneration` for a destroy-all),
|
|
100
|
+
* so an unlock still awaiting the vault/KDF when a lock happens can tell and discard what it unwrapped. */
|
|
101
|
+
const lockGenerations = new Map<string, number>();
|
|
102
|
+
let lockAllGeneration = 0;
|
|
103
|
+
|
|
104
|
+
function lockGeneration(mailboxUid: string): string {
|
|
105
|
+
return `${lockAllGeneration}:${lockGenerations.get(mailboxUid) ?? 0}`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** What changed in the session store — passed to every `subscribeKeySession()` listener. */
|
|
109
|
+
export interface KeySessionEvent {
|
|
110
|
+
mailboxUid: string;
|
|
111
|
+
/** `"unlocked"` when keys were stored (a first unlock, or a re-unlock replacing existing keys, e.g.
|
|
112
|
+
* after a key rotation); `"locked"` when they were destroyed. */
|
|
113
|
+
state: "unlocked" | "locked";
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export type KeySessionListener = (event: KeySessionEvent) => void;
|
|
117
|
+
|
|
118
|
+
const listeners = new Set<KeySessionListener>();
|
|
119
|
+
|
|
120
|
+
function notify(event: KeySessionEvent): void {
|
|
121
|
+
// Iterate a snapshot so a listener that unsubscribes (or subscribes another) mid-dispatch can't skip
|
|
122
|
+
// or double-deliver. A throwing listener must never stop the rest - especially on "locked", where a
|
|
123
|
+
// later listener may be the one clearing decrypted content from the screen - so its error is
|
|
124
|
+
// rethrown asynchronously instead, still surfacing as an uncaught error without aborting delivery.
|
|
125
|
+
for (const listener of [...listeners]) {
|
|
126
|
+
try {
|
|
127
|
+
listener(event);
|
|
128
|
+
} catch (err) {
|
|
129
|
+
queueMicrotask(() => {
|
|
130
|
+
throw err;
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Subscribes to session store changes: fires once per mailbox whenever its keys are unlocked (stored)
|
|
138
|
+
* or destroyed, so a UI can e.g. clear already-decrypted content the moment keys go away instead of
|
|
139
|
+
* polling `getUnlockedKeys()`. A `destroyUnlockedKeys()` call that finds nothing to destroy fires
|
|
140
|
+
* nothing. Returns an unsubscribe function.
|
|
141
|
+
*/
|
|
142
|
+
export function subscribeKeySession(listener: KeySessionListener): () => void {
|
|
143
|
+
listeners.add(listener);
|
|
144
|
+
return () => {
|
|
145
|
+
listeners.delete(listener);
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** Reads back a mailbox's already-unlocked keys this session, or `undefined` if it hasn't been
|
|
150
|
+
* unlocked yet (or was destroyed). Never triggers an unlock itself. */
|
|
151
|
+
export function getUnlockedKeys(mailboxUid: string): UnlockedKeys | undefined {
|
|
152
|
+
return sessions.get(mailboxUid);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Destroys one mailbox's unlocked keys, or every mailbox's if called with no argument — the spec's
|
|
157
|
+
* "destroyed on explicit logout" / "session revocation" / "configurable idle period" triggers.
|
|
158
|
+
*
|
|
159
|
+
* The master key's bytes are overwritten with zeros in place before the entry is dropped, so any
|
|
160
|
+
* `UnlockedKeys` object a caller is still holding no longer carries usable key material either (and the
|
|
161
|
+
* bytes don't linger until garbage collection). That object is also marked `destroyed: true` and its
|
|
162
|
+
* private key `CryptoKey` handles are removed (their material is never exposed to JS, so there is nothing
|
|
163
|
+
* to zero - but a stale holder must not keep signing/decrypting with them after a lock). Sealing/opening
|
|
164
|
+
* with the zeroed master key throws `KeysLockedError` (see `masterKey.ts`).
|
|
165
|
+
*
|
|
166
|
+
* Every object handed out for the mailbox is destroyed - including ones a re-unlock already replaced in the
|
|
167
|
+
* store - and an `unlockWithPassword()`/`unlockWithRecoveryCode()` still in flight for it when this runs throws `KeysLockedError`
|
|
168
|
+
* instead of restoring keys after the lock.
|
|
169
|
+
*/
|
|
170
|
+
export function destroyUnlockedKeys(mailboxUid?: string): void {
|
|
171
|
+
if (mailboxUid) {
|
|
172
|
+
lockGenerations.set(mailboxUid, (lockGenerations.get(mailboxUid) ?? 0) + 1);
|
|
173
|
+
} else {
|
|
174
|
+
lockAllGeneration++;
|
|
175
|
+
}
|
|
176
|
+
const uids = mailboxUid ? [mailboxUid] : [...issued.keys()];
|
|
177
|
+
for (const uid of uids) {
|
|
178
|
+
const objects = issued.get(uid);
|
|
179
|
+
if (!objects) {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
for (const handle of objects) {
|
|
183
|
+
const unlocked = handle.deref();
|
|
184
|
+
if (unlocked) {
|
|
185
|
+
destroyObject(unlocked);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
issued.delete(uid);
|
|
189
|
+
sessions.delete(uid);
|
|
190
|
+
notify({ mailboxUid: uid, state: "locked" });
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function destroyObject(unlocked: UnlockedKeys): void {
|
|
195
|
+
unlocked.masterKey.fill(0);
|
|
196
|
+
unlocked.destroyed = true;
|
|
197
|
+
delete unlocked.signingPrivateKey;
|
|
198
|
+
delete unlocked.encryptionPrivateKey;
|
|
199
|
+
if (unlocked.retainedEncryptionKeys) {
|
|
200
|
+
// Emptied in place as well as removed, so a consumer that captured the array itself loses the handles too.
|
|
201
|
+
unlocked.retainedEncryptionKeys.length = 0;
|
|
202
|
+
delete unlocked.retainedEncryptionKeys;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** Finds the wrapped private key whose fingerprint matches a given published public key. */
|
|
207
|
+
function findWrappedPrivateKey(vault: KeyVault, fingerprint: string) {
|
|
208
|
+
return vault.wrappedKeys.find((k) => k.fingerprint === fingerprint);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** What `unlockWithPassword()` resolves with (and the base of `unlockWithRecoveryCode()`'s result). */
|
|
212
|
+
export interface UnlockResult {
|
|
213
|
+
/** Fingerprints of the active *signing* key and any retained (non-active) *encryption* keys whose wrapped private
|
|
214
|
+
* key couldn't be opened or imported with the (correctly unwrapped) master key - e.g. a wrap sealed under a master
|
|
215
|
+
* key a later rekey replaced. The unlock still succeeds without them (no `signingPrivateKey`; the retained key is
|
|
216
|
+
* left out of `retainedEncryptionKeys`), so a user isn't locked out of reading mail by a key they can't use. Empty
|
|
217
|
+
* when every key opened. */
|
|
218
|
+
unopenableKeys: string[];
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Thrown by `unlockWithPassword()` when the password was right (the master key unwrapped) but the active
|
|
223
|
+
* *encryption* key's wrapped private key couldn't be opened or imported with it. Distinct from the
|
|
224
|
+
* wrong-password failure (which rejects with the underlying AEAD error from opening the password wrap), so a
|
|
225
|
+
* UI can say "your encryption key can't be opened" instead of "incorrect password".
|
|
226
|
+
*/
|
|
227
|
+
export class UnopenableEncryptionKeyError extends Error {
|
|
228
|
+
public readonly fingerprint: string;
|
|
229
|
+
/** The underlying AEAD/import failure. */
|
|
230
|
+
public readonly cause: unknown;
|
|
231
|
+
|
|
232
|
+
constructor(fingerprint: string, cause?: unknown) {
|
|
233
|
+
super(`The encryption key ${fingerprint} for this mailbox couldn't be opened with its master key.`);
|
|
234
|
+
this.name = "UnopenableEncryptionKeyError";
|
|
235
|
+
this.fingerprint = fingerprint;
|
|
236
|
+
this.cause = cause;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/** Opens one wrapped PKCS#8 private key under `masterKey` and imports it (non-extractable by default, like
|
|
241
|
+
* `importPrivateKeyPkcs8()` itself), zeroing the transient plaintext whether or not the import succeeds. */
|
|
242
|
+
async function openPrivateKey(
|
|
243
|
+
masterKey: Uint8Array,
|
|
244
|
+
wrapped: WrappedPrivateKey,
|
|
245
|
+
aad: Uint8Array,
|
|
246
|
+
algorithm: EcKeyImportParams,
|
|
247
|
+
usages: KeyUsage[],
|
|
248
|
+
extractable = false,
|
|
249
|
+
): Promise<CryptoKey> {
|
|
250
|
+
const raw = await openWithKey(masterKey, wrapped, aad);
|
|
251
|
+
try {
|
|
252
|
+
return await importPrivateKeyPkcs8(raw, algorithm, usages, extractable);
|
|
253
|
+
} finally {
|
|
254
|
+
raw.fill(0);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Opens the retained encryption keys onto `unlocked.retainedEncryptionKeys`: every published `encrypt` key in
|
|
260
|
+
* `mailboxKeys` other than the active one (`activeFingerprint`), whatever its revocation or expiry, that has a wrapped
|
|
261
|
+
* private key in the vault - de-duplicated by fingerprint, newest `notBefore` first, at most
|
|
262
|
+
* `MAX_RETAINED_ENCRYPTION_KEYS`. A key that won't open or import, or whose certificate won't decode, is skipped and
|
|
263
|
+
* added to `unopenableKeys`. Retained keys are imported non-extractable. Each key is attached as it opens, so a failed
|
|
264
|
+
* unlock's `destroyObject()` drops those too.
|
|
265
|
+
*/
|
|
266
|
+
async function openRetainedEncryptionKeys(
|
|
267
|
+
mailboxUid: string,
|
|
268
|
+
mailboxKeys: PublicKey[],
|
|
269
|
+
vault: KeyVault,
|
|
270
|
+
unlocked: UnlockedKeys,
|
|
271
|
+
activeFingerprint: string | undefined,
|
|
272
|
+
unopenableKeys: string[],
|
|
273
|
+
): Promise<void> {
|
|
274
|
+
const seen = new Set<string>(activeFingerprint !== undefined ? [activeFingerprint] : []);
|
|
275
|
+
const candidates: { publicKey: PublicKey; wrapped: WrappedPrivateKey }[] = [];
|
|
276
|
+
for (const publicKey of [...mailboxKeys].sort((a, b) => b.notBefore - a.notBefore)) {
|
|
277
|
+
const wrapped = publicKey.useType === "encrypt" && !seen.has(publicKey.fingerprint) ? findWrappedPrivateKey(vault, publicKey.fingerprint) : undefined;
|
|
278
|
+
if (!wrapped) {
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
seen.add(publicKey.fingerprint);
|
|
282
|
+
candidates.push({ publicKey, wrapped });
|
|
283
|
+
if (candidates.length === MAX_RETAINED_ENCRYPTION_KEYS) {
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const aad = buildAad(mailboxUid, ENCRYPTION_PRIVATE_KEY_AAD_PURPOSE);
|
|
289
|
+
for (const { publicKey, wrapped } of candidates) {
|
|
290
|
+
try {
|
|
291
|
+
const certDer = fromBase64(publicKey.publicKey);
|
|
292
|
+
const privateKey = await openPrivateKey(unlocked.masterKey, wrapped, aad, { name: "ECDH", namedCurve: "P-256" }, ["deriveBits"], false);
|
|
293
|
+
(unlocked.retainedEncryptionKeys ??= []).push({ fingerprint: publicKey.fingerprint, certDer, privateKey });
|
|
294
|
+
} catch {
|
|
295
|
+
unopenableKeys.push(publicKey.fingerprint);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Unlocks a mailbox's key vault with its password unlock method: fetches the vault, derives the
|
|
302
|
+
* wrapping key from `password` using the *exact* KDF parameters that wrap was created with, unwraps
|
|
303
|
+
* the master key, then unwraps and imports whichever signing/encryption private keys the mailbox has
|
|
304
|
+
* currently-active public keys for, plus the retained older encryption keys (`UnlockedKeys.retainedEncryptionKeys`).
|
|
305
|
+
* Stores the result in this module's in-memory session store.
|
|
306
|
+
*
|
|
307
|
+
* Throws (never silently no-ops) when there's no password wrap enrolled, or when the password is
|
|
308
|
+
* wrong (AEAD authentication failure opening the password wrap) — callers should present this as
|
|
309
|
+
* "incorrect password," not a generic error, but this module doesn't presume a specific UI's error copy.
|
|
310
|
+
* Once the master key has opened, the password is known to be right: an active signing key that then
|
|
311
|
+
* won't open is skipped and listed in `UnlockResult.unopenableKeys` (so is a retained encryption key); an active
|
|
312
|
+
* encryption key that won't
|
|
313
|
+
* open still fails the unlock, with `UnopenableEncryptionKeyError`. Throws `KeysLockedError` when
|
|
314
|
+
* `destroyUnlockedKeys()` locked this mailbox (or all mailboxes) while the unlock was in flight.
|
|
315
|
+
*/
|
|
316
|
+
export async function unlockWithPassword(mailboxUid: string, mailboxKeys: PublicKey[], password: string): Promise<UnlockResult> {
|
|
317
|
+
const generation = lockGeneration(mailboxUid);
|
|
318
|
+
const vault = await getKeyVault(mailboxUid);
|
|
319
|
+
const passwordWrap = vault.masterKeyWraps.find((w) => w.method === "password");
|
|
320
|
+
if (!passwordWrap) {
|
|
321
|
+
throw new Error("This mailbox has no password unlock method enrolled.");
|
|
322
|
+
}
|
|
323
|
+
const kdfParams = parseArgon2idKdfLabel(passwordWrap.kdf);
|
|
324
|
+
if (!kdfParams) {
|
|
325
|
+
throw new Error(`Unrecognized KDF for this mailbox's password wrap: ${passwordWrap.kdf}`);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const salt = fromBase64(passwordWrap.salt);
|
|
329
|
+
const { wrappingKey } = await deriveFromPassword(password, salt, kdfParams);
|
|
330
|
+
const masterKey = await openWithKey(
|
|
331
|
+
wrappingKey,
|
|
332
|
+
{ ciphertext: passwordWrap.ciphertext, nonce: passwordWrap.nonce },
|
|
333
|
+
buildAad(mailboxUid, MASTER_KEY_AAD_PURPOSE),
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
return openSession(mailboxUid, mailboxKeys, vault, masterKey, generation);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/** What `unlockWithRecoveryCode()` resolves with. */
|
|
340
|
+
export interface RecoveryUnlockResult extends UnlockResult {
|
|
341
|
+
/** The `methodId` of the recovery wrap the code opened (e.g. `"recovery-3"`). Recovery codes are single-use:
|
|
342
|
+
* pass it to `masterKeyWraps.ts`'s `consumeRecoveryCode()` once the unlock flow is done. Absent only for a
|
|
343
|
+
* recovery wrap stored without a `methodId`, which `buildRecoveryWraps()` never produces. */
|
|
344
|
+
recoveryMethodId?: string;
|
|
345
|
+
/** How many *other* recovery wraps the vault holds - the codes left once this one is consumed. */
|
|
346
|
+
remainingRecoveryCodes: number;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/** The one rejection for a recovery code that opens nothing - a wrong code and a vault with no recovery wraps
|
|
350
|
+
* alike, so a UI can say "that recovery code didn't work" without learning (or revealing) which it was. */
|
|
351
|
+
const RECOVERY_CODE_REJECTED = "That recovery code didn't unlock this mailbox.";
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Unlocks a mailbox's key vault with one of its recovery codes: fetches the vault, tries every `"recovery"` wrap
|
|
355
|
+
* (`kdf: "hkdf-sha256"`) with `deriveFromRecoveryCode(code, salt)` until one opens the master key, then opens the
|
|
356
|
+
* signing/encryption private keys and stores the session exactly as `unlockWithPassword()` does - the same
|
|
357
|
+
* `unopenableKeys` handling, `UnopenableEncryptionKeyError`, `KeysLockedError` on a lock mid-unlock, and
|
|
358
|
+
* `subscribeKeySession()` notification. `code` is normalized first (case, spacing, dashes, O/I/L slips).
|
|
359
|
+
*
|
|
360
|
+
* A wrong code and a vault with no recovery wraps both reject with the same plain `Error` (never saying which
|
|
361
|
+
* wrap was tried or failed) - the recovery-code counterpart of `unlockWithPassword()`'s wrong-password rejection.
|
|
362
|
+
*
|
|
363
|
+
* A recovery code is single-use, but this function writes nothing: the caller removes the used wrap afterwards
|
|
364
|
+
* with `consumeRecoveryCode(mailboxUid, result.recoveryMethodId)` (and may first offer a new password via
|
|
365
|
+
* `replacePasswordWrap()` - see that function for why that order is the safe one).
|
|
366
|
+
*/
|
|
367
|
+
export async function unlockWithRecoveryCode(mailboxUid: string, mailboxKeys: PublicKey[], code: string): Promise<RecoveryUnlockResult> {
|
|
368
|
+
const generation = lockGeneration(mailboxUid);
|
|
369
|
+
const vault = await getKeyVault(mailboxUid);
|
|
370
|
+
const recoveryWraps = vault.masterKeyWraps.filter((w) => w.method === "recovery");
|
|
371
|
+
const aad = buildAad(mailboxUid, MASTER_KEY_AAD_PURPOSE);
|
|
372
|
+
|
|
373
|
+
let opened: { masterKey: Uint8Array; methodId?: string } | undefined;
|
|
374
|
+
for (const wrap of recoveryWraps) {
|
|
375
|
+
if (wrap.kdf !== RECOVERY_KDF_LABEL) {
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
let wrappingKey: Uint8Array | undefined;
|
|
379
|
+
try {
|
|
380
|
+
wrappingKey = await deriveFromRecoveryCode(code, fromBase64(wrap.salt));
|
|
381
|
+
opened = { masterKey: await openWithKey(wrappingKey, { ciphertext: wrap.ciphertext, nonce: wrap.nonce }, aad), methodId: wrap.methodId };
|
|
382
|
+
} catch {
|
|
383
|
+
// Wrong code for this wrap (AEAD failure), an empty code, or a corrupt wrap - all just "not this one".
|
|
384
|
+
} finally {
|
|
385
|
+
wrappingKey?.fill(0);
|
|
386
|
+
}
|
|
387
|
+
if (opened) {
|
|
388
|
+
break;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
if (!opened) {
|
|
392
|
+
throw new Error(RECOVERY_CODE_REJECTED);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const result = await openSession(mailboxUid, mailboxKeys, vault, opened.masterKey, generation);
|
|
396
|
+
return {
|
|
397
|
+
...result,
|
|
398
|
+
...(opened.methodId !== undefined ? { recoveryMethodId: opened.methodId } : {}),
|
|
399
|
+
remainingRecoveryCodes: recoveryWraps.length - 1,
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Shared tail of every unlock method once its wrap has yielded `masterKey`: opens and imports the active signing and
|
|
405
|
+
* encryption private keys and the retained encryption keys (`openRetainedEncryptionKeys()`), aborts with `KeysLockedError` if the mailbox was locked since `generation` was taken, then
|
|
406
|
+
* stores the result (tracked weakly in `issued`) and notifies. Zeroes `masterKey` on any failure.
|
|
407
|
+
*/
|
|
408
|
+
async function openSession(
|
|
409
|
+
mailboxUid: string,
|
|
410
|
+
mailboxKeys: PublicKey[],
|
|
411
|
+
vault: KeyVault,
|
|
412
|
+
masterKey: Uint8Array,
|
|
413
|
+
generation: string,
|
|
414
|
+
): Promise<UnlockResult> {
|
|
415
|
+
const unlocked: UnlockedKeys = { masterKey };
|
|
416
|
+
const unopenableKeys: string[] = [];
|
|
417
|
+
|
|
418
|
+
// The active private keys are imported *non-extractable*, same as the retained encryption keys below (round-5
|
|
419
|
+
// review; previously imported extractable, on the reasoning that an XSS able to call `exportKey()` on these
|
|
420
|
+
// handles could equally call `openWithKey()` with the in-memory master key - but that framing missed that
|
|
421
|
+
// `exportKey()` yields raw bytes an attacker can copy out and keep *after* the session ends (logout, master-key
|
|
422
|
+
// zeroing), whereas the master-key-access path only works while the session is live. Their one former consumer,
|
|
423
|
+
// `keyRotation.ts`'s `rewrapPrivateKeysUnderNewMasterKey()`, was removed (2026-09-15; it re-wrapped only the
|
|
424
|
+
// active keys, and web-client's rotation re-seals every vault wrap itself) - confirmed nothing in this package
|
|
425
|
+
// or in `web-client`/`electron-client` calls `exportKey()` on a session key at all, so nothing depends on this
|
|
426
|
+
// being extractable. The transient PKCS#8 plaintext buffers are zeroed as soon as WebCrypto has copied them
|
|
427
|
+
// into a `CryptoKey` either way.
|
|
428
|
+
try {
|
|
429
|
+
const signingPublicKey = findActivePublicKey(mailboxKeys, "sign");
|
|
430
|
+
const wrappedSigningKey = signingPublicKey && findWrappedPrivateKey(vault, signingPublicKey.fingerprint);
|
|
431
|
+
if (signingPublicKey && wrappedSigningKey) {
|
|
432
|
+
try {
|
|
433
|
+
const aad = buildAad(mailboxUid, SIGNING_PRIVATE_KEY_AAD_PURPOSE);
|
|
434
|
+
unlocked.signingPrivateKey = await openPrivateKey(masterKey, wrappedSigningKey, aad, { name: "ECDSA", namedCurve: "P-256" }, ["sign"]);
|
|
435
|
+
unlocked.signingCertDer = fromBase64(signingPublicKey.publicKey);
|
|
436
|
+
unlocked.signingFingerprint = signingPublicKey.fingerprint;
|
|
437
|
+
} catch {
|
|
438
|
+
unopenableKeys.push(signingPublicKey.fingerprint);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const encryptionPublicKey = findActivePublicKey(mailboxKeys, "encrypt");
|
|
443
|
+
const wrappedEncryptionKey = encryptionPublicKey && findWrappedPrivateKey(vault, encryptionPublicKey.fingerprint);
|
|
444
|
+
if (encryptionPublicKey && wrappedEncryptionKey) {
|
|
445
|
+
try {
|
|
446
|
+
const aad = buildAad(mailboxUid, ENCRYPTION_PRIVATE_KEY_AAD_PURPOSE);
|
|
447
|
+
unlocked.encryptionPrivateKey = await openPrivateKey(masterKey, wrappedEncryptionKey, aad, { name: "ECDH", namedCurve: "P-256" }, ["deriveBits"]);
|
|
448
|
+
} catch (err) {
|
|
449
|
+
throw new UnopenableEncryptionKeyError(encryptionPublicKey.fingerprint, err);
|
|
450
|
+
}
|
|
451
|
+
unlocked.encryptionCertDer = fromBase64(encryptionPublicKey.publicKey);
|
|
452
|
+
unlocked.encryptionFingerprint = encryptionPublicKey.fingerprint;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
await openRetainedEncryptionKeys(mailboxUid, mailboxKeys, vault, unlocked, encryptionPublicKey?.fingerprint, unopenableKeys);
|
|
456
|
+
if (lockGeneration(mailboxUid) !== generation) {
|
|
457
|
+
// Locked (e.g. logout) while this unlock was awaiting - never restore keys after that.
|
|
458
|
+
throw new KeysLockedError();
|
|
459
|
+
}
|
|
460
|
+
} catch (err) {
|
|
461
|
+
// A failed unlock never reaches the session store, so nothing else would ever zero this.
|
|
462
|
+
destroyObject(unlocked);
|
|
463
|
+
throw err;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// A re-unlock replacing existing keys (e.g. `settings/encryption`'s post-rotation refresh) deliberately
|
|
467
|
+
// does NOT zero the previous entry's master key: an in-flight consumer that captured the old
|
|
468
|
+
// `UnlockedKeys` object (e.g. a local-index build pass) is still legitimately using it, and nothing
|
|
469
|
+
// "destroyed" the session - only `destroyUnlockedKeys()` does that, and it destroys every issued object still
|
|
470
|
+
// reachable. The store itself keeps the replaced object only weakly (see `issued`).
|
|
471
|
+
sessions.set(mailboxUid, unlocked);
|
|
472
|
+
const objects = issued.get(mailboxUid) ?? new Set<WeakHandle<UnlockedKeys>>();
|
|
473
|
+
// Drop handles whose object was already collected, so repeated re-unlocks don't grow the set.
|
|
474
|
+
for (const handle of objects) {
|
|
475
|
+
if (!handle.deref()) {
|
|
476
|
+
objects.delete(handle);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
objects.add(weakHandle(unlocked));
|
|
480
|
+
issued.set(mailboxUid, objects);
|
|
481
|
+
notify({ mailboxUid, state: "unlocked" });
|
|
482
|
+
return { unopenableKeys };
|
|
483
|
+
}
|