Files
ts-scripts/libraries/deno-piv-mod.ts
2026-02-12 01:34:38 +08:00

62 lines
1.9 KiB
TypeScript

import {execCommand, getKeyRingPassword,} from "https://global.hatter.ink/script/get/@47/deno-commons-mod.ts";
import {encodeHex} from "jsr:@std/encoding/hex";
// example output
// > await signPiv("r1", await sha256AndHexMessage("hello world"))
// {
// algorithm: "ecdsa_p256_with_sha256",
// hash_hex: "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9",
// signed_data_base64: "MEYCIQDfdAyrWLjjChbDwhZ0vapVthJDUfy1BUZsCGOWLCSnKwIhAOB5JQ2oxF3URwAIlOSftBi2kzscr6wcLn3rU6ygtVr1",
// signed_data_hex: "3046022100df740cab58b8e30a16c3c21674bdaa55b6124351fcb505466c0863962c24a72b022100e079250da8c45dd447000894e49fb418b6933b1cafac1c2e7deb53aca0b55af5",
// slot: "R1"
// }
interface CardPivEcSignOutput {
algorithm: string;
hash_hex: string;
signed_data_base64: string;
signed_data_hex: string;
slot: string;
}
interface SignPivOptions {
pin?: string;
service?: string;
user?: string;
}
export async function signPivString(
slot: string,
message: string,
options?: SignPivOptions,
): Promise<CardPivEcSignOutput> {
return await signPiv(slot, await sha256AndHexMessage(message), options);
}
export async function signPiv(
slot: string,
digestSha256Hex: string,
options?: SignPivOptions,
): Promise<CardPivEcSignOutput> {
if (!options?.pin && options?.user) {
options.pin = await getKeyRingPassword(
options.service,
options.user,
);
}
const args = ["piv-ecsign", "-s", slot, "-x", digestSha256Hex, "--json"];
if (options?.pin) {
args.push(...["--pin", options?.pin]);
}
return (await execCommand("card-cli", args))
.assertSuccess().stdoutAsJson() as CardPivEcSignOutput;
}
export async function sha256AndHexMessage(message: string): Promise<string> {
return encodeHex(
await crypto.subtle.digest(
"SHA-256",
new TextEncoder().encode(message),
),
);
}