diff --git a/src/api/pdf-signature.ts b/src/api/pdf-signature.ts index ca73e18..9968013 100644 --- a/src/api/pdf-signature.ts +++ b/src/api/pdf-signature.ts @@ -182,7 +182,7 @@ export class PDFSignature { // Extract bytes to sign and hash them const signedBytes = extractSignedBytes(pdfBytes, byteRange); - const documentHash = hashData(signedBytes, resolved.digestAlgorithm); + const documentHash = await hashData(signedBytes, resolved.digestAlgorithm); // Build CMS signature const formatBuilder = this.getFormatBuilder(resolved.subFilter); @@ -202,7 +202,7 @@ export class PDFSignature { if (resolved.timestampAuthority) { // Hash the signature value for timestamping const signatureValue = signedData.getSignatureValue(); - const signatureHash = hashData(signatureValue, resolved.digestAlgorithm); + const signatureHash = await hashData(signatureValue, resolved.digestAlgorithm); // Request timestamp from TSA const timestampToken = await resolved.timestampAuthority.timestamp( @@ -476,7 +476,7 @@ export class PDFSignature { // Hash and get timestamp const signedBytes = extractSignedBytes(savedBytes, byteRange); - const documentHash = hashData(signedBytes, digestAlgorithm); + const documentHash = await hashData(signedBytes, digestAlgorithm); const timestampToken = await timestampAuthority.timestamp(documentHash, digestAlgorithm); // Patch Contents diff --git a/src/signatures/formats/cades-detached.ts b/src/signatures/formats/cades-detached.ts index 4c54108..6ebaecd 100644 --- a/src/signatures/formats/cades-detached.ts +++ b/src/signatures/formats/cades-detached.ts @@ -62,7 +62,7 @@ export class CAdESDetachedBuilder implements CMSFormatBuilder, CMSSignedData { const allCerts = [signerCert, ...chainCerts]; // Build signed attributes - const signedAttrs = this.buildSignedAttributes( + const signedAttrs = await this.buildSignedAttributes( documentHash, digestAlgorithm, signer, @@ -151,13 +151,13 @@ export class CAdESDetachedBuilder implements CMSFormatBuilder, CMSSignedData { /** * Build the signed attributes for CAdES signature. */ - private buildSignedAttributes( + private async buildSignedAttributes( documentHash: Uint8Array, digestAlgorithm: DigestAlgorithm, signer: Signer, signerCert: pkijs.Certificate, signingTime?: Date, - ): pkijs.Attribute[] { + ): Promise { const attrs: pkijs.Attribute[] = []; // Content Type (required) @@ -190,7 +190,7 @@ export class CAdESDetachedBuilder implements CMSFormatBuilder, CMSSignedData { ); // ESS signing-certificate-v2 (required for CAdES/PAdES) - attrs.push(this.buildSigningCertificateV2(signerCert, digestAlgorithm)); + attrs.push(await this.buildSigningCertificateV2(signerCert, digestAlgorithm)); return attrs; } @@ -213,13 +213,13 @@ export class CAdESDetachedBuilder implements CMSFormatBuilder, CMSSignedData { * issuerSerial IssuerSerial OPTIONAL * } */ - private buildSigningCertificateV2( + private async buildSigningCertificateV2( signerCert: pkijs.Certificate, digestAlgorithm: DigestAlgorithm, - ): pkijs.Attribute { + ): Promise { // Hash the certificate const certDer = signerCert.toSchema().toBER(false); - const certHash = hashData(new Uint8Array(certDer), digestAlgorithm); + const certHash = await hashData(new Uint8Array(certDer), digestAlgorithm); // Build IssuerSerial using pkijs classes for proper encoding // IssuerSerial ::= SEQUENCE { diff --git a/src/signatures/signers/google-kms.ts b/src/signatures/signers/google-kms.ts index 4d9aee8..e220854 100644 --- a/src/signatures/signers/google-kms.ts +++ b/src/signatures/signers/google-kms.ts @@ -8,7 +8,6 @@ import { toArrayBuffer } from "#src/helpers/buffer.ts"; import { derToPem, isPem, normalizePem, parsePem } from "#src/helpers/pem.ts"; -import { sha256, sha384, sha512 } from "@noble/hashes/sha2.js"; import { fromBER } from "asn1js"; import * as pkijs from "pkijs"; @@ -659,7 +658,7 @@ export class GoogleKmsSigner implements Signer { } // Hash data locally and build digest object for KMS - const { digest, digestKey } = this.hashData(data, algorithm); + const { digest, digestKey } = await this.hashData(data, algorithm); try { const [response] = await this.client.asymmetricSign({ @@ -701,19 +700,21 @@ export class GoogleKmsSigner implements Signer { /** * Hash data using the specified algorithm. * + * Uses the Web Crypto API for native-speed hashing. + * * @returns The digest bytes and the KMS digest key name */ - private hashData( + private async hashData( data: Uint8Array, algorithm: DigestAlgorithm, - ): { digest: Uint8Array; digestKey: "sha256" | "sha384" | "sha512" } { - switch (algorithm) { - case "SHA-256": - return { digest: sha256(data), digestKey: "sha256" }; - case "SHA-384": - return { digest: sha384(data), digestKey: "sha384" }; - case "SHA-512": - return { digest: sha512(data), digestKey: "sha512" }; - } + ): Promise<{ digest: Uint8Array; digestKey: "sha256" | "sha384" | "sha512" }> { + const digestKeyMap: Record = { + "SHA-256": "sha256", + "SHA-384": "sha384", + "SHA-512": "sha512", + }; + + const arrayBuffer = await crypto.subtle.digest(algorithm, data as Uint8Array); + return { digest: new Uint8Array(arrayBuffer), digestKey: digestKeyMap[algorithm] }; } } diff --git a/src/signatures/utils.ts b/src/signatures/utils.ts index 880ad3a..caf787c 100644 --- a/src/signatures/utils.ts +++ b/src/signatures/utils.ts @@ -2,7 +2,6 @@ * Shared utilities for signature operations. */ -import { sha256, sha384, sha512 } from "@noble/hashes/sha2.js"; import { fromBER } from "asn1js"; import * as pkijs from "pkijs"; @@ -38,19 +37,18 @@ export function escapePdfString(str: string): string { /** * Hash data using the specified algorithm. * + * Uses the Web Crypto API for native-speed hashing, which is significantly + * faster than pure-JS implementations for large inputs (e.g. hashing an + * entire PDF during signing). + * * @param data - Data to hash * @param algorithm - Digest algorithm * @returns Hash bytes */ -export function hashData(data: Uint8Array, algorithm: DigestAlgorithm): Uint8Array { - switch (algorithm) { - case "SHA-256": - return sha256(data); - case "SHA-384": - return sha384(data); - case "SHA-512": - return sha512(data); - } +export async function hashData(data: Uint8Array, algorithm: DigestAlgorithm): Promise { + const digest = await crypto.subtle.digest(algorithm, data as Uint8Array); + + return new Uint8Array(digest); } // ─────────────────────────────────────────────────────────────────────────────