128 lines
4.0 KiB
TypeScript
128 lines
4.0 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { inspectCryptoInput } from "../../src/crypto/inspection";
|
|
import { inspectPkcs12 } from "../../src/crypto/pkcs12";
|
|
import {
|
|
EMPTY_PASSWORD_PFX_BASE64,
|
|
LEGACY_PFX_BASE64,
|
|
MODERN_PFX_PASSWORD,
|
|
pfxFixture,
|
|
} from "../fixtures/pkcs12";
|
|
|
|
describe("bounded PKCS #12 inspection", () => {
|
|
it("inventories supported content without silently using a password", async () => {
|
|
const result = await inspectPkcs12(pfxFixture());
|
|
expect(result.mac).toMatchObject({
|
|
present: true,
|
|
status: "password-required",
|
|
algorithm: "SHA-256",
|
|
iterations: 2_048,
|
|
});
|
|
expect(result.contents).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({
|
|
type: "encryptedData",
|
|
state: "password-required",
|
|
}),
|
|
expect.objectContaining({ type: "data", state: "parsed" }),
|
|
]),
|
|
);
|
|
expect(result.bags).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({
|
|
bagType: "shrouded-private-key",
|
|
friendlyName: "Local test identity",
|
|
state: "password-required",
|
|
}),
|
|
]),
|
|
);
|
|
});
|
|
|
|
it("verifies MacData and decrypts PBES2 SafeContents/key bags", async () => {
|
|
const result = await inspectPkcs12(pfxFixture(), {
|
|
password: MODERN_PFX_PASSWORD,
|
|
});
|
|
expect(result.mac.status).toBe("verified");
|
|
expect(result.contents.every((content) => content.state === "parsed")).toBe(
|
|
true,
|
|
);
|
|
expect(result.bags).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({
|
|
bagType: "certificate",
|
|
friendlyName: "Local test identity",
|
|
}),
|
|
expect.objectContaining({
|
|
bagType: "shrouded-private-key",
|
|
state: "inspected",
|
|
key: expect.objectContaining({ algorithm: "RSA" }),
|
|
}),
|
|
]),
|
|
);
|
|
});
|
|
|
|
it("distinguishes explicit empty passwords from password omission", async () => {
|
|
const bytes = pfxFixture(EMPTY_PASSWORD_PFX_BASE64);
|
|
expect((await inspectPkcs12(bytes)).mac.status).toBe("password-required");
|
|
const inspected = await inspectPkcs12(bytes, { password: "" });
|
|
expect(inspected.mac.status).toBe("verified");
|
|
expect(
|
|
inspected.bags.some(
|
|
(bag) =>
|
|
bag.bagType === "shrouded-private-key" &&
|
|
bag.state === "inspected" &&
|
|
bag.key?.algorithm === "RSA",
|
|
),
|
|
).toBe(true);
|
|
});
|
|
|
|
it("fails closed for wrong passwords, damaged nesting and legacy PBE", async () => {
|
|
await expect(
|
|
inspectPkcs12(pfxFixture(), { password: "wrong-password" }),
|
|
).rejects.toThrow(/password is incorrect|MacData\/authSafe/u);
|
|
|
|
const truncated = pfxFixture().slice(0, -1);
|
|
await expect(inspectPkcs12(truncated)).rejects.toThrow(
|
|
/malformed DER|trailing bytes/u,
|
|
);
|
|
|
|
await expect(
|
|
inspectPkcs12(pfxFixture(LEGACY_PFX_BASE64), {
|
|
password: MODERN_PFX_PASSWORD,
|
|
}),
|
|
).rejects.toThrow(/legacy PKCS #12 PBE/u);
|
|
|
|
await expect(
|
|
inspectPkcs12(pfxFixture(), { password: "x".repeat(4_097) }),
|
|
).rejects.toThrow(/at most 4,096 UTF-16 units/u);
|
|
});
|
|
|
|
it("integrates bag and certificate inventory without reporting secrets", async () => {
|
|
const result = await inspectCryptoInput(pfxFixture(), new Date(), {
|
|
password: MODERN_PFX_PASSWORD,
|
|
});
|
|
expect(result.items[0]).toMatchObject({
|
|
type: "PKCS #12/PFX",
|
|
facts: expect.objectContaining({
|
|
MacData: expect.stringMatching(/Verified/u),
|
|
}),
|
|
});
|
|
expect(result.items.some((item) => item.certificate)).toBe(true);
|
|
expect(
|
|
result.items.some((item) =>
|
|
item.facts["Private-key algorithm"]?.includes("RSA"),
|
|
),
|
|
).toBe(true);
|
|
const reportSurface = JSON.stringify(
|
|
result.items.map(({ id, type, title, facts, findings }) => ({
|
|
id,
|
|
type,
|
|
title,
|
|
facts,
|
|
findings,
|
|
})),
|
|
);
|
|
expect(reportSurface).not.toContain(MODERN_PFX_PASSWORD);
|
|
expect(reportSurface).not.toContain("PRIVATE KEY-----");
|
|
});
|
|
});
|