From c7e24b4b820bd37fc9e39158014bd35f04d09bbd Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Wed, 18 Dec 2024 14:45:41 +0100 Subject: [PATCH 1/9] use worker --- backend/package.json | 3 +- .../src/graphql/resolver/UserResolver.test.ts | 19 ++-- backend/src/graphql/resolver/UserResolver.ts | 10 +- backend/src/password/EncryptionWorker.ts | 50 ++++++++++ backend/src/password/EncryptorUtils.ts | 93 +++++++++---------- backend/src/password/PasswordEncryptor.ts | 11 ++- backend/src/seeds/factory/creation.ts | 1 - backend/yarn.lock | 7 +- 8 files changed, 125 insertions(+), 69 deletions(-) create mode 100644 backend/src/password/EncryptionWorker.ts diff --git a/backend/package.json b/backend/package.json index db89dfbe7..df138106e 100644 --- a/backend/package.json +++ b/backend/package.json @@ -51,7 +51,8 @@ "type-graphql": "^1.1.1", "typed-rest-client": "^1.8.11", "uuid": "^8.3.2", - "xregexp": "^5.1.1" + "xregexp": "^5.1.1", + "workerpool": "^9.2.0" }, "devDependencies": { "@eslint-community/eslint-plugin-eslint-comments": "^3.2.1", diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index c0a707577..b8e4a5750 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -587,8 +587,8 @@ describe('UserResolver', () => { expect(newUser.emailContact.emailChecked).toBeTruthy() }) - it('updates the password', () => { - const encryptedPass = encryptPassword(newUser, 'Aa12345_') + it('updates the password', async () => { + const encryptedPass = await encryptPassword(newUser, 'Aa12345_') expect(newUser.password.toString()).toEqual(encryptedPass.toString()) }) @@ -1546,7 +1546,9 @@ describe('UserResolver', () => { expect(bibi).toEqual( expect.objectContaining({ - password: SecretKeyCryptographyCreateKey(bibi.gradidoID.toString(), 'Aa12345_')[0] + password: Buffer.from( + (await SecretKeyCryptographyCreateKey(bibi.gradidoID.toString(), 'Aa12345_'))[0], + ) .readBigUInt64LE() .toString(), passwordEncryptionType: PasswordEncryptionType.GRADIDO_ID, @@ -1570,10 +1572,9 @@ describe('UserResolver', () => { }) bibi = usercontact.user bibi.passwordEncryptionType = PasswordEncryptionType.EMAIL - bibi.password = SecretKeyCryptographyCreateKey( - 'bibi@bloxberg.de', - 'Aa12345_', - )[0].readBigUInt64LE() + bibi.password = Buffer.from( + (await SecretKeyCryptographyCreateKey('bibi@bloxberg.de', 'Aa12345_'))[0], + ).readBigUInt64LE() await bibi.save() }) @@ -1590,7 +1591,9 @@ describe('UserResolver', () => { expect(bibi).toEqual( expect.objectContaining({ firstName: 'Bibi', - password: SecretKeyCryptographyCreateKey(bibi.gradidoID.toString(), 'Aa12345_')[0] + password: Buffer.from( + (await SecretKeyCryptographyCreateKey(bibi.gradidoID.toString(), 'Aa12345_'))[0], + ) .readBigUInt64LE() .toString(), passwordEncryptionType: PasswordEncryptionType.GRADIDO_ID, diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 0302c7860..4b5fd3118 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -162,7 +162,7 @@ export class UserResolver { // TODO we want to catch this on the frontend and ask the user to check his emails or resend code throw new LogError('The User has not set a password yet', dbUser) } - if (!verifyPassword(dbUser, password)) { + if (!(await verifyPassword(dbUser, password))) { throw new LogError('No user with this credentials', dbUser) } @@ -178,7 +178,7 @@ export class UserResolver { if (dbUser.passwordEncryptionType !== PasswordEncryptionType.GRADIDO_ID) { dbUser.passwordEncryptionType = PasswordEncryptionType.GRADIDO_ID - dbUser.password = encryptPassword(dbUser, password) + dbUser.password = await encryptPassword(dbUser, password) await dbUser.save() } // add pubKey in logger-context for layout-pattern X{user} to print it in each logging message @@ -502,7 +502,7 @@ export class UserResolver { // Update Password user.passwordEncryptionType = PasswordEncryptionType.GRADIDO_ID - user.password = encryptPassword(user, password) + user.password = await encryptPassword(user, password) logger.debug('User credentials updated ...') const queryRunner = getConnection().createQueryRunner() @@ -632,13 +632,13 @@ export class UserResolver { ) } - if (!verifyPassword(user, password)) { + if (!(await verifyPassword(user, password))) { throw new LogError(`Old password is invalid`) } // Save new password hash and newly encrypted private key user.passwordEncryptionType = PasswordEncryptionType.GRADIDO_ID - user.password = encryptPassword(user, passwordNew) + user.password = await encryptPassword(user, passwordNew) } // Save hideAmountGDD value diff --git a/backend/src/password/EncryptionWorker.ts b/backend/src/password/EncryptionWorker.ts new file mode 100644 index 000000000..2299a1a9b --- /dev/null +++ b/backend/src/password/EncryptionWorker.ts @@ -0,0 +1,50 @@ +import { worker } from 'workerpool' + +import { + crypto_box_SEEDBYTES, + crypto_hash_sha512_init, + crypto_hash_sha512_update, + crypto_hash_sha512_final, + crypto_hash_sha512_BYTES, + crypto_hash_sha512_STATEBYTES, + crypto_shorthash_BYTES, + crypto_pwhash_SALTBYTES, + crypto_pwhash, + crypto_shorthash, +} from 'sodium-native' + +export const SecretKeyCryptographyCreateKey = ( + salt: string, + password: string, + configLoginAppSecret: Buffer, + configLoginServerKey: Buffer, +): Buffer[] => { + const state = Buffer.alloc(crypto_hash_sha512_STATEBYTES) + crypto_hash_sha512_init(state) + crypto_hash_sha512_update(state, Buffer.from(salt)) + crypto_hash_sha512_update(state, configLoginAppSecret) + const hash = Buffer.alloc(crypto_hash_sha512_BYTES) + crypto_hash_sha512_final(state, hash) + + const encryptionKey = Buffer.alloc(crypto_box_SEEDBYTES) + const opsLimit = 10 + const memLimit = 33554432 + const algo = 2 + crypto_pwhash( + encryptionKey, + Buffer.from(password), + hash.slice(0, crypto_pwhash_SALTBYTES), + opsLimit, + memLimit, + algo, + ) + + const encryptionKeyHash = Buffer.alloc(crypto_shorthash_BYTES) + crypto_shorthash(encryptionKeyHash, encryptionKey, configLoginServerKey) + + return [encryptionKeyHash, encryptionKey] +} + +worker({ + SecretKeyCryptographyCreateKey, +}) diff --git a/backend/src/password/EncryptorUtils.ts b/backend/src/password/EncryptorUtils.ts index 64dcb2289..74b9f91d0 100644 --- a/backend/src/password/EncryptorUtils.ts +++ b/backend/src/password/EncryptorUtils.ts @@ -2,7 +2,11 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ +import { cpus } from 'os' +import path from 'path' + import { User } from '@entity/User' +import { pool } from 'workerpool' import { PasswordEncryptionType } from '@enum/PasswordEncryptionType' @@ -10,61 +14,54 @@ import { CONFIG } from '@/config' import { LogError } from '@/server/LogError' import { backendLogger as logger } from '@/server/logger' -import { - crypto_shorthash_KEYBYTES, - crypto_box_SEEDBYTES, - crypto_hash_sha512_init, - crypto_hash_sha512_update, - crypto_hash_sha512_final, - crypto_hash_sha512_BYTES, - crypto_hash_sha512_STATEBYTES, - crypto_shorthash_BYTES, - crypto_pwhash_SALTBYTES, - crypto_pwhash, - crypto_shorthash, -} from 'sodium-native' +import { crypto_shorthash_KEYBYTES } from 'sodium-native' + +const configLoginAppSecret = Buffer.from(CONFIG.LOGIN_APP_SECRET, 'hex') +const configLoginServerKey = Buffer.from(CONFIG.LOGIN_SERVER_KEY, 'hex') + +// TODO: put maxQueueSize into config +const encryptionWorkerPool = pool( + path.join(__dirname, '..', '..', 'build', 'src', 'password', '/EncryptionWorker.js'), + { + maxQueueSize: 30 * cpus().length, + }, +) // We will reuse this for changePassword export const isValidPassword = (password: string): boolean => { return !!password.match(/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[^a-zA-Z0-9 \\t\\n\\r]).{8,}$/) } -export const SecretKeyCryptographyCreateKey = (salt: string, password: string): Buffer[] => { - logger.trace('SecretKeyCryptographyCreateKey...') - const configLoginAppSecret = Buffer.from(CONFIG.LOGIN_APP_SECRET, 'hex') - const configLoginServerKey = Buffer.from(CONFIG.LOGIN_SERVER_KEY, 'hex') - if (configLoginServerKey.length !== crypto_shorthash_KEYBYTES) { - throw new LogError( - 'ServerKey has an invalid size', - configLoginServerKey.length, - crypto_shorthash_KEYBYTES, - ) +/** + * @param salt + * @param password + * @returns can throw an exception if worker pool is full, if more than 30 * cpu core count logins happen in a time range of 30 seconds + */ +export const SecretKeyCryptographyCreateKey = async ( + salt: string, + password: string, +): Promise => { + try { + logger.trace('call worker for: SecretKeyCryptographyCreateKey') + if (configLoginServerKey.length !== crypto_shorthash_KEYBYTES) { + throw new LogError( + 'ServerKey has an invalid size', + configLoginServerKey.length, + crypto_shorthash_KEYBYTES, + ) + } + return (await encryptionWorkerPool.exec('SecretKeyCryptographyCreateKey', [ + salt, + password, + configLoginAppSecret, + configLoginServerKey, + ])) as Promise + } catch (e) { + // pool is throwing this error + // throw new Error('Max queue size of ' + this.maxQueueSize + ' reached'); + // will be shown in frontend to user + throw new LogError('Server is full, please try again in 10 minutes.', e) } - - const state = Buffer.alloc(crypto_hash_sha512_STATEBYTES) - crypto_hash_sha512_init(state) - crypto_hash_sha512_update(state, Buffer.from(salt)) - crypto_hash_sha512_update(state, configLoginAppSecret) - const hash = Buffer.alloc(crypto_hash_sha512_BYTES) - crypto_hash_sha512_final(state, hash) - - const encryptionKey = Buffer.alloc(crypto_box_SEEDBYTES) - const opsLimit = 10 - const memLimit = 33554432 - const algo = 2 - crypto_pwhash( - encryptionKey, - Buffer.from(password), - hash.slice(0, crypto_pwhash_SALTBYTES), - opsLimit, - memLimit, - algo, - ) - - const encryptionKeyHash = Buffer.alloc(crypto_shorthash_BYTES) - crypto_shorthash(encryptionKeyHash, encryptionKey, configLoginServerKey) - - return [encryptionKeyHash, encryptionKey] } export const getUserCryptographicSalt = (dbUser: User): string => { diff --git a/backend/src/password/PasswordEncryptor.ts b/backend/src/password/PasswordEncryptor.ts index 1c7457a40..1aa740672 100644 --- a/backend/src/password/PasswordEncryptor.ts +++ b/backend/src/password/PasswordEncryptor.ts @@ -3,13 +3,14 @@ import { User } from '@entity/User' // import { logger } from '@test/testSetup' getting error "jest is not defined" import { getUserCryptographicSalt, SecretKeyCryptographyCreateKey } from './EncryptorUtils' -export const encryptPassword = (dbUser: User, password: string): bigint => { +export const encryptPassword = async (dbUser: User, password: string): Promise => { const salt = getUserCryptographicSalt(dbUser) - const keyBuffer = SecretKeyCryptographyCreateKey(salt, password) // return short and long hash - const passwordHash = keyBuffer[0].readBigUInt64LE() + const keyBuffer = await SecretKeyCryptographyCreateKey(salt, password) // return short and long hash + const passwordHash = Buffer.from(keyBuffer[0]).readBigUInt64LE() return passwordHash } -export const verifyPassword = (dbUser: User, password: string): boolean => { - return dbUser.password.toString() === encryptPassword(dbUser, password).toString() +export const verifyPassword = async (dbUser: User, password: string): Promise => { + const encryptedPassword = await encryptPassword(dbUser, password) + return dbUser.password.toString() === encryptedPassword.toString() } diff --git a/backend/src/seeds/factory/creation.ts b/backend/src/seeds/factory/creation.ts index 5ad1b2e4e..5e6e058f9 100644 --- a/backend/src/seeds/factory/creation.ts +++ b/backend/src/seeds/factory/creation.ts @@ -23,7 +23,6 @@ export const creationFactory = async ( mutation: login, variables: { email: creation.email, password: 'Aa12345_' }, }) - const { data: { createContribution: contribution }, } = await mutate({ mutation: createContribution, variables: { ...creation } }) diff --git a/backend/yarn.lock b/backend/yarn.lock index effdd77f2..571e2eb59 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -3709,7 +3709,7 @@ graceful-fs@^4.1.6, graceful-fs@^4.2.0: integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== "gradido-database@file:../database": - version "2.3.1" + version "2.4.1" dependencies: "@types/uuid" "^8.3.4" cross-env "^7.0.3" @@ -7369,6 +7369,11 @@ word-wrap@^1.2.3, word-wrap@~1.2.3: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== +workerpool@^9.2.0: + version "9.2.0" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-9.2.0.tgz#f74427cbb61234708332ed8ab9cbf56dcb1c4371" + integrity sha512-PKZqBOCo6CYkVOwAxWxQaSF2Fvb5Iv2fCeTP7buyWI2GiynWr46NcXSgK/idoV6e60dgCBfgYc+Un3HMvmqP8w== + wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" From 53ce67ce921b1208f83457baf73f4f0a6fec7712 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Wed, 18 Dec 2024 14:55:50 +0100 Subject: [PATCH 2/9] make sure login delay is long enough to prevent email guessing --- backend/src/graphql/resolver/UserResolver.ts | 12 +++++++++++- backend/src/util/utilities.ts | 4 ++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 4b5fd3118..f530ed4da 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -86,6 +86,7 @@ import { setUserRole, deleteUserRole } from './util/modifyUserRole' import { sendUserToGms } from './util/sendUserToGms' import { syncHumhub } from './util/syncHumhub' import { validateAlias } from './util/validateAlias' +import { delay } from '@/util/utilities' const LANGUAGES = ['de', 'en', 'es', 'fr', 'nl'] const DEFAULT_LANGUAGE = 'de' @@ -150,7 +151,16 @@ export class UserResolver { ): Promise { logger.info(`login with ${email}, ***, ${publisherId} ...`) email = email.trim().toLowerCase() - const dbUser = await findUserByEmail(email) + let dbUser: DbUser + + try { + dbUser = await findUserByEmail(email) + } catch (e) { + // simulate delay which occur on password encryption 650 ms +- 50 rnd + await delay(650 + Math.floor(Math.random() * 101) - 50) + throw e + } + if (dbUser.deletedAt) { throw new LogError('This user was permanently deleted. Contact support for questions', dbUser) } diff --git a/backend/src/util/utilities.ts b/backend/src/util/utilities.ts index 905cce686..4f45af023 100644 --- a/backend/src/util/utilities.ts +++ b/backend/src/util/utilities.ts @@ -1,3 +1,5 @@ +import { promisify } from 'util' + import { Decimal } from 'decimal.js-light' import i18n from 'i18n' @@ -30,6 +32,8 @@ export function resetInterface>(obj: T): T { return obj } +export const delay = promisify(setTimeout) + export const ensureUrlEndsWithSlash = (url: string): string => { return url.endsWith('/') ? url : url.concat('/') } From b43a2e21f89f4ecc270a73f230fea4cb1958a03b Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Wed, 18 Dec 2024 15:14:29 +0100 Subject: [PATCH 3/9] fix lint --- backend/src/graphql/resolver/UserResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index f530ed4da..e3c7f5fa5 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -69,6 +69,7 @@ import { backendLogger as logger } from '@/server/logger' import { communityDbUser } from '@/util/communityUser' import { hasElopageBuys } from '@/util/hasElopageBuys' import { getTimeDurationObject, printTimeDuration } from '@/util/time' +import { delay } from '@/util/utilities' import random from 'random-bigint' import { randombytes_random } from 'sodium-native' @@ -86,7 +87,6 @@ import { setUserRole, deleteUserRole } from './util/modifyUserRole' import { sendUserToGms } from './util/sendUserToGms' import { syncHumhub } from './util/syncHumhub' import { validateAlias } from './util/validateAlias' -import { delay } from '@/util/utilities' const LANGUAGES = ['de', 'en', 'es', 'fr', 'nl'] const DEFAULT_LANGUAGE = 'de' From 2a93cfa095b0d2f7f0c50bdcce74f527d6c6bbf2 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Fri, 20 Dec 2024 13:15:42 +0100 Subject: [PATCH 4/9] mock parallel worker --- .../src/password/__mocks__/EncryptorUtils.ts | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 backend/src/password/__mocks__/EncryptorUtils.ts diff --git a/backend/src/password/__mocks__/EncryptorUtils.ts b/backend/src/password/__mocks__/EncryptorUtils.ts new file mode 100644 index 000000000..b406f1b1c --- /dev/null +++ b/backend/src/password/__mocks__/EncryptorUtils.ts @@ -0,0 +1,112 @@ +/* eslint-disable @typescript-eslint/no-unsafe-call */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +import { User } from '@entity/User' + +import { PasswordEncryptionType } from '@enum/PasswordEncryptionType' + +import { CONFIG } from '@/config' +import { LogError } from '@/server/LogError' +import { backendLogger as logger } from '@/server/logger' + +import { + crypto_shorthash_KEYBYTES, + crypto_box_SEEDBYTES, + crypto_hash_sha512_init, + crypto_hash_sha512_update, + crypto_hash_sha512_final, + crypto_hash_sha512_BYTES, + crypto_hash_sha512_STATEBYTES, + crypto_shorthash_BYTES, + crypto_pwhash_SALTBYTES, + crypto_pwhash, + crypto_shorthash, +} from 'sodium-native' + +const SecretKeyCryptographyCreateKeyMock = ( + salt: string, + password: string, + configLoginAppSecret: Buffer, + configLoginServerKey: Buffer, +): Uint8Array[] => { + const state = Buffer.alloc(crypto_hash_sha512_STATEBYTES) + crypto_hash_sha512_init(state) + crypto_hash_sha512_update(state, Buffer.from(salt)) + crypto_hash_sha512_update(state, configLoginAppSecret) + const hash = Buffer.alloc(crypto_hash_sha512_BYTES) + crypto_hash_sha512_final(state, hash) + + const encryptionKey = Buffer.alloc(crypto_box_SEEDBYTES) + const opsLimit = 10 + const memLimit = 33554432 + const algo = 2 + crypto_pwhash( + encryptionKey, + Buffer.from(password), + hash.slice(0, crypto_pwhash_SALTBYTES), + opsLimit, + memLimit, + algo, + ) + + const encryptionKeyHash = Buffer.alloc(crypto_shorthash_BYTES) + crypto_shorthash(encryptionKeyHash, encryptionKey, configLoginServerKey) + + return [new Uint8Array(encryptionKeyHash), new Uint8Array(encryptionKey)] +} + +const configLoginAppSecret = Buffer.from(CONFIG.LOGIN_APP_SECRET, 'hex') +const configLoginServerKey = Buffer.from(CONFIG.LOGIN_SERVER_KEY, 'hex') + +// We will reuse this for changePassword +export const isValidPassword = (password: string): boolean => { + return !!password.match(/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[^a-zA-Z0-9 \\t\\n\\r]).{8,}$/) +} + +/** + * @param salt + * @param password + * @returns can throw an exception if worker pool is full, if more than 30 * cpu core count logins happen in a time range of 30 seconds + */ +export const SecretKeyCryptographyCreateKey = async ( + salt: string, + password: string, +): Promise => { + try { + logger.trace('call worker for: SecretKeyCryptographyCreateKey') + if (configLoginServerKey.length !== crypto_shorthash_KEYBYTES) { + throw new LogError( + 'ServerKey has an invalid size', + configLoginServerKey.length, + crypto_shorthash_KEYBYTES, + ) + } + return Promise.resolve( + SecretKeyCryptographyCreateKeyMock( + salt, + password, + configLoginAppSecret, + configLoginServerKey, + ), + ) + } catch (e) { + // pool is throwing this error + // throw new Error('Max queue size of ' + this.maxQueueSize + ' reached'); + // will be shown in frontend to user + throw new LogError('Server is full, please try again in 10 minutes.', e) + } +} + +export const getUserCryptographicSalt = (dbUser: User): string => { + switch (dbUser.passwordEncryptionType) { + case PasswordEncryptionType.NO_PASSWORD: + throw new LogError('User has no password set', dbUser.id) + case PasswordEncryptionType.EMAIL: + return dbUser.emailContact.email + case PasswordEncryptionType.GRADIDO_ID: + return dbUser.gradidoID + default: + throw new LogError('Unknown password encryption type', dbUser.passwordEncryptionType) + } +} From 6f59b3a8549a318390bec4f955f644a0f7f9fe52 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Fri, 20 Dec 2024 14:40:05 +0100 Subject: [PATCH 5/9] mock password worker in test, build worker for seeding in e2e-tests --- .github/workflows/test_e2e.yml | 2 +- backend/src/graphql/resolver/CommunityResolver.test.ts | 2 ++ backend/src/graphql/resolver/ContributionLinkResolver.test.ts | 2 ++ .../src/graphql/resolver/ContributionMessageResolver.test.ts | 1 + backend/src/graphql/resolver/ContributionResolver.test.ts | 1 + backend/src/graphql/resolver/KlicktippResolver.test.ts | 2 ++ backend/src/graphql/resolver/TransactionLinkResolver.test.ts | 2 ++ backend/src/graphql/resolver/TransactionResolver.test.ts | 2 ++ backend/src/graphql/resolver/UserResolver.test.ts | 1 + backend/src/graphql/resolver/semaphore.test.ts | 2 ++ backend/src/graphql/resolver/util/creations.test.ts | 2 ++ .../src/graphql/resolver/util/findUserByIdentifiers.test.ts | 2 ++ .../resolver/util/sendTransactionsToDltConnector.test.ts | 2 ++ backend/src/password/EncryptionWorker.ts | 4 ++-- backend/src/password/EncryptorUtils.ts | 2 +- backend/src/util/klicktipp.test.ts | 1 + 16 files changed, 26 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test_e2e.yml b/.github/workflows/test_e2e.yml index bdf979bf9..120fd0db3 100644 --- a/.github/workflows/test_e2e.yml +++ b/.github/workflows/test_e2e.yml @@ -35,7 +35,7 @@ jobs: cd database yarn && yarn dev_reset cd ../backend - yarn && yarn seed + yarn && yarn build && yarn seed - name: Boot up test system | docker-compose mailserver run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps mailserver diff --git a/backend/src/graphql/resolver/CommunityResolver.test.ts b/backend/src/graphql/resolver/CommunityResolver.test.ts index 8b5c05d5b..f9e6aa9ac 100644 --- a/backend/src/graphql/resolver/CommunityResolver.test.ts +++ b/backend/src/graphql/resolver/CommunityResolver.test.ts @@ -28,6 +28,8 @@ import { peterLustig } from '@/seeds/users/peter-lustig' import { getCommunityByUuid } from './util/communities' +jest.mock('@/password/EncryptorUtils') + // to do: We need a setup for the tests that closes the connection let mutate: ApolloServerTestClient['mutate'], query: ApolloServerTestClient['query'], diff --git a/backend/src/graphql/resolver/ContributionLinkResolver.test.ts b/backend/src/graphql/resolver/ContributionLinkResolver.test.ts index 23be529d3..3a3196c85 100644 --- a/backend/src/graphql/resolver/ContributionLinkResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionLinkResolver.test.ts @@ -22,6 +22,8 @@ import { listContributionLinks } from '@/seeds/graphql/queries' import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' import { peterLustig } from '@/seeds/users/peter-lustig' +jest.mock('@/password/EncryptorUtils') + let mutate: ApolloServerTestClient['mutate'], query: ApolloServerTestClient['query'], con: Connection diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts index 88d79d5c6..a82f623cc 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts @@ -27,6 +27,7 @@ import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' import { bobBaumeister } from '@/seeds/users/bob-baumeister' import { peterLustig } from '@/seeds/users/peter-lustig' +jest.mock('@/password/EncryptorUtils') jest.mock('@/emails/sendEmailVariants', () => { const originalModule = jest.requireActual('@/emails/sendEmailVariants') return { diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 9317b76e7..5c7a3e77d 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -59,6 +59,7 @@ import { stephenHawking } from '@/seeds/users/stephen-hawking' import { getFirstDayOfPreviousNMonth } from '@/util/utilities' jest.mock('@/emails/sendEmailVariants') +jest.mock('@/password/EncryptorUtils') let mutate: ApolloServerTestClient['mutate'], query: ApolloServerTestClient['query'], diff --git a/backend/src/graphql/resolver/KlicktippResolver.test.ts b/backend/src/graphql/resolver/KlicktippResolver.test.ts index 2c819f070..06430c620 100644 --- a/backend/src/graphql/resolver/KlicktippResolver.test.ts +++ b/backend/src/graphql/resolver/KlicktippResolver.test.ts @@ -15,6 +15,8 @@ import { userFactory } from '@/seeds/factory/user' import { login, subscribeNewsletter, unsubscribeNewsletter } from '@/seeds/graphql/mutations' import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' +jest.mock('@/password/EncryptorUtils') + let testEnv: any, mutate: any, con: any beforeAll(async () => { diff --git a/backend/src/graphql/resolver/TransactionLinkResolver.test.ts b/backend/src/graphql/resolver/TransactionLinkResolver.test.ts index fe047b35a..bcea43416 100644 --- a/backend/src/graphql/resolver/TransactionLinkResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionLinkResolver.test.ts @@ -38,6 +38,8 @@ import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK' import { transactionLinkCode } from './TransactionLinkResolver' +jest.mock('@/password/EncryptorUtils') + // mock semaphore to allow use fake timers jest.mock('@/util/TRANSACTIONS_LOCK') TRANSACTIONS_LOCK.acquire = jest.fn().mockResolvedValue(jest.fn()) diff --git a/backend/src/graphql/resolver/TransactionResolver.test.ts b/backend/src/graphql/resolver/TransactionResolver.test.ts index 4bf5ab493..7cc6143e6 100644 --- a/backend/src/graphql/resolver/TransactionResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionResolver.test.ts @@ -37,6 +37,8 @@ import { garrickOllivander } from '@/seeds/users/garrick-ollivander' import { peterLustig } from '@/seeds/users/peter-lustig' import { stephenHawking } from '@/seeds/users/stephen-hawking' +jest.mock('@/password/EncryptorUtils') + let mutate: ApolloServerTestClient['mutate'], con: Connection let query: ApolloServerTestClient['query'] diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index b8e4a5750..6cbcf9830 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -74,6 +74,7 @@ import { objectValuesToArray } from '@/util/utilities' import { Location2Point } from './util/Location2Point' jest.mock('@/apis/humhub/HumHubClient') +jest.mock('@/password/EncryptorUtils') jest.mock('@/emails/sendEmailVariants', () => { const originalModule = jest.requireActual('@/emails/sendEmailVariants') diff --git a/backend/src/graphql/resolver/semaphore.test.ts b/backend/src/graphql/resolver/semaphore.test.ts index ed58d55ec..f3dcce6f6 100644 --- a/backend/src/graphql/resolver/semaphore.test.ts +++ b/backend/src/graphql/resolver/semaphore.test.ts @@ -25,6 +25,8 @@ import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' import { bobBaumeister } from '@/seeds/users/bob-baumeister' import { peterLustig } from '@/seeds/users/peter-lustig' +jest.mock('@/password/EncryptorUtils') + let mutate: ApolloServerTestClient['mutate'], con: Connection let testEnv: { mutate: ApolloServerTestClient['mutate'] diff --git a/backend/src/graphql/resolver/util/creations.test.ts b/backend/src/graphql/resolver/util/creations.test.ts index 83d787dd9..8b59d0b14 100644 --- a/backend/src/graphql/resolver/util/creations.test.ts +++ b/backend/src/graphql/resolver/util/creations.test.ts @@ -12,6 +12,8 @@ import { peterLustig } from '@/seeds/users/peter-lustig' import { getOpenCreations, getUserCreation } from './creations' +jest.mock('@/password/EncryptorUtils') + let mutate: ApolloServerTestClient['mutate'], con: Connection let testEnv: { mutate: ApolloServerTestClient['mutate'] diff --git a/backend/src/graphql/resolver/util/findUserByIdentifiers.test.ts b/backend/src/graphql/resolver/util/findUserByIdentifiers.test.ts index cfdbce8f2..1cdca8429 100644 --- a/backend/src/graphql/resolver/util/findUserByIdentifiers.test.ts +++ b/backend/src/graphql/resolver/util/findUserByIdentifiers.test.ts @@ -16,6 +16,8 @@ import { peterLustig } from '@/seeds/users/peter-lustig' import { findUserByIdentifier } from './findUserByIdentifier' +jest.mock('@/password/EncryptorUtils') + let con: Connection let testEnv: { mutate: ApolloServerTestClient['mutate'] diff --git a/backend/src/graphql/resolver/util/sendTransactionsToDltConnector.test.ts b/backend/src/graphql/resolver/util/sendTransactionsToDltConnector.test.ts index 0d85a35af..064184a9d 100644 --- a/backend/src/graphql/resolver/util/sendTransactionsToDltConnector.test.ts +++ b/backend/src/graphql/resolver/util/sendTransactionsToDltConnector.test.ts @@ -32,6 +32,8 @@ import { raeuberHotzenplotz } from '@/seeds/users/raeuber-hotzenplotz' import { sendTransactionsToDltConnector } from './sendTransactionsToDltConnector' +jest.mock('@/password/EncryptorUtils') + /* // Mock the GraphQLClient jest.mock('graphql-request', () => { diff --git a/backend/src/password/EncryptionWorker.ts b/backend/src/password/EncryptionWorker.ts index 2299a1a9b..00129dc69 100644 --- a/backend/src/password/EncryptionWorker.ts +++ b/backend/src/password/EncryptionWorker.ts @@ -18,7 +18,7 @@ export const SecretKeyCryptographyCreateKey = ( password: string, configLoginAppSecret: Buffer, configLoginServerKey: Buffer, -): Buffer[] => { +): Uint8Array[] => { const state = Buffer.alloc(crypto_hash_sha512_STATEBYTES) crypto_hash_sha512_init(state) crypto_hash_sha512_update(state, Buffer.from(salt)) @@ -42,7 +42,7 @@ export const SecretKeyCryptographyCreateKey = ( const encryptionKeyHash = Buffer.alloc(crypto_shorthash_BYTES) crypto_shorthash(encryptionKeyHash, encryptionKey, configLoginServerKey) - return [encryptionKeyHash, encryptionKey] + return [new Uint8Array(encryptionKeyHash), new Uint8Array(encryptionKey)] } worker({ diff --git a/backend/src/password/EncryptorUtils.ts b/backend/src/password/EncryptorUtils.ts index 74b9f91d0..7086547a2 100644 --- a/backend/src/password/EncryptorUtils.ts +++ b/backend/src/password/EncryptorUtils.ts @@ -21,7 +21,7 @@ const configLoginServerKey = Buffer.from(CONFIG.LOGIN_SERVER_KEY, 'hex') // TODO: put maxQueueSize into config const encryptionWorkerPool = pool( - path.join(__dirname, '..', '..', 'build', 'src', 'password', '/EncryptionWorker.js'), + path.join(__dirname, '..', '..', 'build', 'src', 'password', '/EncryptionWorker.ts'), { maxQueueSize: 30 * cpus().length, }, diff --git a/backend/src/util/klicktipp.test.ts b/backend/src/util/klicktipp.test.ts index 6639a0aa4..a88e8f21a 100644 --- a/backend/src/util/klicktipp.test.ts +++ b/backend/src/util/klicktipp.test.ts @@ -19,6 +19,7 @@ import { peterLustig } from '@/seeds/users/peter-lustig' import { exportEventDataToKlickTipp } from './klicktipp' jest.mock('@/apis/KlicktippController') +jest.mock('@/password/EncryptorUtils') let mutate: ApolloServerTestClient['mutate'], con: Connection let testEnv: { From 677657e23c4c394de5897003be0f01d274eadf94 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Fri, 20 Dec 2024 15:21:04 +0100 Subject: [PATCH 6/9] make using worker optional --- .github/workflows/test_e2e.yml | 2 +- backend/.env.template | 1 + backend/src/config/index.ts | 1 + backend/src/password/EncryptionWorker.ts | 10 +++-- backend/src/password/EncryptorUtils.ts | 47 +++++++++++++++++------- deployment/bare_metal/.env.dist | 1 + 6 files changed, 44 insertions(+), 18 deletions(-) diff --git a/.github/workflows/test_e2e.yml b/.github/workflows/test_e2e.yml index 120fd0db3..bdf979bf9 100644 --- a/.github/workflows/test_e2e.yml +++ b/.github/workflows/test_e2e.yml @@ -35,7 +35,7 @@ jobs: cd database yarn && yarn dev_reset cd ../backend - yarn && yarn build && yarn seed + yarn && yarn seed - name: Boot up test system | docker-compose mailserver run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps mailserver diff --git a/backend/.env.template b/backend/.env.template index 71fbcbf31..7d588ff49 100644 --- a/backend/.env.template +++ b/backend/.env.template @@ -40,6 +40,7 @@ COMMUNITY_SUPPORT_MAIL=$COMMUNITY_SUPPORT_MAIL # Login Server LOGIN_APP_SECRET=21ffbbc616fe LOGIN_SERVER_KEY=a51ef8ac7ef1abf162fb7a65261acd7a +USE_CRYPTO_WORKER=$USE_CRYPTO_WORKER # EMail EMAIL=$EMAIL diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index d66f729db..38d3d8283 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -76,6 +76,7 @@ const community = { const loginServer = { LOGIN_APP_SECRET: process.env.LOGIN_APP_SECRET ?? '21ffbbc616fe', LOGIN_SERVER_KEY: process.env.LOGIN_SERVER_KEY ?? 'a51ef8ac7ef1abf162fb7a65261acd7a', + USE_CRYPTO_WORKER: process.env.USE_CRYPTO_WORKER ?? false, } const email = { diff --git a/backend/src/password/EncryptionWorker.ts b/backend/src/password/EncryptionWorker.ts index 00129dc69..506a42c1e 100644 --- a/backend/src/password/EncryptionWorker.ts +++ b/backend/src/password/EncryptionWorker.ts @@ -1,5 +1,7 @@ import { worker } from 'workerpool' +import { CONFIG } from '@/config' + import { crypto_box_SEEDBYTES, crypto_hash_sha512_init, @@ -45,6 +47,8 @@ export const SecretKeyCryptographyCreateKey = ( return [new Uint8Array(encryptionKeyHash), new Uint8Array(encryptionKey)] } -worker({ - SecretKeyCryptographyCreateKey, -}) +if (CONFIG.USE_CRYPTO_WORKER) { + worker({ + SecretKeyCryptographyCreateKey, + }) +} diff --git a/backend/src/password/EncryptorUtils.ts b/backend/src/password/EncryptorUtils.ts index 7086547a2..1f8b706a2 100644 --- a/backend/src/password/EncryptorUtils.ts +++ b/backend/src/password/EncryptorUtils.ts @@ -6,7 +6,7 @@ import { cpus } from 'os' import path from 'path' import { User } from '@entity/User' -import { pool } from 'workerpool' +import { Pool, pool } from 'workerpool' import { PasswordEncryptionType } from '@enum/PasswordEncryptionType' @@ -16,16 +16,22 @@ import { backendLogger as logger } from '@/server/logger' import { crypto_shorthash_KEYBYTES } from 'sodium-native' +import { SecretKeyCryptographyCreateKey as SecretKeyCryptographyCreateKeySync } from './EncryptionWorker' + const configLoginAppSecret = Buffer.from(CONFIG.LOGIN_APP_SECRET, 'hex') const configLoginServerKey = Buffer.from(CONFIG.LOGIN_SERVER_KEY, 'hex') -// TODO: put maxQueueSize into config -const encryptionWorkerPool = pool( - path.join(__dirname, '..', '..', 'build', 'src', 'password', '/EncryptionWorker.ts'), - { - maxQueueSize: 30 * cpus().length, - }, -) +let encryptionWorkerPool: Pool | undefined + +if (CONFIG.USE_CRYPTO_WORKER) { + encryptionWorkerPool = pool( + path.join(__dirname, '..', '..', 'build', 'src', 'password', '/EncryptionWorker.js'), + { + // TODO: put maxQueueSize into config + maxQueueSize: 30 * cpus().length, + }, + ) +} // We will reuse this for changePassword export const isValidPassword = (password: string): boolean => { @@ -50,12 +56,25 @@ export const SecretKeyCryptographyCreateKey = async ( crypto_shorthash_KEYBYTES, ) } - return (await encryptionWorkerPool.exec('SecretKeyCryptographyCreateKey', [ - salt, - password, - configLoginAppSecret, - configLoginServerKey, - ])) as Promise + let result: Promise + if (encryptionWorkerPool) { + result = (await encryptionWorkerPool.exec('SecretKeyCryptographyCreateKey', [ + salt, + password, + configLoginAppSecret, + configLoginServerKey, + ])) as Promise + } else { + result = Promise.resolve( + SecretKeyCryptographyCreateKeySync( + salt, + password, + configLoginAppSecret, + configLoginServerKey, + ), + ) + } + return result } catch (e) { // pool is throwing this error // throw new Error('Max queue size of ' + this.maxQueueSize + ' reached'); diff --git a/deployment/bare_metal/.env.dist b/deployment/bare_metal/.env.dist index 3b7a19b6b..a07d92dde 100644 --- a/deployment/bare_metal/.env.dist +++ b/deployment/bare_metal/.env.dist @@ -41,6 +41,7 @@ DEPLOY_SEED_DATA=false # if true all email will be send to EMAIL_TEST_RECEIVER instead of email address of user EMAIL_TEST_MODUS=false EMAIL_TEST_RECEIVER=test_team@gradido.net +USE_CRYPTO_WORKER=true # Logging LOG_LEVEL=INFO From 4d383a434661bdb7743db2420a52ac8e3a55d2d2 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Thu, 9 Jan 2025 12:27:46 +0100 Subject: [PATCH 7/9] add comment for more code clarity --- backend/src/password/PasswordEncryptor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/password/PasswordEncryptor.ts b/backend/src/password/PasswordEncryptor.ts index 1aa740672..1ee0a68a8 100644 --- a/backend/src/password/PasswordEncryptor.ts +++ b/backend/src/password/PasswordEncryptor.ts @@ -5,7 +5,7 @@ import { getUserCryptographicSalt, SecretKeyCryptographyCreateKey } from './Encr export const encryptPassword = async (dbUser: User, password: string): Promise => { const salt = getUserCryptographicSalt(dbUser) - const keyBuffer = await SecretKeyCryptographyCreateKey(salt, password) // return short and long hash + const keyBuffer: Uint8Array[] = await SecretKeyCryptographyCreateKey(salt, password) // returns Uint8Array[short hash, long hash] const passwordHash = Buffer.from(keyBuffer[0]).readBigUInt64LE() return passwordHash } From d31d10e3f7eb079e15703edaa6cae4d1b69cbaf6 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Thu, 9 Jan 2025 13:26:44 +0100 Subject: [PATCH 8/9] reduce unneccessary data copy --- .../src/graphql/resolver/UserResolver.test.ts | 22 ++++++++----------- backend/src/password/EncryptionWorker.ts | 5 ++--- backend/src/password/EncryptorUtils.ts | 6 ++--- backend/src/password/PasswordEncryptor.ts | 4 +--- .../src/password/__mocks__/EncryptorUtils.ts | 6 ++--- 5 files changed, 18 insertions(+), 25 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index 2c74a7b8c..58b6b33c4 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -97,6 +97,8 @@ jest.mock('@/apis/KlicktippController', () => { } }) +CONFIG.EMAIL_CODE_REQUEST_TIME = 10 + let admin: User let user: User let mutate: ApolloServerTestClient['mutate'], @@ -1548,11 +1550,9 @@ describe('UserResolver', () => { expect(bibi).toEqual( expect.objectContaining({ - password: Buffer.from( - (await SecretKeyCryptographyCreateKey(bibi.gradidoID.toString(), 'Aa12345_'))[0], - ) - .readBigUInt64LE() - .toString(), + password: ( + await SecretKeyCryptographyCreateKey(bibi.gradidoID.toString(), 'Aa12345_') + ).toString(), passwordEncryptionType: PasswordEncryptionType.GRADIDO_ID, }), ) @@ -1574,9 +1574,7 @@ describe('UserResolver', () => { }) bibi = usercontact.user bibi.passwordEncryptionType = PasswordEncryptionType.EMAIL - bibi.password = Buffer.from( - (await SecretKeyCryptographyCreateKey('bibi@bloxberg.de', 'Aa12345_'))[0], - ).readBigUInt64LE() + bibi.password = await SecretKeyCryptographyCreateKey('bibi@bloxberg.de', 'Aa12345_') await bibi.save() }) @@ -1593,11 +1591,9 @@ describe('UserResolver', () => { expect(bibi).toEqual( expect.objectContaining({ firstName: 'Bibi', - password: Buffer.from( - (await SecretKeyCryptographyCreateKey(bibi.gradidoID.toString(), 'Aa12345_'))[0], - ) - .readBigUInt64LE() - .toString(), + password: ( + await SecretKeyCryptographyCreateKey(bibi.gradidoID.toString(), 'Aa12345_') + ).toString(), passwordEncryptionType: PasswordEncryptionType.GRADIDO_ID, }), ) diff --git a/backend/src/password/EncryptionWorker.ts b/backend/src/password/EncryptionWorker.ts index 506a42c1e..f14d5d1ff 100644 --- a/backend/src/password/EncryptionWorker.ts +++ b/backend/src/password/EncryptionWorker.ts @@ -20,7 +20,7 @@ export const SecretKeyCryptographyCreateKey = ( password: string, configLoginAppSecret: Buffer, configLoginServerKey: Buffer, -): Uint8Array[] => { +): bigint => { const state = Buffer.alloc(crypto_hash_sha512_STATEBYTES) crypto_hash_sha512_init(state) crypto_hash_sha512_update(state, Buffer.from(salt)) @@ -43,8 +43,7 @@ export const SecretKeyCryptographyCreateKey = ( const encryptionKeyHash = Buffer.alloc(crypto_shorthash_BYTES) crypto_shorthash(encryptionKeyHash, encryptionKey, configLoginServerKey) - - return [new Uint8Array(encryptionKeyHash), new Uint8Array(encryptionKey)] + return encryptionKeyHash.readBigUInt64LE() } if (CONFIG.USE_CRYPTO_WORKER) { diff --git a/backend/src/password/EncryptorUtils.ts b/backend/src/password/EncryptorUtils.ts index 1f8b706a2..27432d1a3 100644 --- a/backend/src/password/EncryptorUtils.ts +++ b/backend/src/password/EncryptorUtils.ts @@ -46,7 +46,7 @@ export const isValidPassword = (password: string): boolean => { export const SecretKeyCryptographyCreateKey = async ( salt: string, password: string, -): Promise => { +): Promise => { try { logger.trace('call worker for: SecretKeyCryptographyCreateKey') if (configLoginServerKey.length !== crypto_shorthash_KEYBYTES) { @@ -56,14 +56,14 @@ export const SecretKeyCryptographyCreateKey = async ( crypto_shorthash_KEYBYTES, ) } - let result: Promise + let result: Promise if (encryptionWorkerPool) { result = (await encryptionWorkerPool.exec('SecretKeyCryptographyCreateKey', [ salt, password, configLoginAppSecret, configLoginServerKey, - ])) as Promise + ])) as Promise } else { result = Promise.resolve( SecretKeyCryptographyCreateKeySync( diff --git a/backend/src/password/PasswordEncryptor.ts b/backend/src/password/PasswordEncryptor.ts index 1ee0a68a8..a97c29e7f 100644 --- a/backend/src/password/PasswordEncryptor.ts +++ b/backend/src/password/PasswordEncryptor.ts @@ -5,9 +5,7 @@ import { getUserCryptographicSalt, SecretKeyCryptographyCreateKey } from './Encr export const encryptPassword = async (dbUser: User, password: string): Promise => { const salt = getUserCryptographicSalt(dbUser) - const keyBuffer: Uint8Array[] = await SecretKeyCryptographyCreateKey(salt, password) // returns Uint8Array[short hash, long hash] - const passwordHash = Buffer.from(keyBuffer[0]).readBigUInt64LE() - return passwordHash + return SecretKeyCryptographyCreateKey(salt, password) } export const verifyPassword = async (dbUser: User, password: string): Promise => { diff --git a/backend/src/password/__mocks__/EncryptorUtils.ts b/backend/src/password/__mocks__/EncryptorUtils.ts index b406f1b1c..fcd3ff601 100644 --- a/backend/src/password/__mocks__/EncryptorUtils.ts +++ b/backend/src/password/__mocks__/EncryptorUtils.ts @@ -29,7 +29,7 @@ const SecretKeyCryptographyCreateKeyMock = ( password: string, configLoginAppSecret: Buffer, configLoginServerKey: Buffer, -): Uint8Array[] => { +): bigint => { const state = Buffer.alloc(crypto_hash_sha512_STATEBYTES) crypto_hash_sha512_init(state) crypto_hash_sha512_update(state, Buffer.from(salt)) @@ -53,7 +53,7 @@ const SecretKeyCryptographyCreateKeyMock = ( const encryptionKeyHash = Buffer.alloc(crypto_shorthash_BYTES) crypto_shorthash(encryptionKeyHash, encryptionKey, configLoginServerKey) - return [new Uint8Array(encryptionKeyHash), new Uint8Array(encryptionKey)] + return encryptionKeyHash.readBigUInt64LE() } const configLoginAppSecret = Buffer.from(CONFIG.LOGIN_APP_SECRET, 'hex') @@ -72,7 +72,7 @@ export const isValidPassword = (password: string): boolean => { export const SecretKeyCryptographyCreateKey = async ( salt: string, password: string, -): Promise => { +): Promise => { try { logger.trace('call worker for: SecretKeyCryptographyCreateKey') if (configLoginServerKey.length !== crypto_shorthash_KEYBYTES) { From ac5f576d9e02599e0e913b05bf1b7fcef04ae6f3 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Thu, 9 Jan 2025 13:31:56 +0100 Subject: [PATCH 9/9] speedup tests by 25% by reducing password encryption difficulty in test case --- backend/src/password/__mocks__/EncryptorUtils.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/backend/src/password/__mocks__/EncryptorUtils.ts b/backend/src/password/__mocks__/EncryptorUtils.ts index fcd3ff601..2bc20f951 100644 --- a/backend/src/password/__mocks__/EncryptorUtils.ts +++ b/backend/src/password/__mocks__/EncryptorUtils.ts @@ -22,6 +22,8 @@ import { crypto_pwhash_SALTBYTES, crypto_pwhash, crypto_shorthash, + crypto_pwhash_OPSLIMIT_MIN, + crypto_pwhash_MEMLIMIT_MIN, } from 'sodium-native' const SecretKeyCryptographyCreateKeyMock = ( @@ -38,8 +40,8 @@ const SecretKeyCryptographyCreateKeyMock = ( crypto_hash_sha512_final(state, hash) const encryptionKey = Buffer.alloc(crypto_box_SEEDBYTES) - const opsLimit = 10 - const memLimit = 33554432 + const opsLimit = crypto_pwhash_OPSLIMIT_MIN + const memLimit = crypto_pwhash_MEMLIMIT_MIN const algo = 2 crypto_pwhash( encryptionKey,