From 273fe5cf306bed83a7d9dd93a263d92ef92d7a6f Mon Sep 17 00:00:00 2001 From: adlee-was-taken Date: Thu, 25 Jun 2026 23:40:16 -0400 Subject: [PATCH] fix(ext/sw): sshsig hashes the exact payload view; hoist TextEncoder --- extension/src/service-worker/sshsig.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/extension/src/service-worker/sshsig.ts b/extension/src/service-worker/sshsig.ts index 8bf4e0c..4c064bb 100644 --- a/extension/src/service-worker/sshsig.ts +++ b/extension/src/service-worker/sshsig.ts @@ -44,10 +44,10 @@ export async function buildSshSig( rawSign: (data: Uint8Array) => Uint8Array | Promise, ): Promise { // Step 1: H = SHA-512(payload). - // SubtleCrypto requires a plain ArrayBuffer (not ArrayBufferLike) in TS 5.9+; - // callers always pass a freshly allocated Uint8Array so .buffer is the full buffer. + // slice(0) allocates a fresh backing buffer whose extent exactly matches the + // view bytes, making this correct even when `payload` is a subarray/offset view. 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) @@ -91,9 +91,12 @@ export async function buildSshSig( /** Empty byte array (reused for str("") calls). */ 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). */ function enc(s: string): Uint8Array { - return new TextEncoder().encode(s); + return ENC.encode(s); } /** 4-byte big-endian unsigned integer. */