From ac6b8e666ff893d19786b51f20bff36b6c771038 Mon Sep 17 00:00:00 2001 From: joseji Date: Wed, 9 Nov 2022 10:46:01 +0100 Subject: [PATCH] encryption interface --- .../graphql/enum/PasswordEncryptionType.ts | 12 +++++ backend/src/password/EmailEncryptr.ts | 19 +++++++ backend/src/password/EncryptorUtils.ts | 52 +++++++++++++++++++ backend/src/password/GradidoIDEncryptr.ts | 19 +++++++ backend/src/password/PasswordEncryptr.ts | 6 +++ ...ion => 0053-change_password_encryption.ts} | 4 +- 6 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 backend/src/graphql/enum/PasswordEncryptionType.ts create mode 100644 backend/src/password/EmailEncryptr.ts create mode 100644 backend/src/password/EncryptorUtils.ts create mode 100644 backend/src/password/GradidoIDEncryptr.ts create mode 100644 backend/src/password/PasswordEncryptr.ts rename database/migrations/{0053-change_password_encryption => 0053-change_password_encryption.ts} (97%) diff --git a/backend/src/graphql/enum/PasswordEncryptionType.ts b/backend/src/graphql/enum/PasswordEncryptionType.ts new file mode 100644 index 000000000..4f23aa693 --- /dev/null +++ b/backend/src/graphql/enum/PasswordEncryptionType.ts @@ -0,0 +1,12 @@ +import { registerEnumType } from 'type-graphql' + +export enum PasswordEncryptionType { + EMAIL = 0, + ONE_TIME = 1, + GRADIDO_ID = 2, +} + +registerEnumType(PasswordEncryptionType, { + name: 'PasswordEncryptionType', // this one is mandatory + description: 'Type of the password encryption', // this one is optional +}) diff --git a/backend/src/password/EmailEncryptr.ts b/backend/src/password/EmailEncryptr.ts new file mode 100644 index 000000000..59098e207 --- /dev/null +++ b/backend/src/password/EmailEncryptr.ts @@ -0,0 +1,19 @@ +import { User } from '@entity/User' +import { PasswordEncryptr } from './PasswordEncryptr' +import { SecretKeyCryptographyCreateKey } from './EncryptorUtils' + +export class EmailEncryptr implements PasswordEncryptr { + async encryptPassword(dbUser: User, password: string): Promise { + const keyBuffer = SecretKeyCryptographyCreateKey(dbUser.emailContact.email, password) // return short and long hash + const passwordHash = keyBuffer[0].readBigUInt64LE() + + return passwordHash + } + + async verifyPassword(dbUser: User, password: string): Promise { + if (BigInt(password) !== (await this.encryptPassword(dbUser, dbUser.password.toString()))) { + return false + } + return true + } +} diff --git a/backend/src/password/EncryptorUtils.ts b/backend/src/password/EncryptorUtils.ts new file mode 100644 index 000000000..6609ff075 --- /dev/null +++ b/backend/src/password/EncryptorUtils.ts @@ -0,0 +1,52 @@ +import CONFIG from '@/config' +import { backendLogger as logger } from '@/server/logger' + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const sodium = require('sodium-native') + +// 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 !== sodium.crypto_shorthash_KEYBYTES) { + logger.error( + `ServerKey has an invalid size. The size must be ${sodium.crypto_shorthash_KEYBYTES} bytes.`, + ) + throw new Error( + `ServerKey has an invalid size. The size must be ${sodium.crypto_shorthash_KEYBYTES} bytes.`, + ) + } + + const state = Buffer.alloc(sodium.crypto_hash_sha512_STATEBYTES) + sodium.crypto_hash_sha512_init(state) + sodium.crypto_hash_sha512_update(state, Buffer.from(salt)) + sodium.crypto_hash_sha512_update(state, configLoginAppSecret) + const hash = Buffer.alloc(sodium.crypto_hash_sha512_BYTES) + sodium.crypto_hash_sha512_final(state, hash) + + const encryptionKey = Buffer.alloc(sodium.crypto_box_SEEDBYTES) + const opsLimit = 10 + const memLimit = 33554432 + const algo = 2 + sodium.crypto_pwhash( + encryptionKey, + Buffer.from(password), + hash.slice(0, sodium.crypto_pwhash_SALTBYTES), + opsLimit, + memLimit, + algo, + ) + + const encryptionKeyHash = Buffer.alloc(sodium.crypto_shorthash_BYTES) + sodium.crypto_shorthash(encryptionKeyHash, encryptionKey, configLoginServerKey) + + logger.debug( + `SecretKeyCryptographyCreateKey...successful: encryptionKeyHash= ${encryptionKeyHash}, encryptionKey= ${encryptionKey}`, + ) + return [encryptionKeyHash, encryptionKey] +} diff --git a/backend/src/password/GradidoIDEncryptr.ts b/backend/src/password/GradidoIDEncryptr.ts new file mode 100644 index 000000000..630bee056 --- /dev/null +++ b/backend/src/password/GradidoIDEncryptr.ts @@ -0,0 +1,19 @@ +import { User } from '@entity/User' +import { SecretKeyCryptographyCreateKey } from './EncryptorUtils' +import { PasswordEncryptr } from './PasswordEncryptr' + +export class GradidoIDEncryptr implements PasswordEncryptr { + async encryptPassword(dbUser: User, password: string): Promise { + const keyBuffer = SecretKeyCryptographyCreateKey(dbUser.gradidoID, password) // return short and long hash + const passwordHash = keyBuffer[0].readBigUInt64LE() + + return passwordHash + } + + async verifyPassword(dbUser: User, password: string): Promise { + if (BigInt(password) !== (await this.encryptPassword(dbUser, dbUser.password.toString()))) { + return false + } + return true + } +} diff --git a/backend/src/password/PasswordEncryptr.ts b/backend/src/password/PasswordEncryptr.ts new file mode 100644 index 000000000..24d949be9 --- /dev/null +++ b/backend/src/password/PasswordEncryptr.ts @@ -0,0 +1,6 @@ +import { User } from '@entity/User' + +export interface PasswordEncryptr { + encryptPassword(dbUser: User, password: string): Promise + verifyPassword(dbUser: User, password: string): Promise +} diff --git a/database/migrations/0053-change_password_encryption b/database/migrations/0053-change_password_encryption.ts similarity index 97% rename from database/migrations/0053-change_password_encryption rename to database/migrations/0053-change_password_encryption.ts index 1b87e2511..5d880689f 100644 --- a/database/migrations/0053-change_password_encryption +++ b/database/migrations/0053-change_password_encryption.ts @@ -1,4 +1,4 @@ -/* MIGRATION TO ADD GRADIDO_ID +/* MIGRATION TO ADD ENCRYPTION TO PASSWORDS * * This migration adds and renames columns to and in the table `users` */ @@ -35,4 +35,4 @@ export async function downgrade(queryFn: (query: string, values?: any[]) => Prom await queryFn('ALTER TABLE users ADD COLUMN email_hash binary(32) DEFAULT NULL;') await queryFn('ALTER TABLE users ADD COLUMN passphrase text DEFAULT NULL;') */ -} \ No newline at end of file +}