improved cryptography

This commit is contained in:
Myzel394 2023-01-19 12:02:21 +01:00
parent ee0682a8b6
commit b828f75332
6 changed files with 80 additions and 31 deletions

View File

@ -1,13 +1,12 @@
import {AliasNote} from "~/server-types"
export const MASTER_PASSWORD_LENGTH = 1024
export const LOCAL_REGEX = /^[a-zA-Z0-9!#$%&*+/=?^_`.{|}~-]{1,64}$/g
export const URL_REGEX =
/((http[s]*:\/\/)?[a-z0-9-%\/\&=?\.]+\.[a-z]{2,4}\/?([^\s<>\#%"\,\{\}\\|\\\^\[\]`]+)?)/gi
export const DEFAULT_ALIAS_NOTE: AliasNote = {
version: "1.0",
data: {
createdOn: "instance",
createdOn: "/aliases",
createdAt: null,
creationContext: "web",
personalNotes: "",

View File

@ -37,6 +37,9 @@ export function CreateAliasButton(): ReactElement {
JSON.stringify(
update(DEFAULT_ALIAS_NOTE, {
data: {
createdOn: {
$set: window.location.origin + window.location.pathname,
},
createdAt: {
$set: new Date(),
},

View File

@ -5,19 +5,16 @@ import {readKey} from "openpgp"
import {AxiosError} from "axios"
import {useTranslation} from "react-i18next"
import {useLoaderData} from "react-router-dom"
import React, {ReactElement, useCallback, useContext, useMemo, useRef} from "react"
import passwordGenerator from "secure-random-password"
import React, {ReactElement, useCallback, useContext, useRef} from "react"
import {Box, InputAdornment} from "@mui/material"
import {useMutation} from "@tanstack/react-query"
import {AuthContext, PasswordField, SimpleForm} from "~/components"
import {encryptString, generateKeys, getEncryptionPassword, getUserSalt} from "~/utils"
import {setupEncryptionForUser} from "~/utils"
import {useExtensionHandler, useNavigateToNext, useSystemPreferredTheme, useUser} from "~/hooks"
import {MASTER_PASSWORD_LENGTH} from "~/constants/values"
import {AuthenticationDetails, ServerSettings, UserNote} from "~/server-types"
import {AuthenticationDetails, ServerSettings} from "~/server-types"
import {UpdateAccountData, updateAccount} from "~/apis"
import {encryptUserNote} from "~/utils/encrypt-user-note"
export interface PasswordFormProps {
onDone: () => void
@ -54,7 +51,6 @@ export default function PasswordForm({onDone}: PasswordFormProps): ReactElement
const {_setEncryptionPassword, login} = useContext(AuthContext)
const waitForKeyGeneration = useMemo(generateKeys, [])
const {mutateAsync} = useMutation<AuthenticationDetails, AxiosError, UpdateAccountData>(
updateAccount,
)
@ -66,32 +62,20 @@ export default function PasswordForm({onDone}: PasswordFormProps): ReactElement
},
onSubmit: async ({password}, {setErrors}) => {
try {
const keyPair = await waitForKeyGeneration
const masterPassword = passwordGenerator.randomPassword({
length: MASTER_PASSWORD_LENGTH,
})
const salt = getUserSalt(user, serverSettings)
const encryptionKey = await getEncryptionPassword(
user.email.address,
password,
salt,
)
const encryptedMasterPassword = encryptString(masterPassword, encryptionKey)
const note: UserNote = {
theme,
privateKey: keyPair.privateKey,
}
const encryptedNotes = encryptUserNote(note, masterPassword)
const {encryptionPassword, encryptedPassword, encryptedNotes, publicKey} =
await setupEncryptionForUser({
password,
user,
serverSettings,
theme,
})
await mutateAsync(
{
encryptedPassword: encryptedMasterPassword,
encryptedPassword,
publicKey: (
await readKey({
armoredKey: keyPair.publicKey,
armoredKey: publicKey,
})
)
.toPublic()
@ -101,7 +85,7 @@ export default function PasswordForm({onDone}: PasswordFormProps): ReactElement
{
onSuccess: ({user: newUser}) => {
login(newUser)
_setEncryptionPassword(masterPassword)
_setEncryptionPassword(encryptionPassword)
navigateToNext()
},
},

View File

@ -0,0 +1,9 @@
import passwordGenerator from "secure-random-password"
export default function generateEncryptionPassword(): string {
// 16 characters ^ 64 combinations results in an entropy of 256 bits
return passwordGenerator.randomPassword({
length: 64,
characters: "0123456789ABCDEF",
})
}

View File

@ -12,3 +12,7 @@ export * from "./generate-keys"
export {default as generateKeys} from "./generate-keys"
export * from "./extract-cleartext-from-signed-message"
export {default as extractCleartextFromSignedMessage} from "./extract-cleartext-from-signed-message"
export * from "./generate-encryption-password"
export {default as generateEncryptionPassword} from "./generate-encryption-password"
export * from "./setup-encryption-for-user"
export {default as setupEncryptionForUser} from "./setup-encryption-for-user"

View File

@ -0,0 +1,50 @@
import {ServerSettings, ServerUser, Theme, User, UserNote} from "~/server-types"
import {encryptUserNote} from "~/utils/encrypt-user-note"
import encryptString from "./encrypt-string"
import generateEncryptionPassword from "./generate-encryption-password"
import generateKeys from "./generate-keys"
import getEncryptionPassword from "./get-encryption-password"
import getUserSalt from "./get-user-salt"
export interface SetupEncryptionForUserData {
password: string
user: ServerUser | User
serverSettings: ServerSettings
theme: Theme
}
export interface SetupEncryptionForUserResult {
encryptedPassword: string
encryptedNotes: string
encryptionPassword: string
publicKey: string
}
export default async function setupEncryptionForUser({
user,
serverSettings,
password,
theme,
}: SetupEncryptionForUserData): Promise<SetupEncryptionForUserResult> {
const keyPair = await generateKeys()
const masterPassword = generateEncryptionPassword()
const salt = getUserSalt(user, serverSettings)
const encryptionKey = await getEncryptionPassword(user.email.address, password, salt)
const encryptedMasterPassword = encryptString(masterPassword, encryptionKey)
const note: UserNote = {
theme,
privateKey: keyPair.privateKey,
}
const encryptedNotes = encryptUserNote(note, masterPassword)
return {
encryptedPassword: encryptedMasterPassword,
encryptedNotes,
encryptionPassword: masterPassword,
publicKey: keyPair.publicKey,
}
}