Skip to content

Commit

Permalink
Add restore password from file
Browse files Browse the repository at this point in the history
  • Loading branch information
mrruby committed Dec 11, 2023
1 parent c8cbc77 commit f2a8a90
Show file tree
Hide file tree
Showing 30 changed files with 317 additions and 130 deletions.
6 changes: 4 additions & 2 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module.exports = {
'prettier'
],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
plugins: ['@typescript-eslint', 'simple-import-sort'],
parserOptions: {
sourceType: 'module',
ecmaVersion: 2020,
Expand All @@ -29,6 +29,8 @@ module.exports = {
}
],
rules: {
'@typescript-eslint/ban-ts-comment': 0
'@typescript-eslint/ban-ts-comment': 0,
'simple-import-sort/imports': 'error',
'simple-import-sort/exports': 'error'
}
};
2 changes: 1 addition & 1 deletion .lintstagedrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"src/**/*.{js,ts,svelte,json}": ["eslint", "prettier --write ."]
"src/**/*.{js,ts,svelte,json}": ["eslint --fix", "prettier --write ."]
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --check 'src/**/*.{js,ts,svelte,json}' && eslint 'src/**/*.{js,ts,svelte}'",
"lint": "prettier --check 'src/**/*.{js,ts,svelte,json}' && eslint --fix 'src/**/*.{js,ts,svelte}'",
"format": "prettier --write 'src/**/*.{js,ts,svelte,json}'",
"prepare": "husky install"
},
Expand All @@ -22,6 +22,7 @@
"autoprefixer": "^10.4.16",
"eslint": "^8.55.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-simple-import-sort": "^10.0.0",
"eslint-plugin-svelte": "^2.35.1",
"husky": "^8.0.3",
"lint-staged": "^15.2.0",
Expand Down
11 changes: 11 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/lib/components/Init.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script lang="ts">
import { Button, AppParagraph } from '$components';
import { AppParagraph, Button } from '$components';
import { dismissWindow } from '$lib/helpers';
function redirectToSetup() {
Expand Down
3 changes: 2 additions & 1 deletion src/lib/components/Login.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<script lang="ts">
import { Button, AppParagraph } from '$components';
import { AppParagraph, Button } from '$components';
import { dismissWindow } from '$lib/helpers';
import { sessionStorageQueries } from '$queries';
import InputPassword from './InputPassword.svelte';
let password = '';
Expand Down
3 changes: 2 additions & 1 deletion src/lib/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export { default as AppParagraph } from './AppParagraph.svelte';
export { default as Button } from './Button.svelte';
export { default as Title } from './Title.svelte';
export { default as Init } from './Init.svelte';
export { default as InputPassword } from './InputPassword.svelte';
export { default as Login } from './Login.svelte';
export { default as SetupContainer } from './SetupContainer.svelte';
export { default as Title } from './Title.svelte';
2 changes: 1 addition & 1 deletion src/lib/const/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export * from './secure-store';
export * from './query-keys';
export * from './secure-store';
2 changes: 1 addition & 1 deletion src/lib/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export * from './encryption';
export * from './navigation';
export * from './other';
export * from './encryption';
export * from './storage';
18 changes: 12 additions & 6 deletions src/lib/queries/app-queries.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import { useQueryClient } from '@tanstack/svelte-query';

import {
createSessionQuery,
createSetupDeviceKeyQuery,
createStoreDeviceKey
} from './sessionAndKey';
import {
createChangePasswordWithDeviceKeyMutation,
createPasswordAndStoreDeviceKeyMutation,
createPasswordMutation,
createSetupPasswordQuery,
createSignInMutation
} from './password';
import {
createRecoverDeviceKeyMutation,
createSessionQuery,
createSetupDeviceKeyQuery,
createStoreDeviceKey
} from './sessionAndKey';

export function sessionStorageQueries() {
const queryClient = useQueryClient();
Expand All @@ -22,6 +24,8 @@ export function sessionStorageQueries() {
const setupPasswordQuery = createSetupPasswordQuery();
const signInMutation = createSignInMutation(queryClient);
const storeDeviceKey = createStoreDeviceKey(queryClient);
const recoverDeviceKeyMutation = createRecoverDeviceKeyMutation();
const passwordAndStoreDeviceKeyMutation = createPasswordAndStoreDeviceKeyMutation(queryClient);

return {
changePasswordWithDeviceKeyMutation,
Expand All @@ -30,6 +34,8 @@ export function sessionStorageQueries() {
setupDeviceKeyQuery,
setupPasswordQuery,
signInMutation,
storeDeviceKey
storeDeviceKey,
recoverDeviceKeyMutation,
passwordAndStoreDeviceKeyMutation
};
}
61 changes: 49 additions & 12 deletions src/lib/queries/password.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,30 @@
import { createMutation, createQuery, type QueryClient } from '@tanstack/svelte-query';
import { get } from 'svelte/store';

import {
DEVICE_KEY,
LOCAL,
PASSWORD,
SESSION,
SESSION_DATA,
SESSION_DATA_KEY,
SETUP_KEY,
SETUP_PASSWORD
} from '$const';
import { getPassword, hashPassword, verifyPassword } from '$helpers';
import { lockKey, storageService, unlockKey } from '$services';
import { deviceKeyContentStore, passphraseStore } from '$stores';
import { EncryptedDeviceKeySchema } from '$types';
import { createMutation, createQuery, type QueryClient } from '@tanstack/svelte-query';

const storePasswordReturnHash = async (password: string) => {
const hashSalt = await hashPassword(password);
storageService.set({
key: PASSWORD,
value: hashSalt,
area: LOCAL
});
return hashSalt.hash;
};

export function createSetupPasswordQuery() {
return createQuery({
Expand All @@ -23,17 +37,44 @@ export function createSetupPasswordQuery() {
}

export function createPasswordMutation(queryClient: QueryClient) {
return createMutation({
mutationFn: storePasswordReturnHash,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: [SETUP_PASSWORD] });
}
});
}

export function createPasswordAndStoreDeviceKeyMutation(queryClient: QueryClient) {
return createMutation({
mutationFn: async (password: string) => {
const hash = await hashPassword(password);
const deviceKey = get(deviceKeyContentStore);
const passphrase = get(passphraseStore);

const decryptedKey = await unlockKey(deviceKey, passphrase);

if (!deviceKey) {
throw new Error('Something went wrong');
}

const newHash = await storePasswordReturnHash(password);

storageService.set({
key: PASSWORD,
value: hash,
key: DEVICE_KEY,
value: await lockKey(decryptedKey, newHash),
area: LOCAL
});
decryptedKey.zero();
deviceKeyContentStore.clean();
passphraseStore.clean();
storageService.set({
key: SESSION_DATA,
value: false,
area: SESSION
});
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: [SETUP_PASSWORD] });
queryClient.invalidateQueries({ queryKey: [SETUP_KEY] });
}
});
}
Expand Down Expand Up @@ -83,16 +124,12 @@ export function createChangePasswordWithDeviceKeyMutation(queryClient: QueryClie
}

const decryptedKey = await unlockKey(parsedDeviceKey.data, parsedResult.data.hash);
const newHashSalt = await hashPassword(mutationData.newPassword);

storageService.set({
key: PASSWORD,
value: newHashSalt,
area: LOCAL
});
const newHash = await storePasswordReturnHash(mutationData.newPassword);

storageService.set({
key: DEVICE_KEY,
value: await lockKey(decryptedKey, newHashSalt.hash),
value: await lockKey(decryptedKey, newHash),
area: LOCAL
});
decryptedKey.zero();
Expand Down
25 changes: 23 additions & 2 deletions src/lib/queries/sessionAndKey.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { createMutation, createQuery, QueryClient } from '@tanstack/svelte-query';

import {
DEVICE_KEY,
LOCAL,
Expand All @@ -7,9 +9,9 @@ import {
SESSION_DATA_KEY,
SETUP_KEY
} from '$const';
import { storageService } from '$services';
import { storageService, unlockKey } from '$services';
import { deviceKeyContentStore, passphraseStore } from '$stores';
import { EncryptedDeviceKeySchema, HashSaltSchema, SessionStateSchema } from '$types';
import { QueryClient, createMutation, createQuery } from '@tanstack/svelte-query';

export function createSessionQuery() {
return createQuery({
Expand Down Expand Up @@ -65,3 +67,22 @@ export function createStoreDeviceKey(queryClient: QueryClient) {
}
});
}

export function createRecoverDeviceKeyMutation() {
return createMutation({
mutationFn: async (mutationData: { deviceKey: string; passphrase: string }) => {
const parsedDeviceKey = EncryptedDeviceKeySchema.safeParse(mutationData.deviceKey);

if (!parsedDeviceKey.success) {
throw new Error('Invalid device key');
}

const decryptedKey = await unlockKey(parsedDeviceKey.data, mutationData.passphrase);

deviceKeyContentStore.set(parsedDeviceKey.data);
passphraseStore.set(mutationData.passphrase);

return decryptedKey.zero();
}
});
}
5 changes: 3 additions & 2 deletions src/lib/services/generate-keys.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
// @ts-nocheck
import * as hcSeedBundle from 'hcSeedBundle';

import type { GeneratedKeys } from '$types';

const uint8ArrayToBase64 = (bytes: Uint8Array) => btoa(String.fromCharCode(...bytes));
export const uint8ArrayToBase64 = (bytes: Uint8Array) => btoa(String.fromCharCode(...bytes));

const base64ToArrayBuffer = (base64: string) => {
export const base64ToArrayBuffer = (base64: string) => {
const binaryString = atob(base64);
return new Uint8Array([...binaryString].map((char) => char.charCodeAt(0)));
};
Expand Down
17 changes: 17 additions & 0 deletions src/lib/stores/device-key-content.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { writable } from 'svelte/store';

const createDeviceKeyContentStore = () => {
const { subscribe, set } = writable('');

const clean = () => set('');

return {
subscribe,
set,
clean
};
};

const deviceKeyContentStore = createDeviceKeyContentStore();

export { deviceKeyContentStore };
3 changes: 2 additions & 1 deletion src/lib/stores/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './passphrase';
export * from './device-key-content';
export * from './keys-store';
export * from './passphrase';
6 changes: 4 additions & 2 deletions src/lib/stores/keys-store.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { KeysState } from '$types';
import { writable } from 'svelte/store';
import { generateKeys } from '../services/generate-keys';

import { getPassword } from '$helpers';
import type { KeysState } from '$types';

import { generateKeys } from '../services/generate-keys';

const initKeysStore = () => {
const initialState: KeysState = {
Expand Down
4 changes: 3 additions & 1 deletion src/lib/types/storage-service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type { LOCAL, SESSION, SESSION_DATA, PASSWORD, DEVICE_KEY } from '$const';
import { z } from 'zod';

import type { DEVICE_KEY, LOCAL, PASSWORD, SESSION, SESSION_DATA } from '$const';

import type { HashSalt } from './keys';

export type AreaName = typeof SESSION | typeof LOCAL | 'sync' | 'managed';
Expand Down
Loading

0 comments on commit f2a8a90

Please sign in to comment.