From 12769bd1ab5ae4b6845390682dacab1215fe1d4f Mon Sep 17 00:00:00 2001 From: ThatOneCalculator Date: Thu, 30 Mar 2023 19:09:44 -0700 Subject: [PATCH] feat: :lock: add argon2 support Passwords will be automatically re-hashed on sign-in. All new password hashes will be argon2 by default. This uses argon2id and is not configurable. In the very unlikely case someone has more specific needs, a fork is recommended. ChangeLog: Added Co-authored-by: Chloe Kudryavtsev Breaks Calckey -> Misskey migration, but fixes Foundkey -> Calckey migration --- packages/backend/src/misc/password.ts | 20 +++++++++++++++++++ .../backend/src/server/api/private/signin.ts | 8 +++++++- 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 packages/backend/src/misc/password.ts diff --git a/packages/backend/src/misc/password.ts b/packages/backend/src/misc/password.ts new file mode 100644 index 000000000..c63f89f5c --- /dev/null +++ b/packages/backend/src/misc/password.ts @@ -0,0 +1,20 @@ +import bcrypt from "bcryptjs"; +import * as argon2 from "argon2"; + +export async function hashPassword(password: string): Promise { + return argon2.hash(password); +} + +export async function comparePassword( + password: string, + hash: string, +): Promise { + if (isOldAlgorithm(hash)) return bcrypt.compare(password, hash); + + return argon2.verify(hash, password); +} + +export function isOldAlgorithm(hash: string): boolean { + // bcrypt hashes start with $2[ab]$ + return hash.startsWith("$2"); +} diff --git a/packages/backend/src/server/api/private/signin.ts b/packages/backend/src/server/api/private/signin.ts index b06f47ed4..c8cd5fb2c 100644 --- a/packages/backend/src/server/api/private/signin.ts +++ b/packages/backend/src/server/api/private/signin.ts @@ -12,6 +12,7 @@ import { } from "@/models/index.js"; import type { ILocalUser } from "@/models/entities/user.js"; import { genId } from "@/misc/gen-id.js"; +import { comparePassword, hashPassword, isOldAlgorithm } from '@/misc/password.js'; import { verifyLogin, hash } from "../2fa.js"; import { randomBytes } from "node:crypto"; import { IsNull } from "typeorm"; @@ -88,7 +89,12 @@ export default async (ctx: Koa.Context) => { const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); // Compare password - const same = await bcrypt.compare(password, profile.password!); + const same = await comparePassword(password, profile.password!); + + if (same && isOldAlgorithm(profile.password!)) { + profile.password = await hashPassword(password); + await UserProfiles.save(profile); + } async function fail(status?: number, failure?: { id: string }) { // Append signin history