mirror of
https://github.com/Myzel394/kleckrelay-website.git
synced 2025-06-21 08:40:32 +02:00
improved cryptography
This commit is contained in:
parent
ee0682a8b6
commit
b828f75332
@ -1,13 +1,12 @@
|
|||||||
import {AliasNote} from "~/server-types"
|
import {AliasNote} from "~/server-types"
|
||||||
|
|
||||||
export const MASTER_PASSWORD_LENGTH = 1024
|
|
||||||
export const LOCAL_REGEX = /^[a-zA-Z0-9!#$%&‘*+–/=?^_`.{|}~-]{1,64}$/g
|
export const LOCAL_REGEX = /^[a-zA-Z0-9!#$%&‘*+–/=?^_`.{|}~-]{1,64}$/g
|
||||||
export const URL_REGEX =
|
export const URL_REGEX =
|
||||||
/((http[s]*:\/\/)?[a-z0-9-%\/\&=?\.]+\.[a-z]{2,4}\/?([^\s<>\#%"\,\{\}\\|\\\^\[\]`]+)?)/gi
|
/((http[s]*:\/\/)?[a-z0-9-%\/\&=?\.]+\.[a-z]{2,4}\/?([^\s<>\#%"\,\{\}\\|\\\^\[\]`]+)?)/gi
|
||||||
export const DEFAULT_ALIAS_NOTE: AliasNote = {
|
export const DEFAULT_ALIAS_NOTE: AliasNote = {
|
||||||
version: "1.0",
|
version: "1.0",
|
||||||
data: {
|
data: {
|
||||||
createdOn: "instance",
|
createdOn: "/aliases",
|
||||||
createdAt: null,
|
createdAt: null,
|
||||||
creationContext: "web",
|
creationContext: "web",
|
||||||
personalNotes: "",
|
personalNotes: "",
|
||||||
|
@ -37,6 +37,9 @@ export function CreateAliasButton(): ReactElement {
|
|||||||
JSON.stringify(
|
JSON.stringify(
|
||||||
update(DEFAULT_ALIAS_NOTE, {
|
update(DEFAULT_ALIAS_NOTE, {
|
||||||
data: {
|
data: {
|
||||||
|
createdOn: {
|
||||||
|
$set: window.location.origin + window.location.pathname,
|
||||||
|
},
|
||||||
createdAt: {
|
createdAt: {
|
||||||
$set: new Date(),
|
$set: new Date(),
|
||||||
},
|
},
|
||||||
|
@ -5,19 +5,16 @@ import {readKey} from "openpgp"
|
|||||||
import {AxiosError} from "axios"
|
import {AxiosError} from "axios"
|
||||||
import {useTranslation} from "react-i18next"
|
import {useTranslation} from "react-i18next"
|
||||||
import {useLoaderData} from "react-router-dom"
|
import {useLoaderData} from "react-router-dom"
|
||||||
import React, {ReactElement, useCallback, useContext, useMemo, useRef} from "react"
|
import React, {ReactElement, useCallback, useContext, useRef} from "react"
|
||||||
import passwordGenerator from "secure-random-password"
|
|
||||||
|
|
||||||
import {Box, InputAdornment} from "@mui/material"
|
import {Box, InputAdornment} from "@mui/material"
|
||||||
import {useMutation} from "@tanstack/react-query"
|
import {useMutation} from "@tanstack/react-query"
|
||||||
|
|
||||||
import {AuthContext, PasswordField, SimpleForm} from "~/components"
|
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 {useExtensionHandler, useNavigateToNext, useSystemPreferredTheme, useUser} from "~/hooks"
|
||||||
import {MASTER_PASSWORD_LENGTH} from "~/constants/values"
|
import {AuthenticationDetails, ServerSettings} from "~/server-types"
|
||||||
import {AuthenticationDetails, ServerSettings, UserNote} from "~/server-types"
|
|
||||||
import {UpdateAccountData, updateAccount} from "~/apis"
|
import {UpdateAccountData, updateAccount} from "~/apis"
|
||||||
import {encryptUserNote} from "~/utils/encrypt-user-note"
|
|
||||||
|
|
||||||
export interface PasswordFormProps {
|
export interface PasswordFormProps {
|
||||||
onDone: () => void
|
onDone: () => void
|
||||||
@ -54,7 +51,6 @@ export default function PasswordForm({onDone}: PasswordFormProps): ReactElement
|
|||||||
|
|
||||||
const {_setEncryptionPassword, login} = useContext(AuthContext)
|
const {_setEncryptionPassword, login} = useContext(AuthContext)
|
||||||
|
|
||||||
const waitForKeyGeneration = useMemo(generateKeys, [])
|
|
||||||
const {mutateAsync} = useMutation<AuthenticationDetails, AxiosError, UpdateAccountData>(
|
const {mutateAsync} = useMutation<AuthenticationDetails, AxiosError, UpdateAccountData>(
|
||||||
updateAccount,
|
updateAccount,
|
||||||
)
|
)
|
||||||
@ -66,32 +62,20 @@ export default function PasswordForm({onDone}: PasswordFormProps): ReactElement
|
|||||||
},
|
},
|
||||||
onSubmit: async ({password}, {setErrors}) => {
|
onSubmit: async ({password}, {setErrors}) => {
|
||||||
try {
|
try {
|
||||||
const keyPair = await waitForKeyGeneration
|
const {encryptionPassword, encryptedPassword, encryptedNotes, publicKey} =
|
||||||
|
await setupEncryptionForUser({
|
||||||
const masterPassword = passwordGenerator.randomPassword({
|
|
||||||
length: MASTER_PASSWORD_LENGTH,
|
|
||||||
})
|
|
||||||
|
|
||||||
const salt = getUserSalt(user, serverSettings)
|
|
||||||
const encryptionKey = await getEncryptionPassword(
|
|
||||||
user.email.address,
|
|
||||||
password,
|
password,
|
||||||
salt,
|
user,
|
||||||
)
|
serverSettings,
|
||||||
const encryptedMasterPassword = encryptString(masterPassword, encryptionKey)
|
|
||||||
|
|
||||||
const note: UserNote = {
|
|
||||||
theme,
|
theme,
|
||||||
privateKey: keyPair.privateKey,
|
})
|
||||||
}
|
|
||||||
const encryptedNotes = encryptUserNote(note, masterPassword)
|
|
||||||
|
|
||||||
await mutateAsync(
|
await mutateAsync(
|
||||||
{
|
{
|
||||||
encryptedPassword: encryptedMasterPassword,
|
encryptedPassword,
|
||||||
publicKey: (
|
publicKey: (
|
||||||
await readKey({
|
await readKey({
|
||||||
armoredKey: keyPair.publicKey,
|
armoredKey: publicKey,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.toPublic()
|
.toPublic()
|
||||||
@ -101,7 +85,7 @@ export default function PasswordForm({onDone}: PasswordFormProps): ReactElement
|
|||||||
{
|
{
|
||||||
onSuccess: ({user: newUser}) => {
|
onSuccess: ({user: newUser}) => {
|
||||||
login(newUser)
|
login(newUser)
|
||||||
_setEncryptionPassword(masterPassword)
|
_setEncryptionPassword(encryptionPassword)
|
||||||
navigateToNext()
|
navigateToNext()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
9
src/utils/crypto/generate-encryption-password.ts
Normal file
9
src/utils/crypto/generate-encryption-password.ts
Normal 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",
|
||||||
|
})
|
||||||
|
}
|
@ -12,3 +12,7 @@ export * from "./generate-keys"
|
|||||||
export {default as generateKeys} from "./generate-keys"
|
export {default as generateKeys} from "./generate-keys"
|
||||||
export * from "./extract-cleartext-from-signed-message"
|
export * from "./extract-cleartext-from-signed-message"
|
||||||
export {default as extractCleartextFromSignedMessage} 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"
|
||||||
|
50
src/utils/crypto/setup-encryption-for-user.ts
Normal file
50
src/utils/crypto/setup-encryption-for-user.ts
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user