Files

94 lines
2.8 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { utf8ToBytes } from "../../src/crypto/encoding";
import { exportEncryptedPskc, importEncryptedPskc } from "../../src/otp/pskc";
import type { OtpProfile } from "../../src/otp/profile";
const profiles: OtpProfile[] = [
{
kind: "totp",
account: "alice@example.test",
issuer: "Example & Sons",
secret: utf8ToBytes("12345678901234567890"),
algorithm: "SHA-256",
digits: 8,
period: 45,
epoch: 10,
counter: 0n,
extensions: new Map(),
},
{
kind: "hotp",
account: "hardware token",
issuer: "Example",
secret: utf8ToBytes("abcdefghijklmnopqrst"),
algorithm: "SHA-1",
digits: 6,
period: 30,
counter: 1042n,
extensions: new Map(),
},
];
describe("encrypted PSKC", () => {
it("round-trips authenticated password-encrypted collections", async () => {
const exported = await exportEncryptedPskc(
profiles,
"correct horse battery staple",
{ iterations: 100_000 },
);
expect(exported).toContain("PBKDF2-params");
expect(exported).toContain(
'Algorithm="http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5v2-0#pbkdf2"',
);
expect(exported).toContain("<pskc:MACKey><xenc:EncryptionMethod");
expect(exported).toContain('<pskc:EncryptedValue Id="ED-1">');
expect(exported).not.toContain("12345678901234567890");
const result = await importEncryptedPskc(
exported,
"correct horse battery staple",
);
expect(result.profiles).toHaveLength(2);
expect(result.profiles[0]).toMatchObject({
kind: "totp",
account: "alice@example.test",
issuer: "Example & Sons",
algorithm: "SHA-256",
digits: 8,
period: 45,
epoch: 10,
});
expect(result.profiles[1]).toMatchObject({
kind: "hotp",
counter: 1042n,
});
expect([...result.profiles[0]!.secret]).toEqual([...profiles[0]!.secret]);
});
it("rejects wrong passwords and unauthenticated changes", async () => {
const exported = await exportEncryptedPskc(
profiles.slice(0, 1),
"correct horse battery staple",
{ iterations: 100_000 },
);
await expect(
importEncryptedPskc(exported, "incorrect password"),
).rejects.toThrow(/incorrect|damaged/u);
const tampered = exported.replace(
/<pskc:ValueMAC>([^<])/u,
(_match, first: string) => `<pskc:ValueMAC>${first === "A" ? "B" : "A"}`,
);
await expect(
importEncryptedPskc(tampered, "correct horse battery staple"),
).rejects.toThrow(/ValueMAC/u);
});
it("rejects XML entity declarations", async () => {
await expect(
importEncryptedPskc(
'<!DOCTYPE x [<!ENTITY e SYSTEM "file:///etc/passwd">]><x/>',
"password",
),
).rejects.toThrow(/entity/u);
});
});