fix(ext/sw): sshsig hashes the exact payload view; hoist TextEncoder

This commit is contained in:
adlee-was-taken
2026-06-25 23:40:16 -04:00
parent b3db292474
commit 273fe5cf30

View File

@@ -44,10 +44,10 @@ export async function buildSshSig(
rawSign: (data: Uint8Array) => Uint8Array | Promise<Uint8Array>, rawSign: (data: Uint8Array) => Uint8Array | Promise<Uint8Array>,
): Promise<string> { ): Promise<string> {
// Step 1: H = SHA-512(payload). // Step 1: H = SHA-512(payload).
// SubtleCrypto requires a plain ArrayBuffer (not ArrayBufferLike) in TS 5.9+; // slice(0) allocates a fresh backing buffer whose extent exactly matches the
// callers always pass a freshly allocated Uint8Array so .buffer is the full buffer. // view bytes, making this correct even when `payload` is a subarray/offset view.
const H = new Uint8Array( const H = new Uint8Array(
await crypto.subtle.digest('SHA-512', payload.buffer as ArrayBuffer), await crypto.subtle.digest('SHA-512', payload.slice(0).buffer as ArrayBuffer),
); );
// Step 2: signedBlob — "SSHSIG" (no length prefix) || str("git") || str("") || str("sha512") || str(H) // Step 2: signedBlob — "SSHSIG" (no length prefix) || str("git") || str("") || str("sha512") || str(H)
@@ -91,9 +91,12 @@ export async function buildSshSig(
/** Empty byte array (reused for str("") calls). */ /** Empty byte array (reused for str("") calls). */
const EMPTY = new Uint8Array(0); const EMPTY = new Uint8Array(0);
/** Module-scope encoder — avoids allocating a new TextEncoder on every call. */
const ENC = new TextEncoder();
/** Encode a plain ASCII string as UTF-8 bytes (Relicario uses only ASCII names). */ /** Encode a plain ASCII string as UTF-8 bytes (Relicario uses only ASCII names). */
function enc(s: string): Uint8Array { function enc(s: string): Uint8Array {
return new TextEncoder().encode(s); return ENC.encode(s);
} }
/** 4-byte big-endian unsigned integer. */ /** 4-byte big-endian unsigned integer. */