-
-
Notifications
You must be signed in to change notification settings - Fork 71
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(packages): mutualised encryption in a lib package (#63)
- Loading branch information
1 parent
07de2eb
commit 10645e0
Showing
32 changed files
with
1,906 additions
and
220 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
name: CI - Lib | ||
|
||
on: | ||
pull_request: | ||
push: | ||
branches: | ||
- main | ||
|
||
jobs: | ||
ci-lib: | ||
runs-on: ubuntu-latest | ||
defaults: | ||
run: | ||
working-directory: packages/lib | ||
|
||
steps: | ||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 | ||
- run: corepack enable | ||
- uses: actions/setup-node@v4 | ||
with: | ||
node-version: 22 | ||
corepack: true | ||
cache: 'pnpm' | ||
|
||
- name: Install dependencies | ||
run: pnpm i | ||
working-directory: ./ | ||
|
||
- name: Run linters | ||
run: pnpm lint | ||
|
||
# - name: Type check | ||
# run: pnpm typecheck | ||
|
||
- name: Run unit test | ||
run: pnpm test | ||
|
||
- name: Build the lib | ||
run: pnpm build |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
4 changes: 2 additions & 2 deletions
4
packages/app-client/src/modules/notes/components/note-password-field.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,41 +1,13 @@ | ||
import { createBuffer, mergeBuffers } from '../shared/crypto/buffer'; | ||
import { aesDecrypt, aesEncrypt, deriveKey } from '../shared/crypto/encryption'; | ||
import _ from 'lodash-es'; | ||
|
||
export { encryptNoteContent, decryptNoteContent, createNoteUrl, deriveMasterKey }; | ||
export { createRandomPassword }; | ||
|
||
function createBufferFromPassword({ password }: { password?: string }) { | ||
if (!password) { | ||
return new Uint8Array(0); | ||
} | ||
function createRandomPassword({ length = 16 }: { length?: number } = {}): string { | ||
const alphabet = 'abcdefghijklmnopqrstuvwxyz'; | ||
const numbers = '0123456789'; | ||
const specialChars = '!@#$%^&*()_+'; | ||
|
||
return new TextEncoder().encode(password); | ||
} | ||
|
||
async function encryptNoteContent({ content, masterKey }: { content: string; masterKey: Uint8Array }) { | ||
const contentBuffer = createBuffer({ value: content }); | ||
const encryptedContent = await aesEncrypt({ data: contentBuffer, key: masterKey }); | ||
|
||
return encryptedContent; | ||
} | ||
|
||
async function decryptNoteContent({ encryptedContent, masterKey }: { encryptedContent: string; masterKey: Uint8Array }) { | ||
const decryptedContent = await aesDecrypt({ data: encryptedContent, key: masterKey }); | ||
|
||
return decryptedContent; | ||
} | ||
|
||
function createNoteUrl({ noteId, encryptionKey }: { noteId: string; encryptionKey: string }): { noteUrl: string } { | ||
const url = new URL(`/${noteId}`, window.location.origin); | ||
url.hash = encryptionKey; | ||
|
||
const noteUrl = url.toString(); | ||
|
||
return { noteUrl }; | ||
} | ||
|
||
function deriveMasterKey({ baseKey, password }: { baseKey: Uint8Array; password?: string }) { | ||
const passwordBuffer = createBufferFromPassword({ password }); | ||
const mergedBuffers = mergeBuffers(baseKey, passwordBuffer); | ||
const corpus = alphabet + alphabet.toUpperCase() + numbers + specialChars; | ||
|
||
return deriveKey({ key: mergedBuffers }); | ||
return _.times(length, () => _.sample(corpus)).join(''); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,54 +1,17 @@ | ||
import { base64UrlToBuffer, bufferToBase64Url } from '../shared/crypto/buffer'; | ||
import { createRandomBuffer } from '../shared/random/random'; | ||
import { createNoteUrl, decryptNoteContent, deriveMasterKey, encryptNoteContent } from './notes.models'; | ||
import { createNote } from './notes.services'; | ||
import { createNote } from '@enclosed/lib'; | ||
import { storeNote } from './notes.services'; | ||
|
||
export { encryptAndCreateNote, decryptNote, encryptNote }; | ||
export { encryptAndCreateNote }; | ||
|
||
async function encryptAndCreateNote({ | ||
content, | ||
password, | ||
ttlInSeconds, | ||
deleteAfterReading, | ||
storeNote = createNote, | ||
}: { | ||
async function encryptAndCreateNote(args: { | ||
content: string; | ||
password?: string; | ||
ttlInSeconds: number; | ||
deleteAfterReading: boolean; | ||
storeNote?: (params: { content: string; isPasswordProtected: boolean; ttlInSeconds: number; deleteAfterReading: boolean }) => Promise<{ noteId: string }>; | ||
}) { | ||
const { encryptedContent, encryptionKey } = await encryptNote({ content, password }); | ||
|
||
// Send the encrypted note to the server for storage, the server has no knowledge of the encryption key | ||
const { noteId } = await storeNote({ content: encryptedContent, isPasswordProtected: Boolean(password), ttlInSeconds, deleteAfterReading }); | ||
|
||
// The base key is stored in the URL hash fragment | ||
const { noteUrl } = createNoteUrl({ noteId, encryptionKey }); | ||
|
||
return { encryptedContent, noteId, encryptionKey, noteUrl }; | ||
} | ||
|
||
async function encryptNote({ content, password }: { content: string; password?: string }) { | ||
// The base key ensure e2e encryption even if the user does not provide a password | ||
const baseKey = createRandomBuffer({ length: 32 }); | ||
|
||
// If the user provides a password, we derive a master key from the base key and the password using PBKDF2 | ||
const masterKey = await deriveMasterKey({ baseKey, password }); | ||
|
||
const encryptedContent = await encryptNoteContent({ content, masterKey }); | ||
|
||
const encryptionKey = bufferToBase64Url({ buffer: baseKey }); | ||
|
||
return { encryptedContent, encryptionKey }; | ||
} | ||
|
||
async function decryptNote({ encryptedContent, password, encryptionKey }: { encryptedContent: string; password?: string; encryptionKey: string }) { | ||
const baseKey = base64UrlToBuffer({ base64Url: encryptionKey }); | ||
|
||
const masterKey = await deriveMasterKey({ baseKey, password }); | ||
|
||
const decryptedContent = await decryptNoteContent({ encryptedContent, masterKey }); | ||
|
||
return { decryptedContent }; | ||
return createNote({ | ||
...args, | ||
storeNote, | ||
clientBaseUrl: window.location.origin, | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
56 changes: 0 additions & 56 deletions
56
packages/app-client/src/modules/shared/crypto/encryption.ts
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { defineBuildConfig } from 'unbuild'; | ||
|
||
export default defineBuildConfig({ | ||
entries: [ | ||
'src/index.node', | ||
'src/index.web', | ||
], | ||
|
||
declaration: true, | ||
sourcemap: true, | ||
rollup: { | ||
emitCJS: true, | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import antfu from '@antfu/eslint-config'; | ||
|
||
export default antfu({ | ||
stylistic: { | ||
semi: true, | ||
}, | ||
|
||
rules: { | ||
// To allow export on top of files | ||
'ts/no-use-before-define': ['error', { allowNamedExports: true, functions: false }], | ||
'curly': ['error', 'all'], | ||
'vitest/consistent-test-it': ['error', { fn: 'test' }], | ||
'ts/consistent-type-definitions': ['error', 'type'], | ||
'style/brace-style': ['error', '1tbs', { allowSingleLine: false }], | ||
'unused-imports/no-unused-vars': ['error', { | ||
argsIgnorePattern: '^_', | ||
varsIgnorePattern: '^_', | ||
caughtErrorsIgnorePattern: '^_', | ||
}], | ||
}, | ||
}); |
Oops, something went wrong.