fix(auth): bound local password encoding
This commit is contained in:
@@ -59,8 +59,9 @@ function passwordBytes(password: string): Buffer | undefined {
|
|||||||
} else if (/[\uD800-\uDFFF]/.test(password)) {
|
} else if (/[\uD800-\uDFFF]/.test(password)) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const bytes = Buffer.from(password, "utf8");
|
const byteLength = Buffer.byteLength(password, "utf8");
|
||||||
return bytes.length >= MINIMUM_PASSWORD_BYTES && bytes.length <= MAXIMUM_PASSWORD_BYTES ? bytes : undefined;
|
if (byteLength < MINIMUM_PASSWORD_BYTES || byteLength > MAXIMUM_PASSWORD_BYTES) return undefined;
|
||||||
|
return Buffer.from(password, "utf8");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isValidPasswordHash(encoded: string): boolean {
|
export function isValidPasswordHash(encoded: string): boolean {
|
||||||
|
|||||||
@@ -44,6 +44,28 @@ describe("local Argon2id password verification", () => {
|
|||||||
expect(verifyPassword(loneSurrogatePassword, replacementPasswordHash)).toBe(false);
|
expect(verifyPassword(loneSurrogatePassword, replacementPasswordHash)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("accepts a multibyte password at exactly the 1024-byte boundary", () => {
|
||||||
|
const password = "é".repeat(512);
|
||||||
|
const passwordHash = "$argon2id$v=19$m=65536,t=3,p=1$AAECAwQFBgcICQoLDA0ODw$LfO3M3JBKeXf1knenCjQ6m9z4B7nedb0As2uc522I1I";
|
||||||
|
|
||||||
|
expect(Buffer.byteLength(password, "utf8")).toBe(1024);
|
||||||
|
expect(verifyPassword(password, passwordHash)).toBe(true);
|
||||||
|
expect(verifyPassword(`${password}é`, passwordHash)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("rejects an over-limit password before converting it with Buffer.from", () => {
|
||||||
|
const password = "é".repeat(513);
|
||||||
|
const fromSpy = vi.spyOn(Buffer, "from");
|
||||||
|
|
||||||
|
try {
|
||||||
|
expect(Buffer.byteLength(password, "utf8")).toBe(1026);
|
||||||
|
expect(verifyPassword(password, vectors[0].phc)).toBe(false);
|
||||||
|
expect(fromSpy.mock.calls.some(([value, encoding]) => value === password && encoding === "utf8")).toBe(false);
|
||||||
|
} finally {
|
||||||
|
fromSpy.mockRestore();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
test("rejects malformed and oversized PHC parameters before Argon2 allocation", () => {
|
test("rejects malformed and oversized PHC parameters before Argon2 allocation", () => {
|
||||||
const password = vectors[0].password;
|
const password = vectors[0].password;
|
||||||
const digest = vectors[0].phc.split("$")[5];
|
const digest = vectors[0].phc.split("$")[5];
|
||||||
|
|||||||
Reference in New Issue
Block a user