feat: 🔒 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 <code@toast.bunkerlabs.net>

Breaks Calckey -> Misskey migration, but fixes Foundkey -> Calckey migration
This commit is contained in:
ThatOneCalculator 2023-03-30 19:09:44 -07:00
parent 1e249a7182
commit 12769bd1ab
No known key found for this signature in database
GPG key ID: 8703CACD01000000
2 changed files with 27 additions and 1 deletions

View file

@ -0,0 +1,20 @@
import bcrypt from "bcryptjs";
import * as argon2 from "argon2";
export async function hashPassword(password: string): Promise<string> {
return argon2.hash(password);
}
export async function comparePassword(
password: string,
hash: string,
): Promise<boolean> {
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");
}

View file

@ -12,6 +12,7 @@ import {
} from "@/models/index.js"; } from "@/models/index.js";
import type { ILocalUser } from "@/models/entities/user.js"; import type { ILocalUser } from "@/models/entities/user.js";
import { genId } from "@/misc/gen-id.js"; import { genId } from "@/misc/gen-id.js";
import { comparePassword, hashPassword, isOldAlgorithm } from '@/misc/password.js';
import { verifyLogin, hash } from "../2fa.js"; import { verifyLogin, hash } from "../2fa.js";
import { randomBytes } from "node:crypto"; import { randomBytes } from "node:crypto";
import { IsNull } from "typeorm"; import { IsNull } from "typeorm";
@ -88,7 +89,12 @@ export default async (ctx: Koa.Context) => {
const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
// Compare password // 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 }) { async function fail(status?: number, failure?: { id: string }) {
// Append signin history // Append signin history