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 { return await signPiv(slot, await sha256AndHexMessage(message), options); } export async function signPiv( slot: string, digestSha256Hex: string, options?: SignPivOptions, ): Promise { 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 { return encodeHex( await crypto.subtle.digest( "SHA-256", new TextEncoder().encode(message), ), ); }