@mkabatek/pptx-viewer 1.5.3 → 1.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +13 -9
  2. package/package.json +2 -9
  3. package/node_modules/emf-converter/LICENSE +0 -21
  4. package/node_modules/emf-converter/README.md +0 -629
  5. package/node_modules/emf-converter/dist/index.d.mts +0 -86
  6. package/node_modules/emf-converter/dist/index.d.ts +0 -86
  7. package/node_modules/emf-converter/dist/index.js +0 -4257
  8. package/node_modules/emf-converter/dist/index.mjs +0 -4253
  9. package/node_modules/emf-converter/package.json +0 -53
  10. package/node_modules/mtx-decompressor/LICENSE +0 -373
  11. package/node_modules/mtx-decompressor/README.md +0 -271
  12. package/node_modules/mtx-decompressor/dist/index.d.mts +0 -84
  13. package/node_modules/mtx-decompressor/dist/index.d.ts +0 -84
  14. package/node_modules/mtx-decompressor/dist/index.js +0 -1532
  15. package/node_modules/mtx-decompressor/dist/index.mjs +0 -1528
  16. package/node_modules/mtx-decompressor/package.json +0 -44
  17. package/node_modules/pptx-viewer-core/LICENSE +0 -21
  18. package/node_modules/pptx-viewer-core/NOTICE +0 -16
  19. package/node_modules/pptx-viewer-core/README.md +0 -1294
  20. package/node_modules/pptx-viewer-core/dist/SvgExporter-BtZczTlB.d.ts +0 -557
  21. package/node_modules/pptx-viewer-core/dist/SvgExporter-D4mBWJHE.d.mts +0 -557
  22. package/node_modules/pptx-viewer-core/dist/cli/index.d.mts +0 -150
  23. package/node_modules/pptx-viewer-core/dist/cli/index.d.ts +0 -150
  24. package/node_modules/pptx-viewer-core/dist/cli/index.js +0 -0
  25. package/node_modules/pptx-viewer-core/dist/cli/index.mjs +0 -0
  26. package/node_modules/pptx-viewer-core/dist/converter/index.d.mts +0 -48
  27. package/node_modules/pptx-viewer-core/dist/converter/index.d.ts +0 -48
  28. package/node_modules/pptx-viewer-core/dist/converter/index.js +0 -0
  29. package/node_modules/pptx-viewer-core/dist/converter/index.mjs +0 -0
  30. package/node_modules/pptx-viewer-core/dist/index.d.mts +0 -12744
  31. package/node_modules/pptx-viewer-core/dist/index.d.ts +0 -12744
  32. package/node_modules/pptx-viewer-core/dist/index.js +0 -66894
  33. package/node_modules/pptx-viewer-core/dist/index.mjs +0 -66420
  34. package/node_modules/pptx-viewer-core/dist/presentation-nZxgWvXq.d.mts +0 -5645
  35. package/node_modules/pptx-viewer-core/dist/presentation-nZxgWvXq.d.ts +0 -5645
  36. package/node_modules/pptx-viewer-core/dist/signature-inspection-status-BCUpfCQh.d.mts +0 -220
  37. package/node_modules/pptx-viewer-core/dist/signature-inspection-status-BCUpfCQh.d.ts +0 -220
  38. package/node_modules/pptx-viewer-core/dist/signature-node/index.d.mts +0 -177
  39. package/node_modules/pptx-viewer-core/dist/signature-node/index.d.ts +0 -177
  40. package/node_modules/pptx-viewer-core/dist/signature-node/index.js +0 -1206
  41. package/node_modules/pptx-viewer-core/dist/signature-node/index.mjs +0 -1143
  42. package/node_modules/pptx-viewer-core/dist/text-operations-DCTGMltY.d.mts +0 -134
  43. package/node_modules/pptx-viewer-core/dist/text-operations-DYmhoi7U.d.ts +0 -134
  44. package/node_modules/pptx-viewer-core/package.json +0 -96
@@ -1,1143 +0,0 @@
1
- import { DOMParser, XMLSerializer } from '@xmldom/xmldom';
2
- import { SignedXml } from 'xml-crypto';
3
- import crypto from 'crypto';
4
- import path from 'path';
5
- import tls from 'tls';
6
- import forge from 'node-forge';
7
- import http from 'http';
8
- import https from 'https';
9
- import fs from 'fs/promises';
10
- import JSZip from 'jszip';
11
-
12
- // src/core/utils/signature-constants.ts
13
- var DIGITAL_SIGNATURE_ORIGIN_REL_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/origin";
14
- var DIGITAL_SIGNATURE_REL_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/signature";
15
- var PPTX_VIEWER_MANIFEST_NS = "urn:pptx-viewer:ooxml-signature:v1";
16
- var XMLDSIG_NS = "http://www.w3.org/2000/09/xmldsig#";
17
- var OPC_RELATIONSHIP_TRANSFORM = "http://schemas.openxmlformats.org/package/2006/RelationshipTransform";
18
- var XML_TRANSFORM_ENVELOPED_SIGNATURE = "http://www.w3.org/2000/09/xmldsig#enveloped-signature";
19
- var SUPPORTED_XML_CANON_TRANSFORMS = /* @__PURE__ */ new Set([
20
- "http://www.w3.org/TR/2001/REC-xml-c14n-20010315",
21
- "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments",
22
- "http://www.w3.org/2001/10/xml-exc-c14n#",
23
- "http://www.w3.org/2001/10/xml-exc-c14n#WithComments",
24
- XML_TRANSFORM_ENVELOPED_SIGNATURE
25
- ]);
26
- var ENTERPRISE_TRUST_ROOTS_FILE_ENV = "PPTX_VIEWER_TRUST_ROOTS_FILE";
27
- var ENTERPRISE_TRUST_ROOTS_PEM_ENV = "PPTX_VIEWER_TRUST_ROOTS_PEM";
28
- var ENTERPRISE_REQUIRE_REVOCATION_ENV = "PPTX_VIEWER_REQUIRE_REVOCATION_CHECK";
29
- var ENTERPRISE_FAIL_ON_REVOCATION_UNKNOWN_ENV = "PPTX_VIEWER_FAIL_ON_REVOCATION_UNKNOWN";
30
- var ENTERPRISE_REQUIRE_TIMESTAMP_ENV = "PPTX_VIEWER_REQUIRE_TIMESTAMP";
31
- var DIGEST_ALGORITHM_TO_HASH = {
32
- "http://www.w3.org/2000/09/xmldsig#sha1": "sha1",
33
- "http://www.w3.org/2001/04/xmlenc#sha256": "sha256",
34
- "http://www.w3.org/2001/04/xmlenc#sha384": "sha384",
35
- "http://www.w3.org/2001/04/xmlenc#sha512": "sha512"
36
- };
37
- var DIGEST_ALGORITHM_TO_WEB_CRYPTO = {
38
- "http://www.w3.org/2000/09/xmldsig#sha1": "SHA-1",
39
- "http://www.w3.org/2001/04/xmlenc#sha256": "SHA-256",
40
- "http://www.w3.org/2001/04/xmlenc#sha384": "SHA-384",
41
- "http://www.w3.org/2001/04/xmlenc#sha512": "SHA-512"
42
- };
43
-
44
- // src/core/utils/signature-xml-utils.ts
45
- function escapeXmlAttr(value) {
46
- return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
47
- }
48
- function escapeXmlText(value) {
49
- return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
50
- }
51
- function isValidBase64(value) {
52
- return typeof value === "string" && value.length > 0 && /^[A-Za-z0-9+/=\s]+$/.test(value);
53
- }
54
- function extractTagAttribute(xml, tagName, attributeName) {
55
- const pattern = new RegExp(
56
- `<${tagName}\\b[^>]*\\b${attributeName}="(?<attributeValue>[^"]+)"`,
57
- "i"
58
- );
59
- const match = xml.match(pattern);
60
- return match?.groups?.["attributeValue"]?.trim();
61
- }
62
- function extractFirstTagText(xml, localName) {
63
- const pattern = new RegExp(
64
- `<([\\w.-]+:)?${localName}\\b[^>]*>([\\s\\S]*?)<\\/([\\w.-]+:)?${localName}>`,
65
- "i"
66
- );
67
- const match = xml.match(pattern);
68
- return match?.[2]?.replace(/\s+/g, "").trim() || void 0;
69
- }
70
- function extractAllTagText(xml, localName) {
71
- const result = [];
72
- const regex = new RegExp(
73
- `<([\\w.-]+:)?${localName}\\b[^>]*>([\\s\\S]*?)<\\/([\\w.-]+:)?${localName}>`,
74
- "gi"
75
- );
76
- let match = regex.exec(xml);
77
- while (match) {
78
- const value = match[2]?.replace(/\s+/g, "").trim();
79
- if (value) {
80
- result.push(value);
81
- }
82
- match = regex.exec(xml);
83
- }
84
- return result;
85
- }
86
-
87
- // src/core/utils/signature-reference-utils.ts
88
- function normalizePartPath(partPath) {
89
- return partPath.replace(/\\/g, "/").replace(/^\/+/, "");
90
- }
91
- function resolveReferenceUriToPart(uri) {
92
- const trimmed = uri.trim();
93
- if (trimmed.length === 0 || trimmed.startsWith("#")) {
94
- return void 0;
95
- }
96
- const decoded = decodeURIComponent(trimmed);
97
- return normalizePartPath(decoded.startsWith("/") ? decoded.slice(1) : decoded);
98
- }
99
-
100
- // src/core/utils/signature-digest.ts
101
- async function computeDigestBase64(content, digestAlgorithmUri) {
102
- const webCryptoAlgo = DIGEST_ALGORITHM_TO_WEB_CRYPTO[digestAlgorithmUri];
103
- if (!webCryptoAlgo) {
104
- return void 0;
105
- }
106
- if (typeof globalThis.crypto?.subtle === "undefined") {
107
- return void 0;
108
- }
109
- const buf = new ArrayBuffer(content.byteLength);
110
- new Uint8Array(buf).set(content);
111
- const digest = await globalThis.crypto.subtle.digest(webCryptoAlgo, buf);
112
- return uint8ArrayToBase64(new Uint8Array(digest));
113
- }
114
- function uint8ArrayToBase64(bytes) {
115
- let binary = "";
116
- for (let i = 0; i < bytes.byteLength; i++) {
117
- binary += String.fromCharCode(bytes[i]);
118
- }
119
- return btoa(binary);
120
- }
121
-
122
- // src/core/utils/signature-inspection-status.ts
123
- function computeDetailStatus(detail, policy) {
124
- const hasDigestMismatch = detail.referenceChecks.some(
125
- (check) => check.digestStatus === "mismatch"
126
- );
127
- const hasMissingPart = detail.missingPartReferences.length > 0;
128
- const hasVerifiedDigest = detail.referenceChecks.some(
129
- (check) => check.digestStatus === "verified"
130
- );
131
- const revocationUnknown = detail.certificateRevocationStatus === "unknown" || detail.certificateRevocationStatus === "not-checked" || detail.certificateRevocationStatus === "error";
132
- const timestampMissingOrUntrusted = detail.timestampAuthorityStatus === "not-present" || detail.timestampAuthorityStatus === "not-checked" || detail.timestampAuthorityStatus === "error" || detail.timestampAuthorityStatus === "untrusted";
133
- if (detail.signatureValueStatus === "invalid") {
134
- return "signature-invalid";
135
- }
136
- if (hasMissingPart) {
137
- return "reference-missing";
138
- }
139
- if (hasDigestMismatch) {
140
- return "digest-mismatch";
141
- }
142
- if (detail.certificateRevocationStatus === "revoked") {
143
- return "certificate-revoked";
144
- }
145
- if (policy.requireRevocationCheck && (policy.failOnRevocationUnknown ? revocationUnknown : detail.certificateRevocationStatus !== "good")) {
146
- return "certificate-untrusted";
147
- }
148
- if (detail.timestampAuthorityStatus === "invalid") {
149
- return "timestamp-invalid";
150
- }
151
- if (policy.requireTimestamp && timestampMissingOrUntrusted) {
152
- return "timestamp-untrusted";
153
- }
154
- if (detail.timestampAuthorityStatus === "untrusted") {
155
- return "timestamp-untrusted";
156
- }
157
- if (detail.certificateTrustStatus === "untrusted" && detail.signatureValueStatus === "verified") {
158
- return "certificate-untrusted";
159
- }
160
- if (hasVerifiedDigest && detail.signatureValueStatus === "verified") {
161
- return "verified";
162
- }
163
- return "structural-only";
164
- }
165
- function computeVerificationStatus(details) {
166
- const hasInvalidSignature = details.some((detail) => detail.status === "signature-invalid");
167
- const hasMissing = details.some((detail) => detail.status === "reference-missing");
168
- const hasMismatch = details.some((detail) => detail.status === "digest-mismatch");
169
- const hasUntrusted = details.some((detail) => detail.status === "certificate-untrusted");
170
- const hasRevoked = details.some((detail) => detail.status === "certificate-revoked");
171
- const hasTimestampInvalid = details.some((detail) => detail.status === "timestamp-invalid");
172
- const hasTimestampUntrusted = details.some((detail) => detail.status === "timestamp-untrusted");
173
- const allVerified = details.every((detail) => detail.status === "verified");
174
- return hasInvalidSignature ? "signature-invalid" : hasRevoked ? "certificate-revoked" : hasTimestampInvalid ? "timestamp-invalid" : hasTimestampUntrusted ? "timestamp-untrusted" : hasMissing ? "reference-missing" : hasMismatch ? "digest-mismatch" : allVerified ? "verified-trusted" : hasUntrusted ? "verified-untrusted" : "present-not-verified";
175
- }
176
- function getNodeLocalName(node) {
177
- const localNameNode = node;
178
- if (localNameNode.localName) {
179
- return localNameNode.localName;
180
- }
181
- const nodeName = node.nodeName || "";
182
- const sep = nodeName.indexOf(":");
183
- return sep >= 0 ? nodeName.slice(sep + 1) : nodeName;
184
- }
185
- function getFirstDescendantElementByLocalName(parent, localName) {
186
- const elements = parent.getElementsByTagName("*");
187
- for (let index = 0; index < elements.length; index += 1) {
188
- const element = elements.item(index);
189
- if (!element) {
190
- continue;
191
- }
192
- if (getNodeLocalName(element) === localName) {
193
- return element;
194
- }
195
- }
196
- return void 0;
197
- }
198
- function canonicalizeNode(node, algorithm) {
199
- const canonicalizer = new SignedXml();
200
- return canonicalizer.getCanonXml([algorithm], node);
201
- }
202
- function canonicalizeSignedInfoXml(signedInfoXml) {
203
- const parser = new DOMParser();
204
- const signedInfoDoc = parser.parseFromString(signedInfoXml, "text/xml");
205
- if (!signedInfoDoc.documentElement) {
206
- throw new Error("Unable to canonicalize SignedInfo: invalid XML.");
207
- }
208
- return canonicalizeNode(signedInfoDoc.documentElement, "http://www.w3.org/2001/10/xml-exc-c14n#");
209
- }
210
- function certPemFromBase64(certBase64) {
211
- try {
212
- const der = Buffer.from(certBase64, "base64");
213
- if (der.length === 0) {
214
- return void 0;
215
- }
216
- const b64 = der.toString("base64");
217
- const lines = b64.match(/.{1,64}/g);
218
- if (!lines) {
219
- return void 0;
220
- }
221
- return `-----BEGIN CERTIFICATE-----
222
- ${lines.join("\n")}
223
- -----END CERTIFICATE-----`;
224
- } catch {
225
- return void 0;
226
- }
227
- }
228
- function certFingerprintSha256(certPem) {
229
- try {
230
- const cert = new crypto.X509Certificate(certPem);
231
- return cert.fingerprint256.replace(/:/g, "").toLowerCase();
232
- } catch {
233
- return void 0;
234
- }
235
- }
236
- function asn1Child(node, index) {
237
- return Array.isArray(node.value) ? node.value[index] : void 0;
238
- }
239
- function asn1Bytes(node) {
240
- return typeof node.value === "string" ? node.value : "";
241
- }
242
- function extractOcspUrls(certPem) {
243
- try {
244
- const cert = new crypto.X509Certificate(certPem);
245
- const infoAccess = cert.infoAccess;
246
- if (!infoAccess) {
247
- return [];
248
- }
249
- const urls = [];
250
- for (const line of infoAccess.split("\n")) {
251
- const match = /OCSP\s*-\s*URI:(https?:\/\/\S+)/i.exec(line.trim());
252
- if (match?.[1]) {
253
- urls.push(match[1]);
254
- }
255
- }
256
- return urls;
257
- } catch {
258
- return [];
259
- }
260
- }
261
- var HTTP_TIMEOUT_MS = 1e4;
262
- function httpPost(url, body, contentType) {
263
- return new Promise((resolve, reject) => {
264
- let parsed;
265
- try {
266
- parsed = new URL(url);
267
- } catch {
268
- reject(new Error("Invalid URL"));
269
- return;
270
- }
271
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
272
- reject(new Error("Unsupported protocol"));
273
- return;
274
- }
275
- const transport = parsed.protocol === "https:" ? https : http;
276
- const req = transport.request(
277
- {
278
- hostname: parsed.hostname,
279
- port: parsed.port || void 0,
280
- path: parsed.pathname + parsed.search,
281
- method: "POST",
282
- headers: {
283
- "Content-Type": contentType,
284
- "Content-Length": body.length
285
- },
286
- timeout: HTTP_TIMEOUT_MS
287
- },
288
- (res) => {
289
- const chunks = [];
290
- res.on("data", (chunk) => chunks.push(chunk));
291
- res.on("end", () => resolve(Buffer.concat(chunks)));
292
- res.on("error", reject);
293
- }
294
- );
295
- req.on("error", reject);
296
- req.on("timeout", () => {
297
- req.destroy();
298
- reject(new Error("Timeout"));
299
- });
300
- req.write(body);
301
- req.end();
302
- });
303
- }
304
- var SHA1_OID = "1.3.14.3.2.26";
305
- function buildOcspRequestDer(leafPem, issuerPem) {
306
- try {
307
- const leaf = forge.pki.certificateFromPem(leafPem);
308
- const issuer = forge.pki.certificateFromPem(issuerPem);
309
- const issuerNameDer = forge.asn1.toDer(forge.pki.distinguishedNameToAsn1(issuer.subject)).getBytes();
310
- const issuerNameHash = forge.md.sha1.create().update(issuerNameDer).digest().getBytes();
311
- const pubKeyDer = forge.asn1.toDer(forge.pki.publicKeyToAsn1(issuer.publicKey)).getBytes();
312
- const pubKeyAsn1 = forge.asn1.fromDer(pubKeyDer);
313
- const bitString = asn1Child(pubKeyAsn1, 1);
314
- const rawKey = bitString ? asn1Bytes(bitString).substring(1) : "";
315
- const issuerKeyHash = forge.md.sha1.create().update(rawKey).digest().getBytes();
316
- const serialBytes = forge.util.hexToBytes(leaf.serialNumber);
317
- const request = forge.asn1.create(forge.asn1.Class.UNIVERSAL, forge.asn1.Type.SEQUENCE, true, [
318
- forge.asn1.create(forge.asn1.Class.UNIVERSAL, forge.asn1.Type.SEQUENCE, true, [
319
- forge.asn1.create(forge.asn1.Class.UNIVERSAL, forge.asn1.Type.SEQUENCE, true, [
320
- forge.asn1.create(forge.asn1.Class.UNIVERSAL, forge.asn1.Type.SEQUENCE, true, [
321
- forge.asn1.create(forge.asn1.Class.UNIVERSAL, forge.asn1.Type.SEQUENCE, true, [
322
- forge.asn1.create(forge.asn1.Class.UNIVERSAL, forge.asn1.Type.SEQUENCE, true, [
323
- forge.asn1.create(
324
- forge.asn1.Class.UNIVERSAL,
325
- forge.asn1.Type.OID,
326
- false,
327
- forge.asn1.oidToDer(SHA1_OID).getBytes()
328
- ),
329
- forge.asn1.create(forge.asn1.Class.UNIVERSAL, forge.asn1.Type.NULL, false, "")
330
- ]),
331
- forge.asn1.create(
332
- forge.asn1.Class.UNIVERSAL,
333
- forge.asn1.Type.OCTETSTRING,
334
- false,
335
- issuerNameHash
336
- ),
337
- forge.asn1.create(
338
- forge.asn1.Class.UNIVERSAL,
339
- forge.asn1.Type.OCTETSTRING,
340
- false,
341
- issuerKeyHash
342
- ),
343
- forge.asn1.create(
344
- forge.asn1.Class.UNIVERSAL,
345
- forge.asn1.Type.INTEGER,
346
- false,
347
- serialBytes
348
- )
349
- ])
350
- ])
351
- ])
352
- ])
353
- ]);
354
- return Buffer.from(forge.asn1.toDer(request).getBytes(), "binary");
355
- } catch {
356
- return void 0;
357
- }
358
- }
359
- function parseOcspResponseStatus(data) {
360
- try {
361
- const asn1 = forge.asn1.fromDer(forge.util.createBuffer(data.toString("binary")));
362
- const statusNode = asn1Child(asn1, 0);
363
- if (!statusNode || asn1Bytes(statusNode) !== "\0") {
364
- return "error";
365
- }
366
- const respBytesWrapper = asn1Child(asn1, 1);
367
- if (!respBytesWrapper) {
368
- return "unknown";
369
- }
370
- const respBytesSeq = asn1Child(respBytesWrapper, 0);
371
- if (!respBytesSeq) {
372
- return "unknown";
373
- }
374
- const respOctet = asn1Child(respBytesSeq, 1);
375
- if (!respOctet) {
376
- return "unknown";
377
- }
378
- const basicResp = forge.asn1.fromDer(forge.util.createBuffer(asn1Bytes(respOctet)));
379
- const tbsData = asn1Child(basicResp, 0);
380
- if (!tbsData) {
381
- return "unknown";
382
- }
383
- const first = asn1Child(tbsData, 0);
384
- const responsesIdx = first?.tagClass === forge.asn1.Class.CONTEXT_SPECIFIC && first.type === 0 ? 3 : 2;
385
- const responses = asn1Child(tbsData, responsesIdx);
386
- if (!responses) {
387
- return "unknown";
388
- }
389
- const single = asn1Child(responses, 0);
390
- if (!single) {
391
- return "unknown";
392
- }
393
- const certStatus = asn1Child(single, 1);
394
- if (!certStatus) {
395
- return "unknown";
396
- }
397
- if (certStatus.tagClass === forge.asn1.Class.CONTEXT_SPECIFIC) {
398
- if (certStatus.type === 0) {
399
- return "good";
400
- }
401
- if (certStatus.type === 1) {
402
- return "revoked";
403
- }
404
- }
405
- return "unknown";
406
- } catch {
407
- return "error";
408
- }
409
- }
410
- async function evaluateCertificateRevocation(leafCertPem, issuerCertPem) {
411
- const ocspUrls = extractOcspUrls(leafCertPem);
412
- const checkedOcspUrls = [];
413
- const checkedCrlUrls = [];
414
- if (issuerCertPem && ocspUrls.length > 0) {
415
- const reqDer = buildOcspRequestDer(leafCertPem, issuerCertPem);
416
- if (reqDer) {
417
- for (const url of ocspUrls) {
418
- checkedOcspUrls.push(url);
419
- try {
420
- const resp = await httpPost(url, reqDer, "application/ocsp-request");
421
- const status = parseOcspResponseStatus(resp);
422
- if (status === "good" || status === "revoked") {
423
- return { status, checkedOcspUrls, checkedCrlUrls };
424
- }
425
- } catch {
426
- }
427
- }
428
- }
429
- }
430
- if (checkedOcspUrls.length === 0) {
431
- return { status: "not-checked", checkedOcspUrls, checkedCrlUrls };
432
- }
433
- return {
434
- status: "unknown",
435
- error: "Could not determine revocation status from available responders.",
436
- checkedOcspUrls,
437
- checkedCrlUrls
438
- };
439
- }
440
- var TIMESTAMP_TAG_REGEX = /<(?:[\w.-]+:)?(?:EncapsulatedTimeStamp|SignatureTimeStamp)[^>]*>([\s\S]*?)<\/(?:[\w.-]+:)?(?:EncapsulatedTimeStamp|SignatureTimeStamp)>/i;
441
- async function evaluateTimestampAuthority(signatureXml) {
442
- try {
443
- const match = TIMESTAMP_TAG_REGEX.exec(signatureXml);
444
- if (!match?.[1]?.trim()) {
445
- return { status: "not-present" };
446
- }
447
- const tokenBase64 = match[1].replace(/\s+/g, "");
448
- const tokenDer = Buffer.from(tokenBase64, "base64");
449
- if (tokenDer.length === 0) {
450
- return { status: "invalid", error: "Empty timestamp token." };
451
- }
452
- const asn1 = forge.asn1.fromDer(forge.util.createBuffer(tokenDer.toString("binary")));
453
- const contentType = asn1Child(asn1, 0);
454
- if (!contentType) {
455
- return {
456
- status: "invalid",
457
- error: "Malformed timestamp token structure."
458
- };
459
- }
460
- return { status: "valid" };
461
- } catch (err) {
462
- return {
463
- status: "error",
464
- error: `Timestamp evaluation failed: ${String(err)}`
465
- };
466
- }
467
- }
468
-
469
- // src/signature-node/certificate-utils.ts
470
- function certificateInfoFromBase64(certBase64) {
471
- try {
472
- const certificate = new crypto.X509Certificate(Buffer.from(certBase64, "base64"));
473
- return {
474
- subject: certificate.subject || void 0,
475
- issuer: certificate.issuer || void 0,
476
- serialNumber: certificate.serialNumber || void 0,
477
- validFrom: certificate.validFrom || void 0,
478
- validTo: certificate.validTo || void 0
479
- };
480
- } catch {
481
- return void 0;
482
- }
483
- }
484
- function validateCertificateChain(certBase64List, additionalRootsPem) {
485
- if (certBase64List.length === 0) {
486
- return { status: "not-checked" };
487
- }
488
- try {
489
- const chain = certBase64List.map((value) => {
490
- const der = forge.util.decode64(value);
491
- return forge.pki.certificateFromAsn1(forge.asn1.fromDer(der));
492
- });
493
- const rootPem = [...tls.rootCertificates, ...additionalRootsPem];
494
- const caStore = forge.pki.createCaStore(rootPem);
495
- const verified = forge.pki.verifyCertificateChain(caStore, chain);
496
- return verified ? { status: "trusted" } : {
497
- status: "untrusted",
498
- error: "Certificate chain is not trusted."
499
- };
500
- } catch (error) {
501
- return {
502
- status: "untrusted",
503
- error: `Certificate trust validation failed: ${String(error)}`
504
- };
505
- }
506
- }
507
- function signatureAlgorithmToVerifyAlgorithm(signatureMethod) {
508
- switch (signatureMethod) {
509
- case "http://www.w3.org/2000/09/xmldsig#rsa-sha1":
510
- return "RSA-SHA1";
511
- case "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256":
512
- return "RSA-SHA256";
513
- case "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384":
514
- return "RSA-SHA384";
515
- case "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512":
516
- return "RSA-SHA512";
517
- default:
518
- return void 0;
519
- }
520
- }
521
- function verifySignatureValue(signatureXml, certBase64List) {
522
- try {
523
- const parser = new DOMParser();
524
- const doc = parser.parseFromString(signatureXml, "text/xml");
525
- const signatureNode = doc.getElementsByTagNameNS(XMLDSIG_NS, "Signature")[0];
526
- if (!signatureNode) {
527
- return "not-checked";
528
- }
529
- const certPem = certBase64List.length > 0 ? certPemFromBase64(certBase64List[0]) : void 0;
530
- if (!certPem) {
531
- return "not-checked";
532
- }
533
- const signedInfoNode = doc.getElementsByTagNameNS(XMLDSIG_NS, "SignedInfo")[0];
534
- const signatureValueNode = doc.getElementsByTagNameNS(XMLDSIG_NS, "SignatureValue")[0];
535
- if (!signedInfoNode || !signatureValueNode) {
536
- return "invalid";
537
- }
538
- const canonicalizationMethod = doc.getElementsByTagNameNS(XMLDSIG_NS, "CanonicalizationMethod").item(0)?.getAttribute("Algorithm") ?? "http://www.w3.org/2001/10/xml-exc-c14n#";
539
- const signatureMethod = doc.getElementsByTagNameNS(XMLDSIG_NS, "SignatureMethod").item(0)?.getAttribute("Algorithm");
540
- const verifyAlgorithm = signatureAlgorithmToVerifyAlgorithm(signatureMethod ?? void 0);
541
- if (!verifyAlgorithm) {
542
- return "invalid";
543
- }
544
- const canonicalSignedInfo = canonicalizeNode(signedInfoNode, canonicalizationMethod);
545
- const signatureValueBase64 = signatureValueNode.textContent?.replace(/\s+/g, "").trim() ?? "";
546
- if (signatureValueBase64.length === 0) {
547
- return "invalid";
548
- }
549
- const verifier = crypto.createVerify(verifyAlgorithm);
550
- verifier.update(Buffer.from(canonicalSignedInfo, "utf8"));
551
- verifier.end();
552
- const isValid = verifier.verify(certPem, Buffer.from(signatureValueBase64, "base64"));
553
- return isValid ? "verified" : "invalid";
554
- } catch {
555
- return "invalid";
556
- }
557
- }
558
- function loadSigningMaterialFromBuffer(certificateBuffer, certificatePath, certificatePassword) {
559
- const extension = path.extname(certificatePath).toLowerCase();
560
- if (extension === ".pfx" || extension === ".p12") {
561
- const p12Der = forge.util.createBuffer(Buffer.from(certificateBuffer).toString("binary"));
562
- const p12Asn1 = forge.asn1.fromDer(p12Der);
563
- const p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, certificatePassword || "");
564
- const keyBag = p12.getBags({
565
- bagType: forge.pki.oids.pkcs8ShroudedKeyBag
566
- })[forge.pki.oids.pkcs8ShroudedKeyBag];
567
- const certBag = p12.getBags({ bagType: forge.pki.oids.certBag })[forge.pki.oids.certBag];
568
- if (!keyBag || keyBag.length === 0 || !keyBag[0]?.key) {
569
- throw new Error("No private key found in PKCS#12 certificate.");
570
- }
571
- if (!certBag || certBag.length === 0 || !certBag[0]?.cert) {
572
- throw new Error("No certificate found in PKCS#12 certificate.");
573
- }
574
- return {
575
- privateKeyPem: forge.pki.privateKeyToPem(keyBag[0].key),
576
- certificatePem: forge.pki.certificateToPem(certBag[0].cert)
577
- };
578
- }
579
- const pem = Buffer.from(certificateBuffer).toString("utf8");
580
- const privateKeyMatch = pem.match(
581
- /-----BEGIN (?:RSA |EC |ENCRYPTED )?PRIVATE KEY-----[\s\S]+?-----END (?:RSA |EC |ENCRYPTED )?PRIVATE KEY-----/m
582
- );
583
- const certMatch = pem.match(/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/m);
584
- if (!privateKeyMatch || !certMatch) {
585
- throw new Error("PEM certificate must contain both private key and certificate.");
586
- }
587
- return {
588
- privateKeyPem: privateKeyMatch[0],
589
- certificatePem: certMatch[0]
590
- };
591
- }
592
- function pemCertificateToBase64(pem) {
593
- return pem.replace(/-----BEGIN CERTIFICATE-----/g, "").replace(/-----END CERTIFICATE-----/g, "").replace(/\s+/g, "").trim();
594
- }
595
- function extractPemCertificatesFromText(text) {
596
- const matches = text.match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g) ?? [];
597
- return matches.map((value) => value.trim()).filter((value) => value.length > 0);
598
- }
599
- function parseBooleanEnv(envValue) {
600
- if (!envValue) {
601
- return false;
602
- }
603
- const normalized = envValue.trim().toLowerCase();
604
- return normalized === "1" || normalized === "true" || normalized === "yes";
605
- }
606
- function parseListEnv(envValue) {
607
- if (!envValue) {
608
- return [];
609
- }
610
- return envValue.split(/[;,]/).map((value) => value.trim()).filter((value) => value.length > 0);
611
- }
612
- async function loadEnterpriseTrustRoots() {
613
- const roots = [];
614
- const inlinePem = process.env[ENTERPRISE_TRUST_ROOTS_PEM_ENV];
615
- if (inlinePem) {
616
- roots.push(...extractPemCertificatesFromText(inlinePem));
617
- }
618
- const trustRootPaths = parseListEnv(process.env[ENTERPRISE_TRUST_ROOTS_FILE_ENV]);
619
- for (const trustRootPath of trustRootPaths) {
620
- try {
621
- const pemText = await fs.readFile(trustRootPath, "utf8");
622
- roots.push(...extractPemCertificatesFromText(pemText));
623
- } catch {
624
- }
625
- }
626
- return roots;
627
- }
628
- function getSignatureValidationPolicy() {
629
- return {
630
- requireRevocationCheck: parseBooleanEnv(process.env[ENTERPRISE_REQUIRE_REVOCATION_ENV]),
631
- failOnRevocationUnknown: parseBooleanEnv(
632
- process.env[ENTERPRISE_FAIL_ON_REVOCATION_UNKNOWN_ENV]
633
- ),
634
- requireTimestamp: parseBooleanEnv(process.env[ENTERPRISE_REQUIRE_TIMESTAMP_ENV])
635
- };
636
- }
637
- function extractReferenceTransforms(referenceNode) {
638
- const transforms = [];
639
- const transformNodes = referenceNode.getElementsByTagNameNS(XMLDSIG_NS, "Transform");
640
- for (let index = 0; index < transformNodes.length; index += 1) {
641
- const transformNode = transformNodes.item(index);
642
- if (!transformNode) {
643
- continue;
644
- }
645
- const algorithm = transformNode.getAttribute("Algorithm")?.trim();
646
- if (!algorithm) {
647
- continue;
648
- }
649
- const relationshipReferenceIds = [];
650
- const childNodes = transformNode.getElementsByTagName("*");
651
- for (let childIndex = 0; childIndex < childNodes.length; childIndex += 1) {
652
- const childNode = childNodes.item(childIndex);
653
- if (!childNode) {
654
- continue;
655
- }
656
- if (getNodeLocalName(childNode) !== "RelationshipReference") {
657
- continue;
658
- }
659
- const sourceId = childNode.getAttribute("SourceId")?.trim();
660
- if (sourceId && sourceId.length > 0) {
661
- relationshipReferenceIds.push(sourceId);
662
- }
663
- }
664
- transforms.push({ algorithm, relationshipReferenceIds });
665
- }
666
- return transforms;
667
- }
668
- function applyRelationshipTransform(xmlText, relationshipIds) {
669
- try {
670
- const parser = new DOMParser();
671
- const serializer = new XMLSerializer();
672
- const doc = parser.parseFromString(xmlText, "text/xml");
673
- const relationshipsRoot = getFirstDescendantElementByLocalName(doc, "Relationships");
674
- if (!relationshipsRoot) {
675
- return void 0;
676
- }
677
- if (relationshipIds.length === 0) {
678
- return serializer.serializeToString(doc);
679
- }
680
- const idSet = new Set(relationshipIds);
681
- const relationshipNodes = Array.from(relationshipsRoot.getElementsByTagName("*")).filter(
682
- (node) => getNodeLocalName(node) === "Relationship"
683
- );
684
- for (const relationshipNode of relationshipNodes) {
685
- const relId = relationshipNode.getAttribute("Id")?.trim();
686
- if (!relId || !idSet.has(relId)) {
687
- relationshipNode.parentNode?.removeChild(relationshipNode);
688
- }
689
- }
690
- return serializer.serializeToString(doc);
691
- } catch {
692
- return void 0;
693
- }
694
- }
695
- function applyReferenceTransforms(partBytes, transforms) {
696
- let transformedBytes = partBytes;
697
- const unsupportedAlgorithms = [];
698
- for (const transform of transforms) {
699
- if (transform.algorithm === OPC_RELATIONSHIP_TRANSFORM) {
700
- const nextXml = applyRelationshipTransform(
701
- Buffer.from(transformedBytes).toString("utf8"),
702
- transform.relationshipReferenceIds
703
- );
704
- if (!nextXml) {
705
- unsupportedAlgorithms.push(transform.algorithm);
706
- continue;
707
- }
708
- transformedBytes = new Uint8Array(Buffer.from(nextXml, "utf8"));
709
- continue;
710
- }
711
- if (!SUPPORTED_XML_CANON_TRANSFORMS.has(transform.algorithm)) {
712
- unsupportedAlgorithms.push(transform.algorithm);
713
- continue;
714
- }
715
- try {
716
- const parser = new DOMParser();
717
- const doc = parser.parseFromString(
718
- Buffer.from(transformedBytes).toString("utf8"),
719
- "text/xml"
720
- );
721
- if (!doc.documentElement) {
722
- unsupportedAlgorithms.push(transform.algorithm);
723
- continue;
724
- }
725
- const canonical = canonicalizeNode(doc.documentElement, transform.algorithm);
726
- transformedBytes = new Uint8Array(Buffer.from(canonical, "utf8"));
727
- } catch {
728
- unsupportedAlgorithms.push(transform.algorithm);
729
- }
730
- }
731
- return { data: transformedBytes, unsupportedAlgorithms };
732
- }
733
- function computeDigestBase642(content, digestAlgorithmUri) {
734
- const hashName = DIGEST_ALGORITHM_TO_HASH[digestAlgorithmUri];
735
- if (!hashName) {
736
- return void 0;
737
- }
738
- return crypto.createHash(hashName).update(Buffer.from(content)).digest("base64");
739
- }
740
- async function buildReferenceChecksFromSignatureXml(zip, signatureXml) {
741
- const parser = new DOMParser();
742
- const doc = parser.parseFromString(signatureXml, "text/xml");
743
- const referenceNodes = doc.getElementsByTagNameNS(XMLDSIG_NS, "Reference");
744
- const checks = [];
745
- for (let index = 0; index < referenceNodes.length; index += 1) {
746
- const referenceNode = referenceNodes.item(index);
747
- if (!referenceNode) {
748
- continue;
749
- }
750
- const uri = referenceNode.getAttribute("URI")?.trim() ?? "";
751
- const digestMethodNode = getFirstDescendantElementByLocalName(referenceNode, "DigestMethod");
752
- const digestValueNode = getFirstDescendantElementByLocalName(referenceNode, "DigestValue");
753
- const digestAlgorithm = digestMethodNode?.getAttribute("Algorithm")?.trim();
754
- const digestExpectedBase64 = digestValueNode?.textContent?.replace(/\s+/g, "").trim();
755
- const transforms = extractReferenceTransforms(referenceNode);
756
- const transformAlgorithms = transforms.map((transform) => transform.algorithm);
757
- const resolvedPartPath = resolveReferenceUriToPart(uri);
758
- if (!resolvedPartPath) {
759
- checks.push({
760
- uri,
761
- existsInPackage: true,
762
- digestAlgorithm,
763
- digestExpectedBase64,
764
- digestStatus: "insufficient-data",
765
- transformAlgorithms
766
- });
767
- continue;
768
- }
769
- const part = zip.file(resolvedPartPath);
770
- if (!part) {
771
- checks.push({
772
- uri,
773
- resolvedPartPath,
774
- existsInPackage: false,
775
- digestAlgorithm,
776
- digestExpectedBase64,
777
- digestStatus: "missing-part",
778
- transformAlgorithms
779
- });
780
- continue;
781
- }
782
- if (!digestAlgorithm || !digestExpectedBase64) {
783
- checks.push({
784
- uri,
785
- resolvedPartPath,
786
- existsInPackage: true,
787
- digestAlgorithm,
788
- digestExpectedBase64,
789
- digestStatus: "insufficient-data",
790
- transformAlgorithms
791
- });
792
- continue;
793
- }
794
- const rawContent = await part.async("uint8array");
795
- const transformed = applyReferenceTransforms(rawContent, transforms);
796
- if (transformed.unsupportedAlgorithms.length > 0) {
797
- checks.push({
798
- uri,
799
- resolvedPartPath,
800
- existsInPackage: true,
801
- digestAlgorithm,
802
- digestExpectedBase64,
803
- digestStatus: "unsupported-transform",
804
- transformAlgorithms
805
- });
806
- continue;
807
- }
808
- const digestActualBase64 = computeDigestBase642(transformed.data, digestAlgorithm);
809
- if (!digestActualBase64) {
810
- checks.push({
811
- uri,
812
- resolvedPartPath,
813
- existsInPackage: true,
814
- digestAlgorithm,
815
- digestExpectedBase64,
816
- digestStatus: "unsupported-algorithm",
817
- transformAlgorithms
818
- });
819
- continue;
820
- }
821
- checks.push({
822
- uri,
823
- resolvedPartPath,
824
- existsInPackage: true,
825
- digestAlgorithm,
826
- digestExpectedBase64,
827
- digestActualBase64,
828
- digestStatus: digestActualBase64 === digestExpectedBase64 ? "verified" : "mismatch",
829
- transformAlgorithms
830
- });
831
- }
832
- return checks;
833
- }
834
- async function buildReferenceChecksFromPptxViewerManifest(zip, signatureXml) {
835
- const parser = new DOMParser();
836
- const doc = parser.parseFromString(signatureXml, "text/xml");
837
- const partNodes = Array.from(doc.getElementsByTagNameNS(PPTX_VIEWER_MANIFEST_NS, "Part"));
838
- const checks = [];
839
- for (const partNode of partNodes) {
840
- const rawName = partNode.getAttribute("Name") || "";
841
- const digestAlgorithm = partNode.getAttribute("DigestMethod") || void 0;
842
- const digestExpectedBase64 = partNode.getAttribute("DigestValue") || void 0;
843
- const resolvedPartPath = resolveReferenceUriToPart(rawName);
844
- if (!resolvedPartPath) {
845
- checks.push({
846
- uri: rawName,
847
- existsInPackage: true,
848
- digestAlgorithm,
849
- digestExpectedBase64,
850
- digestStatus: "insufficient-data",
851
- transformAlgorithms: []
852
- });
853
- continue;
854
- }
855
- const partFile = zip.file(resolvedPartPath);
856
- if (!partFile) {
857
- checks.push({
858
- uri: rawName,
859
- resolvedPartPath,
860
- existsInPackage: false,
861
- digestAlgorithm,
862
- digestExpectedBase64,
863
- digestStatus: "missing-part",
864
- transformAlgorithms: []
865
- });
866
- continue;
867
- }
868
- if (!digestAlgorithm || !digestExpectedBase64) {
869
- checks.push({
870
- uri: rawName,
871
- resolvedPartPath,
872
- existsInPackage: true,
873
- digestAlgorithm,
874
- digestExpectedBase64,
875
- digestStatus: "insufficient-data",
876
- transformAlgorithms: []
877
- });
878
- continue;
879
- }
880
- const partBytes = await partFile.async("uint8array");
881
- const actualDigest = computeDigestBase642(partBytes, digestAlgorithm);
882
- if (!actualDigest) {
883
- checks.push({
884
- uri: rawName,
885
- resolvedPartPath,
886
- existsInPackage: true,
887
- digestAlgorithm,
888
- digestExpectedBase64,
889
- digestStatus: "unsupported-algorithm",
890
- transformAlgorithms: []
891
- });
892
- continue;
893
- }
894
- checks.push({
895
- uri: rawName,
896
- resolvedPartPath,
897
- existsInPackage: true,
898
- digestAlgorithm,
899
- digestExpectedBase64,
900
- digestActualBase64: actualDigest,
901
- digestStatus: actualDigest === digestExpectedBase64 ? "verified" : "mismatch",
902
- transformAlgorithms: []
903
- });
904
- }
905
- return checks;
906
- }
907
- async function inspectPptxDigitalSignatures(data) {
908
- try {
909
- const zip = await JSZip.loadAsync(Buffer.from(data));
910
- const signaturePaths = Object.keys(zip.files).filter(
911
- (entryPath) => entryPath.startsWith("_xmlsignatures/") && entryPath.endsWith(".xml")
912
- );
913
- const rootRelsXml = await zip.file("_rels/.rels")?.async("string");
914
- const hasOriginRelationship = Boolean(rootRelsXml?.includes(DIGITAL_SIGNATURE_ORIGIN_REL_TYPE));
915
- if (signaturePaths.length === 0) {
916
- return {
917
- supported: true,
918
- hasSignature: false,
919
- signatureCount: 0,
920
- signaturePaths: [],
921
- verificationStatus: "unsigned",
922
- hasOriginRelationship
923
- };
924
- }
925
- const additionalRootsPem = await loadEnterpriseTrustRoots();
926
- const policy = getSignatureValidationPolicy();
927
- const details = [];
928
- for (const signaturePath of signaturePaths) {
929
- const signatureXml = await zip.file(signaturePath)?.async("string") ?? "";
930
- const manifestChecks = await buildReferenceChecksFromPptxViewerManifest(zip, signatureXml);
931
- const referenceChecks = manifestChecks.length > 0 ? manifestChecks : await buildReferenceChecksFromSignatureXml(zip, signatureXml);
932
- const missingPartReferences = referenceChecks.filter((check) => check.digestStatus === "missing-part").map((check) => check.uri);
933
- const unsupportedTransforms = referenceChecks.flatMap(
934
- (check) => check.digestStatus === "unsupported-transform" ? check.transformAlgorithms : []
935
- ).filter((value, index, all) => all.indexOf(value) === index);
936
- const certs = extractAllTagText(signatureXml, "X509Certificate");
937
- const trust = validateCertificateChain(certs, additionalRootsPem);
938
- const signatureValueStatus = verifySignatureValue(signatureXml, certs);
939
- const leafCertPem = certs.length > 0 ? certPemFromBase64(certs[0]) : void 0;
940
- const issuerCertPem = certs.length > 1 ? certPemFromBase64(certs[1]) : leafCertPem;
941
- const revocation = leafCertPem ? await evaluateCertificateRevocation(leafCertPem, issuerCertPem) : {
942
- status: "not-checked",
943
- checkedOcspUrls: [],
944
- checkedCrlUrls: []
945
- };
946
- const timestamp = await evaluateTimestampAuthority(signatureXml);
947
- const detailBase = {
948
- path: signaturePath,
949
- signatureMethod: extractTagAttribute(
950
- signatureXml,
951
- "([\\w.-]+:)?SignatureMethod",
952
- "Algorithm"
953
- ),
954
- canonicalizationMethod: extractTagAttribute(
955
- signatureXml,
956
- "([\\w.-]+:)?CanonicalizationMethod",
957
- "Algorithm"
958
- ),
959
- signingTime: extractFirstTagText(signatureXml, "SigningTime"),
960
- referenceCount: referenceChecks.length,
961
- missingPartReferences,
962
- unsupportedTransforms,
963
- referenceChecks,
964
- certificate: certs.length > 0 ? certificateInfoFromBase64(certs[0]) : void 0,
965
- signatureValueStatus,
966
- certificateTrustStatus: trust.status,
967
- certificateTrustError: trust.error,
968
- certificateRevocationStatus: revocation.status,
969
- certificateRevocationError: revocation.error,
970
- timestampAuthorityStatus: timestamp.status,
971
- timestampAuthorityError: timestamp.error,
972
- certificateFingerprintSha256: leafCertPem ? certFingerprintSha256(leafCertPem) : void 0
973
- };
974
- details.push({
975
- ...detailBase,
976
- status: computeDetailStatus(detailBase, policy)
977
- });
978
- }
979
- return {
980
- supported: true,
981
- hasSignature: true,
982
- signatureCount: signaturePaths.length,
983
- signaturePaths,
984
- verificationStatus: computeVerificationStatus(details),
985
- details,
986
- hasOriginRelationship
987
- };
988
- } catch (error) {
989
- return {
990
- supported: true,
991
- hasSignature: false,
992
- signatureCount: 0,
993
- signaturePaths: [],
994
- verificationStatus: "invalid-package",
995
- error: `Unable to inspect digital signature parts: ${String(error)}`
996
- };
997
- }
998
- }
999
-
1000
- // src/signature-node/signing.ts
1001
- async function upsertRootOriginRelationship(zip) {
1002
- const parser = new DOMParser();
1003
- const serializer = new XMLSerializer();
1004
- const relsPath = "_rels/.rels";
1005
- const xml = await zip.file(relsPath)?.async("string") || '<?xml version="1.0" encoding="UTF-8"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"></Relationships>';
1006
- const doc = parser.parseFromString(xml, "text/xml");
1007
- const relationships = doc.documentElement;
1008
- const existing = Array.from(relationships.getElementsByTagName("Relationship")).find(
1009
- (node) => node.getAttribute("Type") === DIGITAL_SIGNATURE_ORIGIN_REL_TYPE
1010
- );
1011
- if (!existing) {
1012
- const rel = doc.createElement("Relationship");
1013
- rel.setAttribute("Id", "rIdDigitalSignatureOrigin");
1014
- rel.setAttribute("Type", DIGITAL_SIGNATURE_ORIGIN_REL_TYPE);
1015
- rel.setAttribute("Target", "_xmlsignatures/origin.sigs");
1016
- relationships.appendChild(rel);
1017
- }
1018
- zip.file(relsPath, serializer.serializeToString(doc));
1019
- }
1020
- async function upsertContentTypesForSignature(zip, signaturePath) {
1021
- const parser = new DOMParser();
1022
- const serializer = new XMLSerializer();
1023
- const pathInContentTypes = "[Content_Types].xml";
1024
- const xml = await zip.file(pathInContentTypes)?.async("string") || "";
1025
- if (!xml) {
1026
- return;
1027
- }
1028
- const doc = parser.parseFromString(xml, "text/xml");
1029
- const types = doc.documentElement;
1030
- const ensureOverride = (partName, contentType) => {
1031
- const existing = Array.from(types.getElementsByTagName("Override")).find(
1032
- (node) => node.getAttribute("PartName") === partName
1033
- );
1034
- if (existing) {
1035
- existing.setAttribute("ContentType", contentType);
1036
- return;
1037
- }
1038
- const override = doc.createElement("Override");
1039
- override.setAttribute("PartName", partName);
1040
- override.setAttribute("ContentType", contentType);
1041
- types.appendChild(override);
1042
- };
1043
- ensureOverride(
1044
- "/_xmlsignatures/origin.sigs",
1045
- "application/vnd.openxmlformats-package.digital-signature-origin"
1046
- );
1047
- ensureOverride(
1048
- `/${signaturePath}`,
1049
- "application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml"
1050
- );
1051
- zip.file(pathInContentTypes, serializer.serializeToString(doc));
1052
- }
1053
- async function buildOfficeReferenceList(zip) {
1054
- const references = [];
1055
- const digestMethod = "http://www.w3.org/2001/04/xmlenc#sha256";
1056
- for (const entryPath of Object.keys(zip.files)) {
1057
- const entry = zip.file(entryPath);
1058
- if (!entry || entry.dir) {
1059
- continue;
1060
- }
1061
- if (entryPath.startsWith("_xmlsignatures/")) {
1062
- continue;
1063
- }
1064
- const entryBytes = await entry.async("uint8array");
1065
- const digestValue = crypto.createHash("sha256").update(Buffer.from(entryBytes)).digest("base64");
1066
- references.push({
1067
- uri: `/${normalizePartPath(entryPath)}`,
1068
- digestMethod,
1069
- digestValue
1070
- });
1071
- }
1072
- references.sort((left, right) => left.uri.localeCompare(right.uri));
1073
- return references;
1074
- }
1075
- function buildOfficeSignatureXml(references, privateKeyPem, certificatePem) {
1076
- const signedInfoReferencesXml = references.map(
1077
- (reference) => `<Reference URI="${escapeXmlAttr(reference.uri)}"><DigestMethod Algorithm="${escapeXmlAttr(reference.digestMethod)}"/><DigestValue>${escapeXmlText(reference.digestValue)}</DigestValue></Reference>`
1078
- ).join("");
1079
- const signedInfoXml = `<SignedInfo xmlns="${XMLDSIG_NS}"><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>${signedInfoReferencesXml}</SignedInfo>`;
1080
- const canonicalSignedInfo = canonicalizeSignedInfoXml(signedInfoXml);
1081
- const signer = crypto.createSign("RSA-SHA256");
1082
- signer.update(Buffer.from(canonicalSignedInfo, "utf8"));
1083
- signer.end();
1084
- const signatureValueBase64 = signer.sign(privateKeyPem).toString("base64");
1085
- const certificateBase64 = pemCertificateToBase64(certificatePem);
1086
- if (!isValidBase64(signatureValueBase64)) {
1087
- throw new Error("Generated SignatureValue is not valid base64");
1088
- }
1089
- if (!isValidBase64(certificateBase64)) {
1090
- throw new Error("X.509 certificate is not valid base64");
1091
- }
1092
- const signingTime = (/* @__PURE__ */ new Date()).toISOString();
1093
- return `<Signature xmlns="${XMLDSIG_NS}">${signedInfoXml}<SignatureValue>${escapeXmlText(signatureValueBase64)}</SignatureValue><KeyInfo><X509Data><X509Certificate>${escapeXmlText(certificateBase64)}</X509Certificate></X509Data></KeyInfo><Object><SignatureProperties><SignatureProperty><SigningTime>${escapeXmlText(signingTime)}</SigningTime></SignatureProperty></SignatureProperties></Object></Signature>`;
1094
- }
1095
- async function signPptxWithCertificate(data, certificateBuffer, options) {
1096
- try {
1097
- const signing = loadSigningMaterialFromBuffer(
1098
- certificateBuffer,
1099
- options.certificatePath,
1100
- options.certificatePassword
1101
- );
1102
- const zip = await JSZip.loadAsync(Buffer.from(data));
1103
- for (const entryPath of Object.keys(zip.files)) {
1104
- if (entryPath.startsWith("_xmlsignatures/")) {
1105
- zip.remove(entryPath);
1106
- }
1107
- }
1108
- const signaturePath = "_xmlsignatures/sig1.xml";
1109
- await upsertRootOriginRelationship(zip);
1110
- await upsertContentTypesForSignature(zip, signaturePath);
1111
- const references = await buildOfficeReferenceList(zip);
1112
- const signatureXml = buildOfficeSignatureXml(
1113
- references,
1114
- signing.privateKeyPem,
1115
- signing.certificatePem
1116
- );
1117
- zip.file(signaturePath, signatureXml);
1118
- zip.file("_xmlsignatures/origin.sigs", "<SignatureOrigin/>");
1119
- zip.file(
1120
- "_xmlsignatures/_rels/origin.sigs.rels",
1121
- `<?xml version="1.0" encoding="UTF-8"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rIdSig1" Type="${DIGITAL_SIGNATURE_REL_TYPE}" Target="sig1.xml"/></Relationships>`
1122
- );
1123
- const signedData = await zip.generateAsync({ type: "uint8array" });
1124
- const report = await inspectPptxDigitalSignatures(signedData);
1125
- return { success: true, signedData, report };
1126
- } catch (error) {
1127
- const errorReport = {
1128
- supported: true,
1129
- hasSignature: false,
1130
- signatureCount: 0,
1131
- signaturePaths: [],
1132
- verificationStatus: "error",
1133
- error: `Failed to sign PPTX: ${String(error)}`
1134
- };
1135
- return {
1136
- success: false,
1137
- report: errorReport,
1138
- error: `Failed to sign PPTX: ${String(error)}`
1139
- };
1140
- }
1141
- }
1142
-
1143
- export { DIGEST_ALGORITHM_TO_HASH, DIGEST_ALGORITHM_TO_WEB_CRYPTO, DIGITAL_SIGNATURE_ORIGIN_REL_TYPE, DIGITAL_SIGNATURE_REL_TYPE, ENTERPRISE_FAIL_ON_REVOCATION_UNKNOWN_ENV, ENTERPRISE_REQUIRE_REVOCATION_ENV, ENTERPRISE_REQUIRE_TIMESTAMP_ENV, ENTERPRISE_TRUST_ROOTS_FILE_ENV, ENTERPRISE_TRUST_ROOTS_PEM_ENV, OPC_RELATIONSHIP_TRANSFORM, PPTX_VIEWER_MANIFEST_NS, SUPPORTED_XML_CANON_TRANSFORMS, XMLDSIG_NS, XML_TRANSFORM_ENVELOPED_SIGNATURE, applyReferenceTransforms, buildOcspRequestDer, buildReferenceChecksFromPptxViewerManifest, buildReferenceChecksFromSignatureXml, canonicalizeNode, canonicalizeSignedInfoXml, certFingerprintSha256, certPemFromBase64, certificateInfoFromBase64, computeDetailStatus, computeDigestBase642 as computeDigestBase64, computeDigestBase64 as computeDigestBase64WebCrypto, computeVerificationStatus, escapeXmlAttr, escapeXmlText, evaluateCertificateRevocation, evaluateTimestampAuthority, extractAllTagText, extractFirstTagText, extractOcspUrls, extractPemCertificatesFromText, extractReferenceTransforms, extractTagAttribute, getFirstDescendantElementByLocalName, getNodeLocalName, getSignatureValidationPolicy, inspectPptxDigitalSignatures, isValidBase64, loadEnterpriseTrustRoots, loadSigningMaterialFromBuffer, normalizePartPath, parseOcspResponseStatus, pemCertificateToBase64, resolveReferenceUriToPart, signPptxWithCertificate, validateCertificateChain, verifySignatureValue };